feat(19.3-03): summer.yaml build.tags and the summercms_s3 tag that links the S3 uploads driver
- Manifest.Tags from build.tags (^[a-z0-9_]+$, duplicates dropped), round-tripped by SaveManifest - summer build passes -tags=<list> through goBuildArgs and prints the build tags line - attach: s3blob imported only by s3_on.go (//go:build summercms_s3); an untagged binary refuses an s3:// bucket_url naming the tag, build.tags and -tags - go mod tidy adds the AWS SDK v2 modules as indirect requirements - storage, modules, scaffolding, configuration and scaling docs plus the lagoon README
This commit is contained in:
@@ -16,6 +16,11 @@ import (
|
||||
|
||||
const defaultPublicPathPrefix = "/storage/uploads"
|
||||
|
||||
// S3BuildTag is the Go build tag that links the gocloud S3 driver
|
||||
// (s3blob, imported only by s3_on.go). List it under build.tags in summer.yaml so
|
||||
// summer build passes it to go build; a raw go build needs -tags summercms_s3.
|
||||
const S3BuildTag = "summercms_s3"
|
||||
|
||||
var (
|
||||
prefixMu sync.RWMutex
|
||||
publicPathPrefix = defaultPublicPathPrefix
|
||||
@@ -57,8 +62,24 @@ func normalizeBucketURL(raw string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// OpenBucket opens storage.uploads.bucket_url (file:// or mem://) and
|
||||
// records storage.uploads.public_path_prefix. An empty bucket_url fails boot.
|
||||
// checkS3Driver refuses an s3:// bucket URL when the binary was built
|
||||
// without S3BuildTag, naming the tag and where to set it.
|
||||
func checkS3Driver(bucketURL string) error {
|
||||
if s3Enabled {
|
||||
return nil
|
||||
}
|
||||
scheme, _, ok := strings.Cut(bucketURL, "://")
|
||||
if !ok || !strings.EqualFold(scheme, "s3") {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("attach: storage.uploads.bucket_url uses s3:// but this binary was built without the %s build tag; "+
|
||||
"list %s under build.tags in summer.yaml and rebuild with summer build (a raw go build needs -tags %s, or GOFLAGS=-tags=%s)",
|
||||
S3BuildTag, S3BuildTag, S3BuildTag, S3BuildTag)
|
||||
}
|
||||
|
||||
// OpenBucket opens storage.uploads.bucket_url (file://, mem://, or s3:// in a
|
||||
// binary built with S3BuildTag) and records
|
||||
// storage.uploads.public_path_prefix. An empty bucket_url fails boot.
|
||||
func OpenBucket(ctx context.Context, cfg *compass.Config) (*blob.Bucket, error) {
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("attach: config is nil")
|
||||
@@ -71,6 +92,9 @@ func OpenBucket(ctx context.Context, cfg *compass.Config) (*blob.Bucket, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkS3Driver(bucketURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bucket, err := blob.OpenBucket(ctx, bucketURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("attach: open bucket %q: %w", raw, err)
|
||||
|
||||
60
modules/lagoon/attach/infra_smoke_test.go
Normal file
60
modules/lagoon/attach/infra_smoke_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package attach
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/compass"
|
||||
)
|
||||
|
||||
// openTestConfig writes storage.yaml into a temp config dir and opens it.
|
||||
func openTestConfig(t *testing.T, storageYAML string) *compass.Config {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "storage.yaml"), []byte(storageYAML), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := compass.Open(compass.Options{
|
||||
Dir: dir,
|
||||
Env: "development",
|
||||
Environ: []string{"SUMMER_ENV=development"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// TestOpenBucketS3Smoke runs in both tag modes: without summercms_s3 an
|
||||
// s3:// bucket_url fails boot naming the tag, with it the bucket opens
|
||||
// (s3blob connects lazily, so no network is needed).
|
||||
func TestOpenBucketS3Smoke(t *testing.T) {
|
||||
t.Setenv("AWS_ACCESS_KEY_ID", "smoke-access-key")
|
||||
t.Setenv("AWS_SECRET_ACCESS_KEY", "smoke-secret-key")
|
||||
t.Setenv("AWS_REGION", "us-east-1")
|
||||
t.Setenv("AWS_EC2_METADATA_DISABLED", "true")
|
||||
t.Cleanup(func() { setPublicPathPrefix(defaultPublicPathPrefix) })
|
||||
|
||||
cfg := openTestConfig(t, "uploads:\n bucket_url: \"s3://smoke-bucket?region=us-east-1\"\n")
|
||||
bucket, err := OpenBucket(t.Context(), cfg)
|
||||
if !s3Enabled {
|
||||
if err == nil {
|
||||
_ = bucket.Close()
|
||||
t.Fatal("OpenBucket opened s3:// without the summercms_s3 tag")
|
||||
}
|
||||
for _, want := range []string{S3BuildTag, "build.tags", "-tags " + S3BuildTag} {
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("error %q does not name %q", err, want)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("OpenBucket with the summercms_s3 tag: %v", err)
|
||||
}
|
||||
if err := bucket.Close(); err != nil {
|
||||
t.Fatalf("close: %v", err)
|
||||
}
|
||||
}
|
||||
7
modules/lagoon/attach/s3_off.go
Normal file
7
modules/lagoon/attach/s3_off.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build !summercms_s3
|
||||
|
||||
package attach
|
||||
|
||||
// Without the summercms_s3 tag the S3 driver is not linked; OpenBucket refuses
|
||||
// an s3:// bucket_url with an error naming the tag.
|
||||
const s3Enabled = false
|
||||
9
modules/lagoon/attach/s3_on.go
Normal file
9
modules/lagoon/attach/s3_on.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build summercms_s3
|
||||
|
||||
package attach
|
||||
|
||||
// Binaries built with the summercms_s3 tag (summer.yaml build.tags) link the
|
||||
// gocloud S3 driver, so storage.uploads.bucket_url may use s3://.
|
||||
import _ "gocloud.dev/blob/s3blob"
|
||||
|
||||
const s3Enabled = true
|
||||
Reference in New Issue
Block a user