Repository navigation
fix(atelet): fall back to anonymous GCS read with objectstore-plugin Internal - #2614
Conversation
…rns Internal Fixes an issue introduced in agent-substrate#2439 where running on GKE with a GCS backend causes gs://gvisor asset downloads to fail. When querying gs://gvisor using the cluster's ambient GCP credentials, GCS returns 403 Forbidden, which status.go maps to codes.Internal. Because codes.Internal was omitted from anonymousFallbackCodes, atelet aborted rather than falling back to an anonymous GCS read. This adds codes.Internal to anonymousFallbackCodes and adds unit tests covering the GCS Internal fallback path. - **Unit tests**: `go test ./cmd/atelet/...` passes cleanly. - **E2E validation on GKE**: Verified on Rapid GKE 1.37 (`DATAPLANE=envoy`) with Counter demo: - Actor creation, memory persistence across requests 1 and 2, suspension to snapshot, and resumption from snapshot on request 3 all pass 100%. Fixes agent-substrate#2613 - [ ] Tests pass - [ ] Appropriate changes to documentation are included in the PR
|
One thing the description should say: Could you also paste the body of the 403 from the plugin logs? It'd be good to record the actual cause in the PR. |
|
Neither component logged the actual error: the plugin doesn't log failures, and atelet deliberately drops the plugin's status text so credentials can't leak into crash messages, so all we have is code=Internal. I'm updating this PR so the plugin logs any error it maps to Internal and atelet warns whenever it falls back after a plugin failure, and I'll paste the error from a fresh GKE run once it's redeployed. |
Benjamin Elder (BenTheElder)
left a comment
There was a problem hiding this comment.
thanks for working on a fix, this is blocking all manual testing on GKE
note: you can workaround it in the short term by running your own authenticated mirror ... but it's not great.
|
After getting local logging into the plugin and atelet for the issue, it looks like I'm seeing a 412 error instead (still an unrecognized error which is why it gives Internal) objectstore-plugin: atelet: Jetski analysis of this failure on GKE is that the objectstore-plugin authenticates with a Workload Identity federation token, which GCS rejects on buckets that use object ACLs instead of uniform access, like the public gs://gvisor. Anonymous reads of the same object succeed. Thanks for all the feedback, reading through the comments rn |
- objectstore-plugin logs a scrubbed warning when it returns Internal: HTTP status, plus the service's code and message where the storage backend deems them safe. Never the raw error text. - atelet warns when it falls back after Internal or Unavailable. - Update the AssetProvider contract, api-guide.md and threat model T-08. - Fix the fallback comments and drop the duplicate test.
|
Thanks all for the thorough review! I've pushed an update addressing the feedback:
I reran validation of this version on GKE 1.37 (Envoy dataplane): gVisor prewarm falls back and succeeds, and the counter demo passes create, persistence across requests, suspend, and resume from snapshot. I'll also open an issue to discuss the longer-term design for plugin fallback. |
|
ILLUM1N0X Updated the description to say exactly that. The real error (posted above) is a 412 |
|
Benjamin Elder (@BenTheElder) Thanks! Aiming to land this quickly to unblock GKE testing, and I'll follow up on the long-term fallback design separately. |
|
Opened #2630 to discuss the longer-term design for plugin fallback to anonymous reads of public assets, including |
DescribeServiceError no longer names GCS: each backend appends its describer from an init function in its own file.
|
Something I don't have an immediately good idea on how to handle - the error message proliferation - error messages need to be parsed in a different way across our existing in-tree providers, and I figured each provider would want control over what info is surfaced per error (in case there is sensitive info in an error message). For now I have the init adding optional per provider processing in but it's not my favorite long-term option as it won't play nicely with 3rd party plugins and it loops through each option regardless which in-tree provider is being used - I think long-term I'd prefer something more extensible. Curious if anyone has any thoughts here and if we need to solve this cleanly in this PR or if it could be a followup cleanup issue. |
|
Anna Pendleton (@annapendleton) I wouldn't solve it here. Since every backend lives in pkg/objectstorage, DescribeServiceError could just call describeGCSError before the HTTPStatusCode fallback and skip the init registry, and third-party plugins log their own errors anyway, so the extensible version can be a follow-up. |
|
We are cutting 0.5 today and would like this to be included. Can we make sure this is approved and merged in the next few hours? |
|
I'm working on quick fix to address Anish Ramasekar (@aramase) 's feedback to remove the init registry, then I'd say this is ready for submission if there's no other outstanding blockers. I'm creating an issue for the extensible error messaging to track that for future PR. |
This reverts commit e98c3aa.
Benjamin Elder (BenTheElder)
left a comment
There was a problem hiding this comment.
one nit for follow-up.
| slog.InfoContext(ctx, "Object-store plugin did not serve sandbox asset; downloading it anonymously", | ||
| // Internal and Unavailable mean the plugin failed rather than declined | ||
| // the URL, so falling back must not hide them: warn. The plugin logs | ||
| // why it failed; atelet logs only the code (see fetchFromPlugin). |
There was a problem hiding this comment.
🤖 nit 🟢 – "The plugin logs why it failed" only holds for Internal. toStatus logs its catch-all, but the plugin's own Unavailable statuses aren't logged, and an unreachable plugin can't log anything. Maybe scope that sentence to Internal?
Fixes #2613
On GKE with a GCS backend, the object-store plugin's Workload Identity token gets
412 PreconditionFailed("The operation requires that Uniform Bucket Level Access be enabled.") from the publicgs://gvisor.toStatusmaps that, like any error it doesn't recognize, toInternal, and atelet didn't fall back onInternal, so sandbox creation failed after #2439.Changes
Internal. In practice that's any unexpected plugin failure. That's acceptable because anonymous reads only reach public objects, and atelet still checks the sha256.Internal, and atelet warns when it falls back.AssetProvidercontract,api-guide.mdand threat model T-08.Testing
go test ./cmd/atelet/... ./internal/objectstoreplugin/... ./pkg/objectstorage/...main. With this PR, gVisor prewarm falls back and succeeds, and the counter demo passes create, persistence across requests, suspend, and resume from snapshot.A follow-up issue will discuss the long-term design for plugin fallback.