Skip to content

feat(gke): implement ClusterManager.UpdateMaster - #192

Open
avison9 wants to merge 1 commit into
floci-io:mainfrom
avison9:feat/177-gke-update-master
Open

feat(gke): implement ClusterManager.UpdateMaster#192
avison9 wants to merge 1 commit into
floci-io:mainfrom
avison9:feat/177-gke-update-master

Conversation

@avison9

@avison9 avison9 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements ClusterManager.UpdateMaster (POST .../clusters/{cluster}:updateMaster), the one container.v1 cluster RPC left unimplemented after #96, where it was split out during review to keep that PR focused. Closes #177.

Today the route does not exist, so ClusterManagerClient.updateMaster(...) throws NotFoundException and a raw REST call gets a bare 404, while docs/services/gke.md says the surface is complete except CancelOperation.

One correction to the issue text: gcloud (container clusters upgrade --master) and the Terraform provider both use UpdateCluster with desiredMasterVersion rather than this RPC, so the consumers here are direct SDK and REST callers. The RPC is still part of the documented surface and the SDK exposes it, hence the change.

# BEFORE
H=http://localhost:4588/container/v1/projects/p/locations/us-central1
curl -s -X POST "$H/clusters" -H 'Content-Type: application/json' -d '{"cluster":{"name":"demo"}}'
curl -s -o /dev/null -w '%{http_code}\n' -X POST "$H/clusters/demo:updateMaster" \
  -H 'Content-Type: application/json' -d '{"masterVersion":"1.31.5-gke.1"}'
# 404

# AFTER
curl -s -X POST "$H/clusters/demo:updateMaster" \
  -H 'Content-Type: application/json' -d '{"masterVersion":"1.31.5-gke.1"}'
# {"name":"operation-...","operationType":"UPGRADE_MASTER","status":"DONE",
#  "targetLink":"projects/p/locations/us-central1/clusters/demo", ...}
curl -s "$H/clusters/demo" | jq '{currentMasterVersion, currentNodeVersion, pool: .nodePools[0].version}'
# {"currentMasterVersion":"1.31.5-gke.1","currentNodeVersion":"1.30.5-gke.1014001","pool":"1.30.5-gke.1014001"}
curl -s -X POST "$H/clusters/demo:updateMaster" -H 'Content-Type: application/json' -d '{}'
# {"error":{"code":400,"message":"masterVersion is required","status":"INVALID_ARGUMENT",...}}

What changed

  • OperationType: add UPGRADE_MASTER, the Operation.Type real GKE reports for a master upgrade (cluster_service.proto), so the SDK's enum parses it rather than returning UNRECOGNIZED.
  • KubernetesController: POST /clusters/{clusterId: [^:/]+}:updateMaster, same regex-constrained pattern as the sibling custom methods.
  • GkeService.updateMaster: requires the cluster; rejects a missing or blank masterVersion with 400 INVALID_ARGUMENT before touching state (the proto marks it REQUIRED); sets currentMasterVersion only; bumps etag/fingerprint; returns a synchronous DONE operation.
  • currentNodeVersion and every node pool's version are deliberately left untouched. Real GKE upgrades the control plane independently of node pools; this was the open question in the issue and it is pinned by tests at all three levels.
  • Version aliases (latest, -, 1.X, 1.X.Y) resolve against the single version GetServerConfig advertises. Any other explicit version is stored verbatim, consistent with initialClusterVersion on create and desiredMasterVersion/desiredNodeVersion on UpdateCluster.
  • docs/services/gke.md: UpdateMaster added to the supported list with a paragraph on the semantics above.

Not included, on purpose: the deprecated zonal binding (.../zones/{z}/clusters/{c}/master), since no other route here implements zonal bindings; and alias resolution on UpdateCluster.desiredMasterVersion, which is a separate pre-existing gap (gcloud sends "-" there) and better handled in its own PR.

