Some checks failed
Docker image builds / build (push) Waiting to run
Asset Build Verification / asset_verification (push) Has been cancelled
Docs deploy / build (push) Has been cancelled
Go Mod Tidy Check / go_mod_tidy_check (push) Has been cancelled
Go / go_tests (push) Has been cancelled
Package builds (unstable) / package_builds (push) Has been cancelled
Smoke tests / smoke-test (default-config-macro) (push) Has been cancelled
Smoke tests / smoke-test (docker-registry) (push) Has been cancelled
Smoke tests / smoke-test (double_slash) (push) Has been cancelled
Smoke tests / smoke-test (forced-language) (push) Has been cancelled
Smoke tests / smoke-test (git-clone) (push) Has been cancelled
Smoke tests / smoke-test (git-push) (push) Has been cancelled
Smoke tests / smoke-test (healthcheck) (push) Has been cancelled
Smoke tests / smoke-test (i18n) (push) Has been cancelled
Smoke tests / smoke-test (log-file) (push) Has been cancelled
Smoke tests / smoke-test (nginx) (push) Has been cancelled
Smoke tests / smoke-test (palemoon/amd64) (push) Has been cancelled
Smoke tests / smoke-test (robots_txt) (push) Has been cancelled
Check Spelling / Check Spelling (push) Has been cancelled
SSH CI / ssh (aarch64-16k) (push) Has been cancelled
SSH CI / ssh (aarch64-4k) (push) Has been cancelled
SSH CI / ssh (ppc64le) (push) Has been cancelled
SSH CI / ssh (riscv64) (push) Has been cancelled
zizmor / zizmor latest via PyPI (push) Has been cancelled
107 lines
2.9 KiB
Go
107 lines
2.9 KiB
Go
package s3api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.sad.ovh/sophie/nuke/lib/store"
|
|
awsConfig "github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
)
|
|
|
|
var (
|
|
ErrNoRegion = errors.New("s3api.Config: no region env var name defined")
|
|
ErrNoAccessKeyID = errors.New("s3api.Config: no access key id env var name defined")
|
|
ErrNoSecretAccessKey = errors.New("s3api.Config: no secret access key env var name defined")
|
|
ErrNoBucketName = errors.New("s3api.Config: no bucket name env var name defined")
|
|
)
|
|
|
|
func init() {
|
|
store.Register("s3api", Factory{})
|
|
}
|
|
|
|
// S3API is the subset of the AWS S3 client used by this store. It enables mocking in tests.
|
|
type S3API interface {
|
|
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
|
|
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
|
|
DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error)
|
|
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
|
|
}
|
|
|
|
// Factory builds an S3-backed store. Tests can inject a Mock via Client.
|
|
// Factory can optionally carry a preconstructed S3 client (e.g., a mock in tests).
|
|
type Factory struct {
|
|
Client S3API
|
|
}
|
|
|
|
func (f Factory) Build(ctx context.Context, data json.RawMessage) (store.Interface, error) {
|
|
var config Config
|
|
|
|
if err := json.Unmarshal([]byte(data), &config); err != nil {
|
|
return nil, fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
if err := config.Valid(); err != nil {
|
|
return nil, fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
if config.BucketName == "" {
|
|
return nil, fmt.Errorf("%w: %s", store.ErrBadConfig, ErrNoBucketName)
|
|
}
|
|
|
|
// If a client was injected (e.g., tests), use it directly.
|
|
if f.Client != nil {
|
|
return &Store{
|
|
s3: f.Client,
|
|
bucket: config.BucketName,
|
|
}, nil
|
|
}
|
|
|
|
cfg, err := awsConfig.LoadDefaultConfig(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't load AWS config from environment: %w", err)
|
|
}
|
|
|
|
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
|
|
o.UsePathStyle = config.PathStyle
|
|
})
|
|
|
|
return &Store{
|
|
s3: client,
|
|
bucket: config.BucketName,
|
|
}, nil
|
|
}
|
|
|
|
func (Factory) Valid(data json.RawMessage) error {
|
|
var config Config
|
|
if err := json.Unmarshal([]byte(data), &config); err != nil {
|
|
return fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
if err := config.Valid(); err != nil {
|
|
return fmt.Errorf("%w: %w", store.ErrBadConfig, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Config struct {
|
|
BucketName string `json:"bucketName"`
|
|
PathStyle bool `json:"pathStyle"`
|
|
}
|
|
|
|
func (c Config) Valid() error {
|
|
var errs []error
|
|
|
|
if c.BucketName == "" {
|
|
errs = append(errs, ErrNoBucketName)
|
|
}
|
|
|
|
if len(errs) != 0 {
|
|
return fmt.Errorf("s3api.Config: invalid config: %w", errors.Join(errs...))
|
|
}
|
|
|
|
return nil
|
|
}
|