fix(worker): use BaseEndpoint instead of EndpointResolverV2 for ARMOR

EndpointResolverV2 with a custom static URI does not honor UsePathStyle —
the resolver provides the final endpoint and the SDK does not re-apply
path-style bucket addressing on top of it. This means the bucket name was
dropped from the request path even with UsePathStyle=true, sending PUTs
to /replays/... instead of /armor-apexalgo/replays/...

BaseEndpoint is the SDK's documented approach for S3-compatible custom
endpoints; it sets the base URL and then correctly applies path-style
addressing to produce /bucket/key URLs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-30 09:48:53 -04:00
parent 55dffb624c
commit d126654dbb

View file

@ -5,13 +5,11 @@ import (
"bytes"
"context"
"fmt"
"net/url"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
smithyendpoints "github.com/aws/smithy-go/endpoints"
)
// B2Client handles B2 bucket operations (S3-compatible).
@ -23,12 +21,6 @@ type B2Client struct {
// NewB2Client creates a new B2 client.
func NewB2Client(cfg *Config) *B2Client {
// Parse and validate the custom endpoint
endpointURL, err := url.Parse(cfg.B2Endpoint)
if err != nil {
panic(fmt.Sprintf("failed to parse B2 endpoint: %v", err))
}
// Load AWS config with B2 credentials
// For S3-compatible endpoints (ARMOR/B2), the region is not used
// but must be set to a valid value for the SDK
@ -44,14 +36,14 @@ func NewB2Client(cfg *Config) *B2Client {
panic(fmt.Sprintf("failed to load AWS config: %v", err))
}
// Create S3 client with custom endpoint (ARMOR proxy wrapping B2)
// UsePathStyle is required: without it the SDK uses virtual-hosted style and
// drops the bucket name from the URL path (bucket ends up in hostname which
// the custom resolver replaces, losing it entirely).
// Use BaseEndpoint + UsePathStyle for S3-compatible endpoints (ARMOR/B2).
// EndpointResolverV2 with a custom resolver does NOT honor UsePathStyle —
// the resolver replaces the full URI before bucket addressing is applied,
// so the bucket ends up dropped from the path. BaseEndpoint is the SDK's
// supported path for custom S3-compatible services; path style is applied
// after the base URL is set, producing /bucket/key URLs correctly.
client := s3.NewFromConfig(awsCfg, func(o *s3.Options) {
o.EndpointResolverV2 = &customEndpointResolver{
endpointURL: *endpointURL,
}
o.BaseEndpoint = aws.String(cfg.B2Endpoint)
o.UsePathStyle = true
})
@ -135,14 +127,3 @@ func (c *B2Client) List(ctx context.Context, prefix string) ([]string, error) {
func (c *B2Client) Endpoint() string {
return c.endpoint
}
// customEndpointResolver implements s3.EndpointResolverV2 for custom S3-compatible endpoints.
type customEndpointResolver struct {
endpointURL url.URL
}
func (r *customEndpointResolver) ResolveEndpoint(ctx context.Context, params s3.EndpointParameters) (smithyendpoints.Endpoint, error) {
return smithyendpoints.Endpoint{
URI: r.endpointURL,
}, nil
}