Skip to content

feat(gke): model node pools as real resources, complete ClusterManage… - #96

Open
avison9 wants to merge 2 commits into
floci-io:mainfrom
avison9:feat/gke-full-emulation
Open

feat(gke): model node pools as real resources, complete ClusterManage…#96
avison9 wants to merge 2 commits into
floci-io:mainfrom
avison9:feat/gke-full-emulation

Conversation

@avison9

@avison9 avison9 commented Jul 27, 2026

Copy link
Copy Markdown

Summary

GKE cluster support existed, but node pools were a static hardcoded field, not
a real resource so the standard remove_default_node_pool = true +
standalone google_container_node_pool Terraform pattern (the most common
real-world way to provision GKE) could not work at all. This PR adds full
node pool CRUD and the rest of the container.v1 ClusterManager RPC
surface (mutation RPCs Terraform/gcloud rely on, IP rotation, node pool
upgrade acknowledgment, legacy ABAC, maintenance policy, server config, JWKS,
usable subnetworks, Autopilot compatibility checks, upgrade info) the only
RPC intentionally left out is CancelOperation, since every operation here
is synchronous and there's never anything in flight to cancel. Fixes two
bugs along the way (found via live terraform apply testing) that would
otherwise cause perpetual cluster replacement on every plan.

Closes #95

# Verified end to end against a live instance:
$ terraform apply
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

$ terraform plan
No changes. Your infrastructure matches the configuration.

Type of change

  • New feature (feat:)

What changed

  • StoredNodePool (new): node pools as real, independently-lifecycled resources.
  • GkeService: node pool CRUD (create/get/list/delete/updateNodePool,
    setNodePoolAutoscaling/Management/Size) and cluster mutation RPCs
    (updateCluster, setLabels, setMasterAuth, setNetworkPolicy,
    setAddonsConfig, setLoggingService, setMonitoringService,
    setLocations). Cluster deletion cascades to its node pools.
  • KubernetesController: matching REST routes, including GCP's colon-suffixed
    custom-method paths (:setNetworkPolicy etc.), using the existing
    {param: [^:/]+} JAX-RS pattern already established elsewhere in this repo.
  • StoredCluster: typed fields for everything Terraform/gcloud actually diff
    on (initialNodeCount, locations, labelFingerprint, etc.), plus an
    extraConfig passthrough map for nested blocks the emulator doesn't act on
    semantically (privateClusterConfig, workloadIdentityConfig, etc.)
    stored and echoed verbatim for exact round-trip fidelity.
  • Also added: setLegacyAbac, setMaintenancePolicy, startIpRotation/
    completeIpRotation, completeNodePoolUpgrade/rollbackNodePoolUpgrade,
    getServerConfig, getJsonWebKeys, listUsableSubnetworks,
    checkAutopilotCompatibility, and fetchClusterUpgradeInfo/
    fetchNodePoolUpgradeInfo the remaining standalone mutation/read RPCs.
    The five read-only ones return honest stub data (empty JWKS, a synthetic
    subnetwork, no compatibility issues, current-version-as-target) rather than
    fabricated analysis, since floci-gcp has no real infrastructure behind them.
  • KubernetesProjectController (new): ListUsableSubnetworks is
    project-scoped with no location segment, unlike every other GKE method,
    so it needed its own controller mounted at /container/v1/projects/{project}.
  • Confirmed Autopilot mode (autopilot.enabled) and Fleet/Anthos registration
    (fleet) already round-trip correctly through the existing extraConfig
    passthrough no new code needed, just a regression test locking it in.

Two bugs found and fixed via live Terraform validation

  1. initial_node_count was never echoed back on the Cluster response, so the
    (ForceNew) attribute always read as 0 and the provider planned to
    destroy+recreate the cluster on every single plan, not just the first.
  2. network/subnetwork are read by the provider from
    Cluster.NetworkConfig.network/.subnetwork, not the deprecated top-level
    fields real GKE populates both locations; floci-gcp only set the
    top-level ones.

Also documented (not fixable here): the hashicorp/google provider's
RemoveBasePathVersion helper strips the last path segment of any custom
endpoint, so container_custom_endpoint = "<endpoint>/container/" silently
collapses to <endpoint>/ and misroutes to Managed Kafka (near-identical REST
shape). GKE needs host-mode routing
(container_custom_endpoint = "http://container.localhost:4588/"), not path-mode
now documented with a warning callout in docs/services/gke.md.

GCP Compatibility

Traced the real HTTP method+path bindings and message shapes directly from
googleapis/googleapis's google/container/v1/cluster_service.proto (the
authoritative source per this repo's own AGENTS.md), not just the Terraform
provider's behavior.

