Skip to content

Fix GCP Parity Audit Findings Across 19 Services - #322

Merged
thzgajendra merged 6 commits into
stackshy:developmentfrom
thzgajendra:feat/gcp-parity-audit
Aug 5, 2026
Merged

Fix GCP Parity Audit Findings Across 19 Services#322
thzgajendra merged 6 commits into
stackshy:developmentfrom
thzgajendra:feat/gcp-parity-audit

Conversation

@thzgajendra

@thzgajendra thzgajendra commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Objective / Issue

Fixes the GCP parity audit findings in #321. The audit drove real cloud.google.com/go / google.golang.org/api clients against cloudemu serve --providers gcp across all 23 GCP handlers and catalogued bugs / gaps / enhancements. This PR resolves every High and every Medium finding (19 services), each backed by a real-SDK regression test.

What we found (blast radius)

The emulator's GCP surface was strong on dispatch but had a recurring class of silent-wrong-data bugs: a resource created with configuration X read back as a hardcoded default, because the portable driver couldn't model the GCP-specific field and the handler dropped it. Plus two GAPIC-breaking LRO routing gaps and a few broken standard flows.

How we fixed it

  • Silent-wrong-data reads → persist the GCP-specific fields and echo them on read.
  • Driver can't model a field → round-trip it via a reserved tag key or handler-local state, so the shared driver interfaces (and the AWS/Azure providers) stay untouched.
  • GAPIC LRO 404 → add the /operations/{op} route that returns a done operation.

High

Service Fix
compute Instance read echoes networkInterfaces (subnet + assigned IP); image create no longer needs a phantom instance; operations list
networks Firewall rules (allowed/denied/sourceRanges/direction/priority/targetTags) persist + round-trip; autoCreateSubnetworks; subnet parent-network link
pubsub Subscription registry: distinct sub/topic names, ackDeadline/labels round-trip, effective delete, publishTime
monitoring Alert-policy conditions/combiner/enabled/userLabels round-trip; alertPolicies.patch
clouddns delete+add of the same record set (the standard update) no longer fails AlreadyExists; unique change id; dnsName FQDN
iam SA :getIamPolicy/:setIamPolicy, :signBlob/:signJwt/:generateAccessToken, :enable/:disable
artifactregistry / eventarc /operations/{op} routing so GAPIC .Wait() resolves (was 404)

Medium

artifactregistry non-DOCKER format + description · eventarc serviceAccount + labels · gcs bucket PATCH (versioning + labels) · cloudsql getBackupRun surfaces real errors · alloydb real lifecycle state · gke desiredMasterVersion/desiredNodeVersion applied · cloudfunctions httpsTrigger.url + :generateUploadUrl · firestore ALREADY_EXISTS on explicit-id create · memorystore memorySizeGb/redisVersion/displayName round-trip + instances.patch · cloudlogging severity/jsonPayload/labels/insertId round-trip + orderBy desc · secretmanager create makes an empty container (no phantom version) · loadbalancer backend-service description/portName/healthChecks round-trip · vertexai endpoint :countTokens + stream routing.

Alternatives not taken

  • Widening the shared driver types to hold GCP-specific fields — rejected: it would ripple into the AWS/Azure providers. Reserved-tag / handler-local storage keeps the change GCP-local.
  • gcs md5Hash — deliberately left empty: the driver stores a SHA-256 ETag, not the object bytes, so any synthesized md5Hash would mismatch the content and make checksum-validating clients fail — worse than omitting it.

Test plan

  • go build ./..., go vet ./..., gofmt, go mod tidy clean
  • go test ./... green (full suite); new real-SDK round-trip tests per fixed service assert field values
  • golangci-lint --new-from-rev0 new issues
  • CodeQL — only the pre-existing baseline alerts (middleware, databricks, sts, cloudsql/subresources); zero new

Deferred follow-ups (Low-value long tail / needs infrastructure)

