docs/gcp-in-specific-project.md: SA + firewall setup for the egress-proxy project, in-kube secret, and apply-ready ConfigMap/Deployment/Proxy manifests (Ubuntu image — debian-cloud lacks cloud-init). docs/gcp-vm-validation.md: end-to-end GCP VM validation walkthrough. docs/reviews/: proxy-operator PR review notes from 2026-08-10. .claude/: operator-reviewer agent, accumulated permission allowlist. .gitignore: never commit sa_key.json (live SA key stays untracked). Co-Authored-By: Claude <noreply@anthropic.com>
5.2 KiB
Validating real VM creation on GCP
Recipe for wiring the GCP provider into a live cluster and watching a
Proxy CR create a real Compute Engine VM. Angle brackets mark values you
supply: <PROJECT_ID>, <SA_KEY_PATH>, <ZONE>, <CLUSTER_EGRESS_IP>,
<REGISTRY_IMAGE>.
The one important fact up front: the operator takes no GCP credentials
through its own config. The client is built with Application Default
Credentials (internal/provider/gcp/gcp.go, New()); there is no
key-file field in the providers config. The only secret to prepare is a
service-account JSON key, injected via the standard
GOOGLE_APPLICATION_CREDENTIALS mechanism. On GKE you would use workload
identity instead and skip the key entirely.
1. GCP-side prerequisites (prepared outside the cluster)
-
A project —
<PROJECT_ID>— with the Compute Engine API enabled. -
A service account with
roles/compute.instanceAdmin.v1on the project. The operator only calls instancesInsert/Get/Delete/AggregatedListand does not attach a service account to the VMs it creates, so noiam.serviceAccountUseris needed. -
A JSON key for that service account, saved at
<SA_KEY_PATH>. -
A firewall rule: created VMs get network tag
proxy-operator(the default; configurable asgcp.networkTag), an ephemeral external IP, and Squid listening on 3128.gcloud compute firewall-rules create allow-proxy-operator \ --project <PROJECT_ID> \ --network default \ --allow tcp:3128 \ --target-tags proxy-operator \ --source-ranges <CLUSTER_EGRESS_IP>/32The source range must cover the cluster's egress IP — the operator's CONNECT health probes originate there, and without the rule the Proxy hangs at
Running/unhealthy instead of reachingReady. ⚠️ The sample cloud-init configureshttp_access allow all, so on a public IP this is an open proxy — keep the source ranges tight.
2. Create the credentials Secret
Namespace is egress-proxies-operator-system after kustomize prefixing:
kubectl -n egress-proxies-operator-system create secret generic gcp-credentials \
--from-file=key.json=<SA_KEY_PATH>
3. Add a GCP entry to the providers ConfigMap
Edit config/manager/providers_config.yaml (mounted at
/etc/proxy-operator/providers.yaml):
providers:
- name: kubernetes
type: kubernetes
- name: gcp-eu # spec.provider on a Proxy refers to this NAME, not the type
type: gcp
gcp:
project: <PROJECT_ID>
# network: default # these three default as shown
# networkTag: proxy-operator
# diskSizeGb: 10
The config is validated fail-fast at startup — a typo shows up immediately in the manager log, not on first use.
4. Mount the Secret and point ADC at it
In config/manager/manager.yaml, add to the manager container:
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/gcp/key.json
volumeMounts:
- name: gcp-credentials
mountPath: /var/secrets/gcp
readOnly: true
volumes:
- name: gcp-credentials
secret:
secretName: gcp-credentials
(volumeMounts merges into the existing container list; volumes into
the existing pod-level list.)
5. Deploy and create the Proxy
make deploy IMG=<REGISTRY_IMAGE>
config/samples/proxy_gcp.yaml is usable as-is once spec.provider
matches the name from step 3. All three placement fields are mandatory
for GCP — a missing one sets the Proxy to Failed with a message naming
it:
spec:
mode: Managed
provider: gcp-eu
placement:
zone: <ZONE> # e.g. europe-west1-b
machineType: e2-micro
image: projects/debian-cloud/global/images/family/debian-12
kubectl apply -f config/samples/proxy_gcp.yaml
6. What you should see
kubectl get proxy -w
Provisioning → Running (VM's external IP published in status) →
Ready (CONNECT health probe succeeded through the public IP). Then:
# the VM exists and carries the GC labels
gcloud compute instances list --project <PROJECT_ID> \
--filter 'labels.proxy-operator-managed=yes'
# the proxy actually tunnels — should print the VM's external IP
curl -x http://<EXTERNAL_IP>:3128 https://ifconfig.me
Cleanup — the finalizer deletes the VM:
kubectl delete proxy proxy-gcp-sample
gcloud compute instances list --project <PROJECT_ID> # should be empty again
Gotchas
- The orphan GC sweeps the whole project: any VM labeled
proxy-operator-managed=yeswhose UID does not match a live Proxy CR in this cluster is deleted once past the age threshold. Do not point two operator installs at the same project, and do not hand-create VMs with that label. - VM creation is fire-and-forget — the provider never waits on the
insert operation; progress is discovered by polling
Get. A quota error or bad image name surfaces on the Proxy's status/conditions a reconcile later, not synchronously.kubectl describe proxyis the place to look when something stalls. - e2-micro costs pennies but is not free everywhere — remember to
delete the CR (or check
gcloud compute instances list) when done.