orca: limited s3 compatibility#172
Merged
Merged
Conversation
Replaces the EdgeHandler's plain-text error responses with the
standard AWS S3 <Error> envelope so SDKs (aws-sdk-go-v2, boto3,
MinIO client) surface a typed error code to callers. HEAD requests
get status code + headers only, matching real S3 behavior.
Mapping:
origin.ErrNotFound -> 404 NoSuchKey
origin.ErrAuth -> 502 OriginUnauthorized (see comment)
UnsupportedBlobTypeError -> 502 OriginUnsupported
OriginETagChangedError -> 502 OriginETagChanged
MissingETagError -> 502 OriginMissingETag
default -> 502 OriginUnreachable
range failures -> 416 InvalidRange
ListBuckets/ListObjectsV2/HeadBucket -> 501 NotImplemented
unsupported method -> 405 MethodNotAllowed
auth stub -> 401 AccessDenied
ErrAuth maps to 502 rather than 403: a 401/403 from the upstream
origin means orca's own credentials were rejected, not the client's,
so 403 would mislead the client into rotating credentials that are
not at fault. The OriginUnauthorized code lets operators tell the
case apart from a generic origin failure.
Also fixes a routing doc/code mismatch in EdgeHandler.ServeHTTP: the
comment claimed GET / returned 405 but the code returned 501 and
labelled it ListObjectsV2 regardless of whether a bucket was
present. Splits the GET branch so GET / -> ListBuckets and
GET /{bucket}/ -> ListObjectsV2 (both 501) and updates the comment.
The InternalHandler is intentionally left unchanged; it is the
peer-to-peer surface between orca replicas and is never consumed by
an S3 SDK.
…ster
Adds TestS3SDK in internal/orca/inttest/s3sdk_test.go: a suite of
aws-sdk-go-v2 calls against orca's edge listener that proves the SDK
unmarshals orca's S3 <Error> envelope into typed errors
(*s3types.NoSuchKey, *s3types.NotFound) and surfaces the Code via
smithy.APIError for codes without typed shapes (InvalidRange,
NotImplemented, MethodNotAllowed). This is the headline contract
test for the XML compatibility work: TestS3Errors verified the bytes
on the wire are correct, this suite verifies real S3 client code
can consume them.
Subtests covered:
- GetObject success (positive control)
- GetObject missing key -> *s3types.NoSuchKey
- HeadObject missing key -> *s3types.NotFound (HEAD-no-body
contract: SDK relies on status code alone)
- GetObject out-of-range -> smithy.APIError Code=InvalidRange
- GetObject byte range -> 206 + correct body slice + ContentRange
- ListObjectsV2 -> smithy.APIError Code=NotImplemented
- PutObject -> smithy.APIError Code=MethodNotAllowed
Also restructures TestS3Errors into a single parent with parallel
subtests sharing one cluster, saving ~18s of repeated cluster
startup. Both files use t.Cleanup rather than defer cancel because
parallel subtests are paused until the parent returns; a defer
would cancel the context before the subtests resume.
TestS3SDK previously covered HeadObject only for the missing-key typed-error case. Adds HeadObject_Size_TypedFields verifying that a HEAD against a present object surfaces the correct size via aws-sdk-go-v2's typed *out.ContentLength field plus a non-empty ETag. This closes a gap where neither integration nor SDK suites checked that prefetchers and range planners (the dominant users of HEAD) see a correct object size. The two existing inttest HEAD calls in e2e_test.go only inspect the ETag header.
jveski
approved these changes
May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the EdgeHandler's plain-text error responses with the standard AWS S3
<Error>envelope so SDKs (aws-sdk-go-v2, boto3, MinIO client) surface a typed error code to callers. HEAD requests get status code + headers only, matching real S3 behavior.Also fixes a routing doc/code mismatch in
EdgeHandler.ServeHTTP: the comment claimedGET /returned 405 but the code returned 501 and labelled itListObjectsV2regardless of whether a bucket was present.Error mapping
origin.ErrNotFoundNoSuchKeyorigin.ErrAuthOriginUnauthorized(see below)*UnsupportedBlobTypeErrorOriginUnsupported*OriginETagChangedErrorOriginETagChanged*MissingETagErrorOriginMissingETagOriginUnreachableInvalidRangeGET /NotImplemented(ListBuckets)GET /{bucket}/NotImplemented(ListObjectsV2)HEAD /{bucket}/NotImplemented(HeadBucket)MethodNotAllowedAccessDeniedWhy
ErrAuth-> 502 (not 403)A 401/403 from the upstream origin means orca's own credentials were rejected by the origin, not the client's. Returning 403 to the client would falsely imply the client should rotate its own credentials, which would not fix anything. 502 BadGateway communicates that orca cannot satisfy the request through no fault of the client; the orca-specific
OriginUnauthorizedcode lets operators with access to orca logs tell this case apart from a generic origin failure. This rationale is documented in the code.Routing split
Previously
GET /andGET /{bucket}/both fell into the same branch that returned 501 with the message "ListObjectsV2 not implemented in MVP", which was both inaccurate forGET /and contradicted the in-code routing comment. They are now split:GET /-> 501NotImplemented("ListBuckets is not implemented by orca.")GET /{bucket}/-> 501NotImplemented("ListObjectsV2 is not implemented by orca.")HEAD /{bucket}/-> 501NotImplemented("HeadBucket is not implemented by orca.")The routing comment has been updated to match the actual behavior.
Scope
XML errors apply to the EdgeHandler only. The InternalHandler is the peer-to-peer surface between orca replicas and is never consumed by an S3 SDK; its current plain-text/JSON responses are unchanged.
Testing
writeS3Error(GET path, HEAD body suppression, omitted-empty optional fields, nil request).TestWriteOriginErrorrewritten to assert XML<Code>rather than substring matching.GET /,GET /{bucket}/,HEAD /{bucket}/, unsupported method, auth-enabled.error_test.go): on-the-wire end-to-end checks forNoSuchKeyXML, HEAD-no-body on 404,InvalidRangeXML, andListObjectsV2501.Code.