Tracked, not in this PR: monitoring notificationChannels REST surface; iam :undelete + org-level roles (/v1/organizations/) + updateMask on updateSA/updateRole; cloudsql PUT-vs-PATCH replace semantics (SDK Update sends the full resource, so partial-merge is observationally equivalent); firestore fabricated timestamps + REST documents.list pagination; vertexai multimodal generateContent parts (driver is text-only); cloudasset searchAllIamPolicies (no cross-service policy index exists) + assetTypes filter; bigtable/fcm Low fidelity nits; cross-cutting list pagination (pageSize/pageToken/nextPageToken) — emulator datasets are small, low practical impact.

Conclusion

All High + Medium GCP parity findings resolved across 19 services with regression coverage; shared drivers and other cloud providers untouched.

Closes #321


Standalone-server E2E round (68608ea)

Follow-up: drove the compiled cloudemu serve --providers gcp binary (all 23 handlers registered at once) with real REST lifecycles per service — not just the single-driver package tests. This surfaced full-server-only issues that package tests structurally can't (dispatch collisions between handlers sharing a path prefix, greedy fallbacks). All fixed and now guarded by a new full-server harness (server/gcp/fullserver_test.go):

  • Shared LRO operations handler (server/gcp/lro) — location-scoped operation polls (/v1/projects/{p}/locations/{l}/operations/{op}) were greedily claimed by alloydb/gke (registered first) and 404'd artifactregistry/eventarc/memorystore ops. One shared handler now owns them, returning a done operation (superset of longrunning done + GKE status).
  • compute instances.delete now removes the instance (GET-after-delete → 404) instead of leaving a TERMINATED tombstone.
  • firestore writes auto-create the collection (real Firestore) — a first document write no longer 404s "collection not found".
  • GCS Matches no longer swallows unclaimed API-version paths (/v1, /sql, …) as bogus bucket lookups.
  • cloudlogging entries:list sorts by timestamp for orderBy (was reverse-only, mis-ordering out-of-order writes).
  • pubsub createTopic tolerates an empty body; networks duplicate network create → 409.

Verified false positive: the agent-reported vertexai endpoint :countTokens/:generateContent 405 was a shell/curl artifact mangling the :verb suffix when interpolated from a variable — a literal URL returns 200. The fix was already correct.

Additional deferrals from this round: compute requested networkIP/disks echo; regional forwardingRule selfLink/portRange/target; unscoped /v1/roles + org roles (now a clean not-found rather than a bogus bucket error); cloudasset export-op project-scoped collision with Cloud SQL; alloydb is shadowed by GKE in the shipped binary (they share /clusters and are mutually exclusive by design — AlloyDB needs a custom build); bigtable/fcm Low nits.

