GCP parity E2E audit — bugs, gaps & enhancements
Method. Mirrors the AWS audit (#319). For each of the 23 GCP service handlers under server/gcp/*, read the wire handler + providers/gcp/* Mock, enumerated implemented+dispatched operations, ran the existing real-SDK round-trip suites (go test ./server/gcp/... ./providers/gcp/..., all PASS), and classified each finding as BUG (wrong / silent-wrong data), GAP (missing op), or ENHANCEMENT (fidelity). High-severity items hand-verified in source.
Coverage. All 23 GCP handlers: compute, networks, loadbalancer, servicenetworking, gcs, cloudsql, alloydb, firestore, bigtable, memorystore, gke, cloudfunctions, artifactregistry, eventarc, pubsub, iam, secretmanager, fcm, monitoring, cloudlogging, clouddns, cloudasset, vertexai.
Note. Existing tests all pass — these are behaviors the current suites don't exercise (many use raw google.golang.org/api inline-done LROs and never hit the paths below).
Tally: 9 High · ~22 Medium · ~30 Low (Low dominated by missing pagination + cosmetic field omissions across nearly every list op).
HIGH (verified in source)
- compute — instance reads drop
networkInterfaces (and disks). server/gcp/compute/instances.go:254-263 — toInstanceResponse returns kind/id/name/machineType/status/zone/selfLink/labels only. NICs are stored on insert but a read-back returns an empty list → silent wrong data on a normal GET.
- networks — firewall rules silently discarded.
server/gcp/networks/handler.go:404-409 builds SecurityGroupConfig with only name/description/vpc/tags; allowed/denied/sourceRanges/direction/priority/targetTags are decoded but never persisted, and toFirewallResponse (handler.go:592-602) never echoes them → firewall reads back as an empty rule.
- pubsub —
getSubscription returns wrong data. server/gcp/pubsub/handler.go:333-345 hardcodes ackDeadlineSeconds: 10 and omits labels.
- pubsub — subscription name must equal topic name.
handler.go:306-308 — driver pairs topic+sub 1:1, so a sub whose name differs from its topic 404s. Real Pub/Sub routinely uses distinct names.
- monitoring — alert policy semantics dropped.
server/gcp/monitoring/handler.go:103-112 createPolicy stores a hardcoded AlarmConfig and drops conditions/combiner/enabled/notificationChannels/userLabels; getPolicy/listPolicies (130-150) return a skeleton. Any policy read back is meaningless.
- clouddns — delete+add of the same record set falsely fails.
server/gcp/clouddns/operations.go:111-118 checks additions for pre-existence before applying deletions (120-126). The standard "update = delete old + add new (same name)" change trips AlreadyExists.
- iam — missing IAM-policy + token-minting + org-role surface. No SA
:getIamPolicy/:setIamPolicy, :signBlob, :signJwt, :generateAccessToken, :undelete, :enable/:disable; organizations/{o}/roles unmatched.
- artifactregistry — GAPIC LRO polling 404s.
server/gcp/artifactregistry/handler.go:55-79 parseRoute requires parts[4]=="repositories", so GET /v1/.../operations/{op} is unmatched → artifactregistry/apiv1 .Wait() 404s (raw REST inline-done works). Also non-DOCKER format and description dropped on create.
- eventarc — same GAPIC LRO gap.
server/gcp/eventarc/handler.go:77-96 has no /operations/{op} route. Create also drops serviceAccount + labels.
MEDIUM (~22, by service)
- compute —
insertImage (images.go:60-76) fabricates from an arbitrary instance and 400s if none exists, ignoring sourceDisk/sourceSnapshot; operations list returns 501 (handler.go:232).
- networks —
toNetworkResponse hardcodes autoCreateSubnetworks:false + drops routingConfig (566); toSubnetworkResponse omits parent network self-link (581-588).
- loadbalancer — backendService
healthChecks/description/portName dropped (operations.go:29-33); regional LB paths route here with no scope guard.
- gcs — no bucket PATCH/UPDATE route (handler.go:145-154) → versioning/lifecycle/CORS/IAM/labels implemented in provider but unreachable (405);
MD5Hash/X-Goog-Hash never set.
- cloudsql — PATCH and PUT served identically as partial-merge (operations.go:82-126); PUT should full-replace.
getBackupRun collapses a backend error into 404 (205-213).
- alloydb — instance/cluster reads hardcode
State:"READY" (helpers.go:103,120-132).
- gke —
UpdateCluster accepts desiredMaster/NodeVersion but never applies them (gke.go:379-395) → version upgrade no-op.
- cloudfunctions — Update ignores
updateMask; httpsTrigger.url never emitted (handler.go:390-411); no :generateUploadUrl (breaks source-upload deploy).
- firestore —
createDocument uses Put unconditionally, no ALREADY_EXISTS (handler.go:606); createTime/updateTime fabricated per-response (695-703); listDocuments ignores pageSize/pageToken/orderBy (624).
- memorystore —
memorySizeGb hardcoded 1, redisVersion hardcoded REDIS_6_X, create drops request fields (types.go:110, operations.go:27); no Update/PATCH route.
- cloudlogging —
jsonPayload/protoPayload/severity/labels/resource dropped on write; orderBy ignored; list filter only honors logName.
- cloudasset —
searchAllIamPolicies always returns empty (handler.go:250).
- secretmanager — create seeds an empty initial version (off-by-one vs real GCP); no version
:disable/:enable/:destroy, no secret :patch.
- iam —
updateServiceAccount/updateRole are non-atomic delete+create and ignore updateMask.
- vertexai —
generateContent parses only text parts; inlineData/fileData/functionCall/functionResponse dropped.
LOW (~30)
Pervasive: list ops ignore pageSize/pageToken/filter and never emit nextPageToken (nearly every service). Per-service cosmetics: gcs bucket location hardcoded "US"; cloudsql IPs hardcoded 10.0.0.1/SECOND_GEN; fcm dropped android/apns/webpush config + no multi-target validation; bigtable dropRowRange no-op + storage-target unit mislabel; gke CancelOperation on DONE ops; vertexai endpoint :countTokens 404; clouddns change id hardcoded "1"/status "done" + dnsName returns zone name not FQDN; alloydb user PATCH ignores updateMask.
Plan: fix all severities in one combined PR with per-service commits and regression tests (real-SDK round-trip, asserting field values), mirroring #319/#320.
GCP parity E2E audit — bugs, gaps & enhancements
Method. Mirrors the AWS audit (#319). For each of the 23 GCP service handlers under
server/gcp/*, read the wire handler +providers/gcp/*Mock, enumerated implemented+dispatched operations, ran the existing real-SDK round-trip suites (go test ./server/gcp/... ./providers/gcp/..., all PASS), and classified each finding as BUG (wrong / silent-wrong data), GAP (missing op), or ENHANCEMENT (fidelity). High-severity items hand-verified in source.Coverage. All 23 GCP handlers: compute, networks, loadbalancer, servicenetworking, gcs, cloudsql, alloydb, firestore, bigtable, memorystore, gke, cloudfunctions, artifactregistry, eventarc, pubsub, iam, secretmanager, fcm, monitoring, cloudlogging, clouddns, cloudasset, vertexai.
Note. Existing tests all pass — these are behaviors the current suites don't exercise (many use raw
google.golang.org/apiinline-done LROs and never hit the paths below).Tally: 9 High · ~22 Medium · ~30 Low (Low dominated by missing pagination + cosmetic field omissions across nearly every list op).
HIGH (verified in source)
networkInterfaces(anddisks).server/gcp/compute/instances.go:254-263—toInstanceResponsereturns kind/id/name/machineType/status/zone/selfLink/labels only. NICs are stored on insert but a read-back returns an empty list → silent wrong data on a normal GET.server/gcp/networks/handler.go:404-409buildsSecurityGroupConfigwith only name/description/vpc/tags;allowed/denied/sourceRanges/direction/priority/targetTagsare decoded but never persisted, andtoFirewallResponse(handler.go:592-602) never echoes them → firewall reads back as an empty rule.getSubscriptionreturns wrong data.server/gcp/pubsub/handler.go:333-345hardcodesackDeadlineSeconds: 10and omitslabels.handler.go:306-308— driver pairs topic+sub 1:1, so a sub whose name differs from its topic 404s. Real Pub/Sub routinely uses distinct names.server/gcp/monitoring/handler.go:103-112createPolicystores a hardcodedAlarmConfigand drops conditions/combiner/enabled/notificationChannels/userLabels;getPolicy/listPolicies(130-150) return a skeleton. Any policy read back is meaningless.server/gcp/clouddns/operations.go:111-118checks additions for pre-existence before applying deletions (120-126). The standard "update = delete old + add new (same name)" change tripsAlreadyExists.:getIamPolicy/:setIamPolicy,:signBlob,:signJwt,:generateAccessToken,:undelete,:enable/:disable;organizations/{o}/rolesunmatched.server/gcp/artifactregistry/handler.go:55-79parseRouterequiresparts[4]=="repositories", soGET /v1/.../operations/{op}is unmatched →artifactregistry/apiv1.Wait()404s (raw REST inline-done works). Also non-DOCKERformatanddescriptiondropped on create.server/gcp/eventarc/handler.go:77-96has no/operations/{op}route. Create also dropsserviceAccount+labels.MEDIUM (~22, by service)
insertImage(images.go:60-76) fabricates from an arbitrary instance and 400s if none exists, ignoringsourceDisk/sourceSnapshot; operations list returns 501 (handler.go:232).toNetworkResponsehardcodesautoCreateSubnetworks:false+ drops routingConfig (566);toSubnetworkResponseomits parentnetworkself-link (581-588).healthChecks/description/portNamedropped (operations.go:29-33); regional LB paths route here with no scope guard.MD5Hash/X-Goog-Hashnever set.getBackupRuncollapses a backend error into 404 (205-213).State:"READY"(helpers.go:103,120-132).UpdateClusteraccepts desiredMaster/NodeVersion but never applies them (gke.go:379-395) → version upgrade no-op.updateMask;httpsTrigger.urlnever emitted (handler.go:390-411); no:generateUploadUrl(breaks source-upload deploy).createDocumentuses Put unconditionally, noALREADY_EXISTS(handler.go:606); createTime/updateTime fabricated per-response (695-703);listDocumentsignores pageSize/pageToken/orderBy (624).memorySizeGbhardcoded 1,redisVersionhardcoded REDIS_6_X, create drops request fields (types.go:110, operations.go:27); no Update/PATCH route.jsonPayload/protoPayload/severity/labels/resourcedropped on write;orderByignored; list filter only honors logName.searchAllIamPoliciesalways returns empty (handler.go:250).:disable/:enable/:destroy, no secret:patch.updateServiceAccount/updateRoleare non-atomic delete+create and ignoreupdateMask.generateContentparses onlytextparts; inlineData/fileData/functionCall/functionResponse dropped.LOW (~30)
Pervasive: list ops ignore
pageSize/pageToken/filterand never emitnextPageToken(nearly every service). Per-service cosmetics: gcs bucket location hardcoded "US"; cloudsql IPs hardcoded 10.0.0.1/SECOND_GEN; fcm dropped android/apns/webpush config + no multi-target validation; bigtable dropRowRange no-op + storage-target unit mislabel; gke CancelOperation on DONE ops; vertexai endpoint:countTokens404; clouddns change id hardcoded "1"/status "done" +dnsNamereturns zone name not FQDN; alloydb user PATCH ignores updateMask.Plan: fix all severities in one combined PR with per-service commits and regression tests (real-SDK round-trip, asserting field values), mirroring #319/#320.