How it was tested

  • ./mvnw test: 465 run, 0 failures, 0 errors (31 GkeServiceTest cases now,
    up from 6, all new behavior covered).
  • Live: ./mvnw quarkus:dev + real terraform apply/plan/destroy against
    a config mirroring a real-world production GKE module exactly
    (VPC-native networking, private cluster config, workload identity, master
    authorized networks, remove_default_node_pool + standalone node pool with
    autoscaling/node_config/management) apply succeeds, a second plan shows
    zero drift, destroy is clean, and remove_default_node_pool was confirmed
    via direct curl to have actually deleted default-pool.
  • Not yet wired into the CI compatibility-tests/compat-terraform suite
    that requires a Docker network alias for container.floci-gcp in
    compatibility.yml, flagged as a follow-up rather than touched here to
    avoid risking the shared CI networking other suites depend on.

Checklist

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

@greptile-apps

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown

Greptile Summary

The PR expands GKE emulation to support independently persisted node pools and the broader ClusterManager v1 API surface.

  • Adds node-pool CRUD, cluster and node-pool mutations, project-scoped subnetwork discovery, and synchronous operation types.
  • Extends cluster persistence and response serialization for Terraform-compatible round trips.
  • Migrates node pools embedded by older persisted cluster records into the new node-pool store.
  • Adds controller routes, compatibility documentation, and extensive GKE service regression tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/main/java/io/floci/gcp/services/gke/GkeService.java Implements node-pool lifecycle, cluster mutations, startup migration, compatibility reads, and atomic upfront validation for explicit initial pools.
src/main/java/io/floci/gcp/services/gke/KubernetesController.java Adds GCP-compatible ClusterManager REST routes and serializes typed plus passthrough cluster and node-pool fields.
src/main/java/io/floci/gcp/services/gke/KubernetesProjectController.java Exposes the project-scoped ListUsableSubnetworks route at its distinct GCP path.
src/main/java/io/floci/gcp/services/gke/model/StoredCluster.java Expands persisted cluster state while retaining legacy embedded node pools for startup migration.
src/main/java/io/floci/gcp/services/gke/model/StoredNodePool.java Introduces the independently persisted node-pool model and round-tripped configuration fields.
src/test/java/io/floci/gcp/services/gke/GkeServiceTest.java Adds regression coverage for node-pool lifecycle, typed updates, creation validation, migration, and compatibility methods.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Client[Terraform, gcloud, or SDK] --> Controller[Kubernetes REST controllers]
    Controller --> Service[GkeService]
    Service --> ClusterStore[(Cluster store)]
    Service --> NodePoolStore[(Node-pool store)]
    Service --> Operations[Synchronous DONE operations]
    ClusterStore --> Migration[Startup embedded-pool migration]
    Migration --> NodePoolStore
    Service --> Manager[GkeClusterManager]
    Manager --> K3s[k3s container in real mode]
Loading

Reviews (2): Last reviewed commit: "fix(gke): address review findings on nod..." | Re-trigger Greptile

Comment thread src/main/java/io/floci/gcp/services/gke/GkeService.java
Comment thread src/main/java/io/floci/gcp/services/gke/model/StoredCluster.java Outdated
Comment thread src/main/java/io/floci/gcp/services/gke/GkeService.java Outdated
@hectorvent hectorvent added enhancement New feature or request gke Google Kubernetes Engine (GKE) labels Aug 1, 2026
@hectorvent

Copy link
Copy Markdown
Contributor

Thank you for this, modeling node pools as real resources is exactly what the standalone google_container_node_pool pattern needed, and the proto grounded writeup with live Terraform evidence made it a pleasure to verify. I checked every new route against cluster_service.proto and the verbs, paths and field names all line up.

  1. (blocking) CompleteNodePoolUpgrade returns google.protobuf.Empty, not an Operation (cluster_service.proto#L382-L388). Would you be open to returning {} from :completeUpgrade? (rollback really does return an Operation, which you have right.)

  2. (blocking) getCluster/listClusters call setNodePools on the live stored object, so a stale pool snapshot gets persisted and the startup migration can resurrect deleted pools after a restart in persistent mode. Attaching pools on a copy (or at the JSON building layer) would close that hole.

  3. (follow-up, separate PR) :updateMaster (L221-L231) looks like the one other missing RPC. Happy to track it separately, or the "full surface" wording could just mention it.

Really solid contribution, thank you!

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

Labels

enhancement New feature or request gke Google Kubernetes Engine (GKE) waiting-contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] GKE: model node pools as real resources + full ClusterManager RPC coverage

2 participants