package objstore import ( "bytes" "context" "encoding/json" "testing" "time" "github.com/carlosframework/platform/internal/release" ) // A seed's mark (Declared.Seed) survives only on a store its own seed // granted. Any other first grant of a marked record clears the mark in the // same write as the grant block, so a seed's finish can never tear down a // store another path made (seeded-instances spec ยง4 Storage). func TestFirstGrantClearsAnotherSeedsMark(t *testing.T) { const seedA, seedB = "sd_aaaaaaaaaaaaaaaaaaaaaaaaaa", "sd_bbbbbbbbbbbbbbbbbbbbbbbbbb" for name, c := range map[string]struct { mark, grantFor, want string }{ "an unseeded grant": {seedB, "", ""}, "another seed's grant": {seedB, seedA, ""}, "the marking seed's grant": {seedB, seedB, seedB}, } { t.Run(name, func(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) _, pub := testSealKeypair(t) at := time.Now().UTC().Add(-time.Hour) decl := &Declared{EnvPrefix: DefaultEnvPrefix, At: at, By: "member@example.com", Seed: c.mark} if _, err := PutIn(ctx, s, "bab", "shop", "canary", &Record{Declared: decl}, ""); err != nil { t.Fatal(err) } rec, err := GrantInFor(ctx, s, NewMemProvisioner("eu-west-1"), testDeployment(), pub, "bab", "shop", "canary", "op", GrantSourceOperator, "", c.grantFor, time.Now().UTC()) if err != nil { t.Fatal(err) } stored, _, err := GetIn(ctx, s, "bab", "shop", "canary") if err != nil { t.Fatal(err) } for _, r := range []*Record{rec, stored} { if r.Declared.Seed != c.want { t.Fatalf("Declared.Seed = %q, want %q", r.Declared.Seed, c.want) } if r.Declared.By != decl.By || !r.Declared.At.Equal(at) || r.Declared.EnvPrefix != decl.EnvPrefix { t.Fatalf("declaration = %+v, want only the mark changed from %+v", r.Declared, decl) } } }) } } // A re-run over a store its seed already granted keeps the mark only when // it is that seed's own re-run. An unseeded re-run (an operator grant, a // provision, an ordinary create sharing the environment) or another seed's // clears it, so the aborted seed's finish cannot tear down a store someone // else now holds; its bucket leaks instead, the safe direction. func TestGrantRerunClearsAMarkThatIsNotItsOwn(t *testing.T) { const seedA, seedB = "sd_aaaaaaaaaaaaaaaaaaaaaaaaaa", "sd_bbbbbbbbbbbbbbbbbbbbbbbbbb" for name, c := range map[string]struct{ rerunFor, want string }{ "the same seed's re-run": {seedB, seedB}, "an unseeded re-run": {"", ""}, "another seed's re-run": {seedA, ""}, } { t.Run(name, func(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) _, pub := testSealKeypair(t) p := NewMemProvisioner("eu-west-1") decl := &Declared{EnvPrefix: DefaultEnvPrefix, At: time.Now().UTC(), By: "member@example.com", Seed: seedB} if _, err := PutIn(ctx, s, "bab", "shop", "canary", &Record{Declared: decl}, ""); err != nil { t.Fatal(err) } first, err := GrantInFor(ctx, s, p, testDeployment(), pub, "bab", "shop", "canary", "op", GrantSourceOperator, "", seedB, time.Now().UTC()) if err != nil { t.Fatal(err) } rec, err := GrantInFor(ctx, s, p, testDeployment(), pub, "bab", "shop", "canary", "op", GrantSourceOperator, "", c.rerunFor, time.Now().UTC()) if err != nil { t.Fatal(err) } stored, _, err := GetIn(ctx, s, "bab", "shop", "canary") if err != nil { t.Fatal(err) } for _, r := range []*Record{rec, stored} { if r.Declared.Seed != c.want { t.Fatalf("Declared.Seed after the re-run = %q, want %q", r.Declared.Seed, c.want) } if r.Grant == nil || *r.Grant != *first.Grant || r.Provisioned == nil || r.Provisioned.KeyID != first.Provisioned.KeyID { t.Fatalf("re-run changed the estate: grant %+v provisioned %+v, want %+v %+v", r.Grant, r.Provisioned, first.Grant, first.Provisioned) } } }) } } // A record with no mark is granted exactly as before: its declaration // block comes out byte-identical, and no seed key appears. func TestGrantLeavesAnUnmarkedDeclarationAsItWas(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) _, pub := testSealKeypair(t) decl := &Declared{EnvPrefix: "SHOP_FILES", At: time.Now().UTC().Add(-time.Hour), By: "member@example.com"} if _, err := PutIn(ctx, s, "bab", "shop", "canary", &Record{Declared: decl}, ""); err != nil { t.Fatal(err) } before, _ := json.Marshal(decl) if _, err := GrantIn(ctx, s, NewMemProvisioner("eu-west-1"), testDeployment(), pub, "bab", "shop", "canary", "op", GrantSourceOperator, "", time.Now().UTC()); err != nil { t.Fatal(err) } raw, _, err := s.Get(ctx, KeyIn("bab", "shop", "canary")) if err != nil { t.Fatal(err) } var stored struct { Declared json.RawMessage `json:"declared"` } if err := json.Unmarshal(raw, &stored); err != nil { t.Fatal(err) } var compact bytes.Buffer if err := json.Compact(&compact, stored.Declared); err != nil { t.Fatal(err) } if !bytes.Equal(compact.Bytes(), before) { t.Fatalf("declaration after the grant = %s, want %s", compact.Bytes(), before) } if bytes.Contains(raw, []byte(`"seed"`)) { t.Fatalf("record = %s, want no seed key", raw) } }