| 1 | package park |
| 2 | |
| 3 | import ( |
| 4 | "reflect" |
| 5 | "strings" |
| 6 | "testing" |
| 7 | |
| 8 | "github.com/carlosframework/platform/internal/hostclaim" |
| 9 | ) |
| 10 | |
| 11 | func TestInstanceParkPreservesStaging(t *testing.T) { |
| 12 | for _, legacy := range []bool{true, false} { |
| 13 | name := "incarnation" |
| 14 | if legacy { |
| 15 | name = "legacy" |
| 16 | } |
| 17 | t.Run(name, func(t *testing.T) { |
| 18 | f := newFixture(t) |
| 19 | if legacy { |
| 20 | f.ob.Park.Location = "instances/" + f.w.Host |
| 21 | } |
| 22 | obs, err := hostclaim.Read(t.Context(), f.e.Claims, f.w.Host) |
| 23 | if err != nil { |
| 24 | t.Fatal(err) |
| 25 | } |
| 26 | _, err = hostclaim.Update(t.Context(), f.e.Claims, obs, func(c *hostclaim.Claim) error { |
| 27 | c.Holder.Location = f.ob.Park.Location |
| 28 | c.Op = hostclaim.Op{Kind: hostclaim.KindLanding, ID: "move-1", Phase: "parking", Exec: "target"} |
| 29 | c.Obligations = []hostclaim.Obligation{f.ob} |
| 30 | return nil |
| 31 | }) |
| 32 | if err != nil { |
| 33 | t.Fatal(err) |
| 34 | } |
| 35 | source := f.seed() |
| 36 | for _, op := range []string{"move-1", f.ob.Park.Incarnation} { |
| 37 | p := "moves/" + f.w.Host + "/" + op + "/" |
| 38 | for _, key := range []string{"replica/0000/data", "seal.json"} { |
| 39 | f.b.Put("data", p+key, []byte("old")) |
| 40 | f.b.Put("data", p+key, []byte("sealed")) |
| 41 | } |
| 42 | f.b.BareDelete("data", p+"replica/0000/data") |
| 43 | } |
| 44 | child := "instances/" + f.w.Host + "/" + strings.Repeat("b", 64) + "/0000/data" |
| 45 | childID := f.b.Put("data", child, []byte("other incarnation")) |
| 46 | staging, err := f.b.ListVersions(t.Context(), "data", "moves/") |
| 47 | if err != nil { |
| 48 | t.Fatal(err) |
| 49 | } |
| 50 | f.b.FailOn = func(action, key string) error { |
| 51 | if action == "list" && !strings.HasPrefix(key, "parked/") && key != f.ob.Park.Location+"/" { |
| 52 | t.Errorf("instance park listed %q", key) |
| 53 | return ErrUnsafe |
| 54 | } |
| 55 | return nil |
| 56 | } |
| 57 | if err := f.e.Run(t.Context(), f.w); err != nil { |
| 58 | t.Fatal(err) |
| 59 | } |
| 60 | f.done(t) |
| 61 | m, err := f.e.manifest(t.Context(), f.w, f.ob) |
| 62 | if err != nil || m == nil || !reflect.DeepEqual(m.Delete, source) { |
| 63 | t.Fatalf("manifest = %+v, error = %v", m, err) |
| 64 | } |
| 65 | f.b.FailOn = nil |
| 66 | after, err := f.b.ListVersions(t.Context(), "data", "moves/") |
| 67 | if err != nil || !reflect.DeepEqual(after, staging) { |
| 68 | t.Fatalf("staging changed: %+v, error = %v", after, err) |
| 69 | } |
| 70 | if body, err := f.b.GetVersion(t.Context(), "data", child, childID); err != nil || string(body) != "other incarnation" { |
| 71 | t.Fatalf("child incarnation changed: %q, error = %v", body, err) |
| 72 | } |
| 73 | }) |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | func TestInstanceScopeExcludesMovesKeys(t *testing.T) { |
| 78 | f := newFixture(t) |
| 79 | for _, location := range []string{"instances/" + f.w.Host, f.ob.Park.Location} { |
| 80 | for _, op := range []string{"move-1", f.ob.Park.Incarnation} { |
| 81 | key := "moves/" + f.w.Host + "/" + op + "/replica/0000/data" |
| 82 | if inScope, _ := scopeContains(f.w.Host, location, key); inScope { |
| 83 | t.Errorf("%q includes %q", location, key) |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |