//go:build linux && rootrunner package host import ( "context" "encoding/xml" "fmt" "net/http" "net/http/httptest" "sort" "strings" "testing" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/carlosframework/platform/internal/lease/leasetest" ) func TestPlan14LifecycleS3(t *testing.T) { model := leasetest.NewVersionedBucket() model.AddBucket("data") handler, _ := leasetest.NewS3Server(model) server := httptest.NewServer(lifecycleS3(handler, model)) defer server.Close() lifecycleEnvironment(t, server.URL) markers, err := newProductionClaimMarkers(t.Context(), "data", "eu-west-1") if err != nil { t.Fatal(err) } key := "instances/lifecycle/generations/one/generation.json" id, err := markers.PutMarker(t.Context(), "data", key, []byte("marker"), "", func(context.Context) error { return nil }) if err != nil || id == "" { t.Fatal(id, err) } listed, err := markers.(productionClaimMarkers).Client.ListObjectsV2(t.Context(), &s3.ListObjectsV2Input{Bucket: aws.String("data"), Prefix: aws.String("instances/")}) if err != nil || len(listed.Contents) != 1 || aws.ToString(listed.Contents[0].Key) != key { t.Fatal(listed, err) } } func lifecycleS3(next http.Handler, model *leasetest.VersionedBucket) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() if r.Method != http.MethodGet || strings.Trim(r.URL.Path, "/") != "data" || q.Get("list-type") != "2" { next.ServeHTTP(w, r) return } versions, err := model.ListVersions(r.Context(), "data", q.Get("prefix")) if err != nil { http.Error(w, err.Error(), 500) return } type object struct { Key, LastModified, ETag string Size int64 } type common struct{ Prefix string } result := struct { XMLName xml.Name `xml:"ListBucketResult"` Name, Prefix, Delimiter string MaxKeys, KeyCount int IsTruncated bool Contents []object CommonPrefixes []common }{Name: "data", Prefix: q.Get("prefix"), Delimiter: q.Get("delimiter"), MaxKeys: 1000} prefixes := map[string]bool{} for _, v := range versions { if !v.IsLatest || v.DeleteMarker { continue } if result.Delimiter != "" { rest := strings.TrimPrefix(v.Key, result.Prefix) if at := strings.Index(rest, result.Delimiter); at >= 0 { prefixes[result.Prefix+rest[:at+len(result.Delimiter)]] = true continue } } result.Contents = append(result.Contents, object{v.Key, v.LastModified.UTC().Format(time.RFC3339Nano), fmt.Sprintf("%q", v.ETag), v.Size}) } for p := range prefixes { result.CommonPrefixes = append(result.CommonPrefixes, common{p}) } sort.Slice(result.CommonPrefixes, func(i, j int) bool { return result.CommonPrefixes[i].Prefix < result.CommonPrefixes[j].Prefix }) result.KeyCount = len(result.Contents) + len(result.CommonPrefixes) if result.KeyCount > result.MaxKeys { http.Error(w, "fixture listing exceeds one page", 500) return } w.Header().Set("Content-Type", "application/xml") xml.NewEncoder(w).Encode(result) }) }