…services (stackshy#321)

High-severity (silent-wrong-data reads, broken flows, missing surface):
- compute: echo networkInterfaces on instance read; image-from-disk (no phantom instance); operations list
- networks: persist+echo firewall rules; autoCreateSubnetworks; subnet parent-network link
- pubsub: subscription registry — distinct sub/topic names, ackDeadline/labels round-trip, effective delete, publishTime
- monitoring: real alert-policy conditions/combiner/enabled/labels round-trip + alertPolicies.patch
- clouddns: delete+add of the same rrset no longer AlreadyExists; unique change id; dnsName FQDN
- iam: SA getIamPolicy/setIamPolicy, signBlob/signJwt/generateAccessToken, enable/disable
- artifactregistry, eventarc: /operations/{op} routing so GAPIC .Wait() resolves (was 404)

Medium fidelity:
- artifactregistry: non-DOCKER format + description; eventarc: serviceAccount + labels
- gcs: bucket PATCH wiring versioning + labels
- cloudsql: getBackupRun surfaces real errors instead of masking as 404
- alloydb: report the driver's real lifecycle state (not hardcoded READY)
- gke: apply desiredMaster/NodeVersion on UpdateCluster
- cloudfunctions: emit httpsTrigger.url; add :generateUploadUrl
- firestore: CreateDocument returns ALREADY_EXISTS on explicit-id collision
- memorystore: memorySizeGb/redisVersion/displayName round-trip + instances.patch (Update)
- cloudlogging: severity/jsonPayload/labels/insertId round-trip + orderBy desc
- secretmanager: create makes an empty container (no phantom seeded version)
- vertexai: endpoint :countTokens + stream routing
- loadbalancer: backendService description/portName/healthChecks round-trip

Each fix is backed by a real-SDK regression test. GCP fields the portable
drivers can't model are round-tripped via reserved tags or handler-local state,
keeping the shared driver interfaces (and the AWS/Azure providers) untouched.
…oken flows (stackshy#321)

Standalone-server E2E (driving cloudemu serve --providers gcp with all handlers
registered) surfaced bugs the single-driver package tests can't see:

- Shared location-operations handler (server/gcp/lro): owns
  /v1/projects/{p}/locations/{l}/operations/{op}, registered first, returning a
  done operation (superset: longrunning `done` + GKE `status`). Fixes
  artifactregistry/eventarc/memorystore operation polls that alloydb/gke
  previously shadowed with a 404.
- compute: instances.delete now removes the instance (GET-after-delete is 404)
  via a GCP-local hard-delete, instead of leaving a TERMINATED tombstone.
- firestore: writes auto-create the collection (real Firestore behavior); a
  first document write no longer 404s "collection not found".
- gcs: Matches no longer swallows unclaimed API-version paths (/v1, /sql, …) as
  bogus bucket lookups.
- cloudlogging: entries:list sorts by timestamp for orderBy (was reverse-only,
  mis-ordering out-of-order writes).
- pubsub: createTopic tolerates an empty body; networks: duplicate network
  create returns 409.

Adds server/gcp/fullserver_test.go — a full-server harness exercising the
collision paths so these regressions are caught in-suite.
- fcm: reject a message setting more than one target (token/topic/condition) —
  real FCM returns INVALID_ARGUMENT.
- cloudasset: searchAllResources honors assetTypes and both search/assets.list
  emit nextPageToken so paged callers don't silently miss truncated results.

(bigtable's InvalidArgument-on-missing-parent is intentional and tested, so it
is left as-is; searchAllIamPolicies stays empty — it needs a cross-service IAM
policy index the engine doesn't expose.)

@NitinKumar004 NitinKumar004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Deep review — GCP parity (closes #321) — request changes

Reviewed at 9af10fbc (isolated worktree, whole-repo go test + -race, four adversarial lenses, verification against the vendored Go client structs). (The latest commit 9af10fb closes deferred cloudasset/fcm low nits — it does not touch any of the findings below, which still stand.)

The core of this PR is strong — clean dispatch with no collisions, 6 of 9 High findings fully fixed with real assertions, and the reserved-tag round-trip approach is leak-free and thread-safe. I'm requesting changes for two fixes that reintroduce the exact silent-wrong-data class #321 set out to eliminate, one headline High fix that looks undelivered for its target client, and three "fixed" claims with no test.

Gates — all green

build / vet / gofmt / go mod tidy clean · go test ./... 225 ok, 0 fail (no cascade) · -race clean on the full-server GCP harness. Honest scope: 58 GCP files, +2,167/−274; shared drivers untouched (verified).

Solid (verified, not just claimed)

  • Dispatch / no cascade / no duplicate: the shared lro handler is tightly scoped (GET-only, 5-segment locations/…/operations/{op}) and does not steal real-data polls (every mutation inlines its result in the create response); gcs Matches now excludes reserved API prefixes; zero duplicate claims, no registration regression — guarded by fullserver_test.go.
  • 6/9 High fully fixed + asserted: compute NIC subnet+IP, networks firewall (all 6 fields + autoCreateSubnetworks + subnet parent link), clouddns delete+add, IAM verbs (all 7), AR format+description, eventarc serviceAccount+labels.

Blockers — correctness / wire

  1. monitoring alertPolicies.patch silently disables the policy. server/gcp/monitoring/handler.go:214 sets cur.Enabled = body.Enabled unconditionally while every other field is nil-guarded; with enabled bool json:"enabled,omitempty", a PATCH that omits enabled decodes it to false. Create enabled=truePATCH {"combiner":"OR"} → policy silently disabled. This is the #321 silent-wrong-data class, reintroduced (real GCP honors updateMask). (inline)
  2. pubsub subscriptions.list ignores the new registry. server/gcp/pubsub/handler.go:174-181 emits one phantom subscription per driver queue, named after the topic, never consulting h.subs. After creating billing-sub on topic events, List returns a phantom events sub and omits billing-sub — Get was fixed, List wasn't, and it re-violates the "distinct sub/topic names" High. (inline)
  3. AR/eventarc GAPIC LRO .Wait() looks undelivered + unverified. operation.response is emitted without @type (server/gcp/artifactregistry/operations.go:101 doneOperation), whereas the codebase's own convention elsewhere (cloudfunctions, vertexai, cloudasset) includes it — vertexai's comment explicitly says done LROs must carry @type for SDK Any-decoding. The AR/eventarc regression tests use google.golang.org/api REST clients, not the apiv1 GAPIC clients that findings #8/#9 targeted, so the "GAPIC .Wait() resolves (was 404)" headline is unproven and likely still broken (now a decode error instead of a 404). (inline)
  4. monitoring drops non-threshold conditions. server/gcp/monitoring/types.go models only conditionThreshold; conditionAbsent / MQL / PromQL / matchedLog are discarded on decode and never echoed — silent data loss for those policy types.

Blockers — test coverage (the "every finding backed by a real-SDK regression test" claim is overstated)

  1. gke desiredMasterVersion/desiredNodeVersion — implemented, but no test sets or asserts them (the SDK Update test asserts logging service instead).
  2. loadbalancer description/portName/healthChecks — round-trip implemented (operations.go:246-247), but the test asserts only Name+Protocol; the three named fields are never set or read back.
  3. cloudfunctions httpsTrigger.url + :generateUploadUrl — implemented, but no test exercises either endpoint.

Low (non-blocking)

  • gcs isReservedAPIPrefix false-rejects buckets named v+digit (e.g. v2-assets) on the direct-media path.
  • monitoring/pubsub handler-local maps aren't project-scoped (cross-project name collision); monitoring is keyed by displayName, so a duplicate displayName in one project 409s (real GCP allows it).
  • compute network-only NIC (only network, no subnetwork) reads back network:"".
  • Weak coverage: gcs labels PATCH, cloudlogging insertId, cloudsql getBackupRun error path, alloydb state mapping — asserted partially or not at all.
  • Lint hygiene (PR-introduced, CLAUDE.md local 0-issue gate): gocyclo in cloudlogging/clouddns/iam changed funcs, analogue misspell in eventarc; the gcs:719 err shadow is benign.

Net: excellent groundwork and clean dispatch. Fixing #1#2 (guard Enabled / honor updateMask; make List read h.subs), delivering #3 (emit @type, and test with the GAPIC apiv1 client the finding names), modeling #4's condition types, and adding the three missing field-assertion tests (#5#7) would get this to a clean approve.

Comment thread server/gcp/monitoring/handler.go Outdated
cur.NotificationChannels = body.NotificationChannels
}

cur.Enabled = body.Enabled

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Medium (correctness) — PATCH silently disables the policy. Unlike every other field here (Conditions, UserLabels, NotificationChannels are all nil-guarded), cur.Enabled = body.Enabled is unconditional. Enabled is bool with json:"enabled,omitempty", so a partial PATCH that omits enabled decodes it as false: create a policy enabled=true, then PATCH {"combiner":"OR"} → the policy is silently disabled. This is exactly the #321 silent-wrong-data class the PR set out to kill. Real GCP alertPolicies.patch applies only the paths in updateMask; at minimum make Enabled a *bool (or gate it on updateMask) so an omitted field is left unchanged. The test at monitoring_test.go sends enabled:false alongside combiner, so it never exercises the omitted-field path.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 52bb2a4. patchPolicy now decodes enabled as *bool, so an omitted field leaves the policy's state unchanged (only an explicit enabled is applied) — a partial PATCH no longer silently disables it. The semantics test now PATCHes {"combiner":"OR"} (no enabled) and asserts the policy stays enabled.

…RO, missing tests (stackshy#321)

Blockers:
- monitoring alertPolicies.patch: decode enabled as *bool so an omitted field
  leaves it unchanged instead of silently disabling the policy.
- pubsub subscriptions.list: read the subscription registry (distinct name +
  ackDeadline/labels) instead of emitting one phantom sub per topic queue.
- artifactregistry/eventarc: emit the LRO response with an @type so a GAPIC
  apiv1 .Wait() can Any-decode it; add real apiv1 CreateX(...).Wait() tests.
- monitoring: round-trip non-threshold conditions (conditionAbsent / MQL /
  PromQL / matchedLog) instead of modelling only conditionThreshold.

Test coverage the earlier claim overstated:
- gke Update now asserts desiredMasterVersion/desiredNodeVersion apply.
- loadbalancer asserts description/portName/healthChecks round-trip.
- cloudfunctions asserts httpsTrigger.url + generateUploadUrl.

Low:
- gcs Matches only reserves whole-segment API versions (v1, v1beta4) — a bucket
  like "v2-assets" is no longer false-rejected on the direct-media path.
- fix "analogue" misspell in eventarc comments.

Deferred (documented, non-blocking): monitoring/pubsub project-scoped keying
(monitoring keyed by displayName), compute network-only NIC network:"" echo.
@thzgajendra

Copy link
Copy Markdown
Collaborator Author

Thanks for the deep review — all blockers + the flagged Low items are addressed in 52bb2a4 (full go test ./... green, golangci-lint --new-from-rev = 0 new).

Blockers

  1. monitoring PATCH silently disablesenabled now decodes as *bool; an omitted field is left unchanged (only an explicit value applies). Test PATCHes without enabled and asserts it stays true. (inline)
  2. pubsub subscriptions.list — now reads the subscription registry (h.subs), returning the real distinct-named subs with their ackDeadline/labels, not one phantom sub per topic queue. Test lists after creating billing-sub on topic events and asserts the real sub.
  3. AR/eventarc GAPIC .Wait() — you were right: the response lacked @type, so a GAPIC Any-decode would fail. The operation response now carries the resource @type (…artifactregistry.v1.Repository / …eventarc.v1.Trigger). Added real apiv1 GAPIC tests (NewRESTClientCreateRepository/CreateTrigger(...).Wait()) that now pass — the exact client the finding named. (Note: the apiv1 client also encodes the AR format enum as a number, which the string-typed request field rejects — an orthogonal request-encoding quirk, not this finding; noting it as a follow-up rather than expanding scope.)
  4. monitoring non-threshold conditionsalertCondition now round-trips conditionAbsent / MQL / PromQL / matchedLog verbatim, not just conditionThreshold. New test asserts a conditionAbsent policy survives a create→read.

Missing test coverage (5–7) — added field assertions: gke desiredMasterVersion/desiredNodeVersion (asserts currentMasterVersion/currentNodeVersion change), loadbalancer description/portName/healthChecks, cloudfunctions httpsTrigger.url + :generateUploadUrl.

Low — gcs Matches now reserves only whole-segment API versions (v1, v1beta4), so a bucket like v2-assets is no longer false-rejected (unit test added both directions); fixed the analogue misspell.

Deferred (non-blocking, documented): monitoring/pubsub project-scoped keying (monitoring keyed by displayName → dup-displayName-across-projects 409) needs a key redesign the current tests depend on; compute network-only NIC reads network:"". Happy to take these in a follow-up if you'd prefer them here.

@NitinKumar004 NitinKumar004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review — GCP parity (closes #321) — one change requested

Re-reviewed at 52bb2a4, the commit addressing my earlier review. All seven of my earlier findings are resolved, each with a passing test — this is very close. I'm holding on one remaining determinism nit before approve.

Gates — green

build / vet / gofmt / go mod tidy clean · go test ./... 225 ok, 0 fail (no cascade) · -race clean on all changed GCP packages.

Earlier findings — all fixed + verified

  • monitoring alertPolicies.patch silent-disableEnabled is now *bool; an omitted enabled is left unchanged. ✅
  • pubsub subscriptions.list phantom subs → now lists the h.subs registry with real name/topic/ackDeadline/labels (TestSDKPubSubSubscriptionMetadata). ✅
  • AR/eventarc GAPIC LROoperation.response now carries the correct @type, and — the part I most wanted — there are now real apiv1 GAPIC tests (TestGAPICCreateRepositoryWait, TestGAPICCreateTriggerWait) driving NewRESTClient(...).Create(...).Wait(). Both pass. ✅
  • monitoring non-threshold conditionsconditionAbsent/matchedLog/MQL/PromQL now round-trip (TestMonitoringNonThresholdCondition). ✅
  • gke / loadbalancer / cloudfunctions untested claims → now set + assert the fields (CurrentMaster/NodeVersion, TestSDKGCPBackendServiceRoundTrip, HttpsTrigger.Url + GenerateUploadUrl). ✅
  • gcs v+digit false-reject → fixed with reserved_prefix_test.go. ✅

Requested change (the only blocker)

Determinism — subscriptions.list returns subscriptions in nondeterministic order. The new List iterates the h.subs map, so with two or more subscriptions the response order varies run-to-run. This is the repo's determinism pillar (elsewhere lists sort before emitting), and the test only creates one subscription so it doesn't surface. Please sort the subscriptions (by name) before writing the response, and extend the test to two subs to lock the order. (inline)

Everything else is done — once the list is deterministic this is an approve.

Comment thread server/gcp/pubsub/handler.go Outdated
h.mu.RLock()
out := listSubscriptionsResponse{Subscriptions: make([]subscription, 0, len(h.subs))}

for subName, meta := range h.subs {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Low/determinism — nondeterministic list order. Iterating h.subs (a map) means subscriptions.list returns subs in a different order each run once there are 2+. The repo sorts list output elsewhere for determinism, and the test only creates one sub so it doesn't catch this. Collect into a slice and sort.Slice(...) by subscription name before writing the response; add a second subscription to the test to lock the order.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 91c238d. subscriptions.list now collects the subscription names, sort.Strings them, and emits in sorted order, so the response is deterministic regardless of map-iteration order — consistent with the repo's other list endpoints. TestSDKPubSubSubscriptionMetadata now creates a second subscription (analytics-sub, created after billing-sub but sorting before it) and asserts List returns [analytics-sub, billing-sub]; passes deterministically across -count=3.

subscriptions.list iterated the h.subs map directly, so with two or more
subscriptions the response order varied run-to-run. Collect the names,
sort them, and emit in order — matching the repo's deterministic list
pillar. Extend the SDK test to two subscriptions (created out of sort
order) and assert the sorted result.
@thzgajendra

Copy link
Copy Markdown
Collaborator Author

Addressed the one remaining blocker (determinism of subscriptions.list) in 91c238d:

  • subscriptions.list now sorts subscriptions by name before emitting, so the response is deterministic regardless of Go map-iteration order — matching the repo's other list endpoints.
  • TestSDKPubSubSubscriptionMetadata extended to two subscriptions (analytics-sub created after billing-sub but sorting before it); asserts List returns them in sorted order. Passes deterministically over -count=3.

Local gates on the change: build / vet / go test ./server/gcp/pubsub/... green; golangci-lint --new-from-rev 0 new. Ready for re-review.

@NitinKumar004 NitinKumar004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review — GCP parity (closes #321) — approve

The last remaining item from my previous review is now fixed. 91c238d sorts subscriptions.list output by name for determinism, and the test now creates a second subscription (analytics-sub, which sorts before billing-sub) and asserts len==2 in sorted order — a real regression guard proving the list sorts by name rather than echoing map/insertion order. Verified green (build + server/gcp/pubsub tests pass at this head).

That closes the loop on everything: all seven earlier findings were resolved in 52bb2a4 (monitoring PATCH *bool, pubsub subscription registry, AR/eventarc @type with real apiv1 GAPIC .Wait() tests, monitoring non-threshold conditions, and the gke/loadbalancer/cloudfunctions field-asserting tests), and this commit resolves the determinism nit I held on.

Nothing outstanding. LGTM.

@thzgajendra
thzgajendra merged commit eac1cc0 into stackshy:development Aug 5, 2026
16 of 17 checks passed
@thzgajendra thzgajendra mentioned this pull request Aug 5, 2026
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.

GCP parity E2E audit: bugs, gaps & enhancements across all 23 services

2 participants