Type of change

  • Bug fix (fix:)
  • New feature (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

GCP Compatibility

Wire shape from googleapis/googleapis@aa87617d67 google/container/v1/cluster_service.proto (UpdateMasterRequest, Operation.Type.UPGRADE_MASTER). Verified with google-cloud-container Java SDK via the HttpJson transport (compatibility-tests/sdk-test-java GkeTest.updateMasterUpgradesOnlyTheControlPlane) against a local quarkus:dev instance in GKE mock mode: 5/5 pass.

Tests

  • GkeServiceTest (+4): master moves and node versions do not on a two-pool cluster; alias resolution incl. the 1.3 vs 1.30 non-match; missing/blank/null version is 400 and leaves the stored cluster and etag untouched; unknown cluster is 404.
  • GkeUpdateMasterRestIntegrationTest (new, 3): the route itself, operation body and GET /operations/{id} round trip, cluster read-back; 400 and 404 error shapes. Written first and failing 3/3 with 404 before the change.
  • compatibility-tests/sdk-test-java GkeTest (+1), README counts updated.

Full suite: ./mvnw test baseline on main @ 0ec3a3e was 1031 run / 0 failures / 0 errors / 0 skipped; after this change it is 1038 run / 0 failures / 0 errors / 0 skipped (the 7 new tests, nothing else changed).

Checklist

  • ./mvnw test passes locally
  • New or updated integration test added
  • Commit messages follow Conventional Commits

🤖 Generated with Claude Code

https://claude.ai/code/session_01VTYz9WSVDx9vDqBoy4K96j

Add the POST .../clusters/{cluster}:updateMaster route, the last
unimplemented container.v1 cluster RPC after floci-io#96. The call moves
currentMasterVersion only: real GKE upgrades the control plane
independently of node pools, so currentNodeVersion and each pool's
version are left untouched. masterVersion is required (400 when
missing), the documented aliases (latest, -, 1.X, 1.X.Y) resolve
against the single version GetServerConfig advertises, and the
operation is reported as UPGRADE_MASTER, the Operation.Type real GKE
uses.

Closes floci-io#177
@greptile-apps

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR implements the GKE ClusterManager.UpdateMaster REST method and validates it at service, REST, and Java SDK levels.

  • Adds the :updateMaster custom route and UPGRADE_MASTER operation type.
  • Updates only the control-plane version while preserving node and node-pool versions.
  • Resolves supported version aliases and updates cluster metadata.
  • Documents the endpoint and updates compatibility-test coverage counts.
  • One malformed-body path still returns an internal server error instead of a GCP-compatible client error.

Confidence Score: 4/5

The PR should not merge until malformed masterVersion values return a GCP-compatible client error rather than an internal server error.

The main update path and SDK contract are implemented correctly, but syntactically valid non-string JSON reaches an unchecked cast and breaks the endpoint's error contract.

Files Needing Attention: src/main/java/io/floci/gcp/services/gke/GkeService.java

Important Files Changed

Filename Overview
src/main/java/io/floci/gcp/services/gke/GkeService.java Implements control-plane-only master upgrades and alias resolution, but directly casting malformed JSON values can produce an internal server error.
src/main/java/io/floci/gcp/services/gke/KubernetesController.java Adds the correctly shaped regional :updateMaster REST custom method.
src/main/java/io/floci/gcp/services/gke/operations/OperationType.java Adds the GKE-compatible UPGRADE_MASTER operation enum value.
src/test/java/io/floci/gcp/services/gke/GkeUpdateMasterRestIntegrationTest.java Covers successful routing, operation lookup, state changes, missing versions, and missing clusters, but not non-string version values.
compatibility-tests/sdk-test-java/src/test/java/io/floci/gcp/test/GkeTest.java Verifies the new method through the Java HttpJson SDK and confirms node versions remain unchanged.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Controller as KubernetesController
    participant Service as GkeService
    participant Store as Cluster Store
    participant Operations as Operation Service
    Client->>Controller: "POST /clusters/{id}:updateMaster"
    Controller->>Service: updateMaster(project, location, id, body)
    Service->>Store: requireCluster(...)
    Store-->>Service: StoredCluster
    Service->>Service: validate and resolve masterVersion
    Service->>Store: persist updated control-plane version
    Service->>Operations: create UPGRADE_MASTER operation
    Operations-->>Client: DONE operation
Loading

Reviews (1): Last reviewed commit: "feat(gke): implement ClusterManager.Upda..." | Re-trigger Greptile

Comment on lines +384 to +387
String masterVersion = body == null ? null : (String) body.get("masterVersion");
if (masterVersion == null || masterVersion.isBlank()) {
throw GcpException.invalidArgument("masterVersion is required");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Malformed versions cause 500s

A REST caller can send valid JSON such as {"masterVersion":123}. Jackson stores that value as a non-string object, so this direct cast throws ClassCastException. The exception is not mapped to the GCP error format, causing an internal server error instead of 400 INVALID_ARGUMENT. This violates the repository directive to preserve GCP protocol compatibility and return GCP-compatible JSON errors.

Suggested change
String masterVersion = body == null ? null : (String) body.get("masterVersion");
if (masterVersion == null || masterVersion.isBlank()) {
throw GcpException.invalidArgument("masterVersion is required");
}
Object masterVersionValue = body == null ? null : body.get("masterVersion");
if (!(masterVersionValue instanceof String masterVersion) || masterVersion.isBlank()) {
throw GcpException.invalidArgument("masterVersion is required");
}

Context Used: AGENTS.md (source)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] GKE: implement ClusterManager.UpdateMaster (:updateMaster)

1 participant