| 1 | package hostfloor |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "crypto/ed25519" |
| 6 | "crypto/rand" |
| 7 | "encoding/json" |
| 8 | "github.com/carlosframework/platform/internal/release" |
| 9 | "os" |
| 10 | "path/filepath" |
| 11 | "testing" |
| 12 | ) |
| 13 | |
| 14 | func TestFloorPairAndPermanentArming(t *testing.T) { |
| 15 | pub, key, _ := ed25519.GenerateKey(rand.Reader) |
| 16 | sg := release.RootSigner(key) |
| 17 | ctx := context.Background() |
| 18 | s := release.NewDirStore(t.TempDir()) |
| 19 | floor := Floor{1, "v1.2.3"} |
| 20 | raw, _ := json.Marshal(floor) |
| 21 | auth, _ := release.SignRootEnvelope(sg, Authorization{1, "moves-protocol", "bucket", Key, Digest(raw)}) |
| 22 | if _, err := Verify(pub, "other", raw, auth); err == nil { |
| 23 | t.Fatal("wrong deployment") |
| 24 | } |
| 25 | if _, err := Verify(pub, "bucket", append(raw, ' '), auth); err == nil { |
| 26 | t.Fatal("changed bytes") |
| 27 | } |
| 28 | if _, err := s.Put(ctx, AuthKey, auth, ""); err != nil { |
| 29 | t.Fatal(err) |
| 30 | } |
| 31 | if _, err := Read(ctx, s, pub, "bucket"); err == nil { |
| 32 | t.Fatal("incomplete pair accepted") |
| 33 | } |
| 34 | if err := Publish(ctx, s, sg, "bucket", floor); err != nil { |
| 35 | t.Fatal(err) |
| 36 | } |
| 37 | d := Disk{Dir: t.TempDir(), Bucket: "bucket", PublicKey: pub} |
| 38 | _ = os.Mkdir(filepath.Join(d.Dir, "identities"), 0700) |
| 39 | initial, _ := json.Marshal(Policy{Schema: 1, DeploymentBucket: "bucket", Mode: "unpublished"}) |
| 40 | _ = os.WriteFile(filepath.Join(d.Dir, "policy.json"), initial, 0600) |
| 41 | armed := Policy{1, "bucket", "armed", raw, auth} |
| 42 | if err := d.Persist(armed, nil); err != nil { |
| 43 | t.Fatal(err) |
| 44 | } |
| 45 | if err := d.Persist(Policy{Schema: 1, DeploymentBucket: "bucket", Mode: "unpublished"}, nil); err == nil { |
| 46 | t.Fatal("floor lowered") |
| 47 | } |
| 48 | _ = os.Remove(filepath.Join(d.Dir, "policy.json")) |
| 49 | if _, _, err := d.Load(); err == nil { |
| 50 | t.Fatal("missing is unpublished") |
| 51 | } |
| 52 | } |
| 53 | func TestPendingAndLostControllersBlockUpdate(t *testing.T) { |
| 54 | for _, state := range []string{"pending", "active", "lost", "broken"} { |
| 55 | t.Run(state, func(t *testing.T) { |
| 56 | d := Disk{Dir: t.TempDir(), Participation: floorParticipationFixture(t)} |
| 57 | data := t.TempDir() |
| 58 | _ = os.Mkdir(filepath.Join(data, ".hostexec"), 0700) |
| 59 | _ = os.WriteFile(filepath.Join(data, ".hostexec", "host.json"), []byte(state), 0600) |
| 60 | if err := d.Begin(t.Context(), "update", data); err == nil { |
| 61 | t.Fatal("controller did not block") |
| 62 | } |
| 63 | if _, err := os.Stat(filepath.Join(d.Dir, "update-begun.json")); !os.IsNotExist(err) { |
| 64 | t.Fatal("marker survived refusal", err) |
| 65 | } |
| 66 | }) |
| 67 | } |
| 68 | d := Disk{Dir: t.TempDir(), Participation: floorParticipationFixture(t)} |
| 69 | data := t.TempDir() |
| 70 | _ = os.Mkdir(filepath.Join(data, ".hostexec"), 0700) |
| 71 | if err := d.Begin(t.Context(), "update", data); err != nil { |
| 72 | t.Fatal(err) |
| 73 | } |
| 74 | if err := UpdateIdle(d.Dir, filepath.Join(data, "journal.json")); err == nil { |
| 75 | t.Fatal("participant ignored begun update") |
| 76 | } |
| 77 | } |
| 78 | func TestCanonicalReleaseTags(t *testing.T) { |
| 79 | for _, tag := range []string{"dev", "abc123", "1.2.3", "vv1.2.3", "v01.2.3", "v1.2.3-rc1"} { |
| 80 | if _, ok := release.ReleaseTag(tag); ok { |
| 81 | t.Fatal(tag) |
| 82 | } |
| 83 | } |
| 84 | a, _ := release.ReleaseTag("v1.10.0") |
| 85 | b, _ := release.ReleaseTag("v1.9.9") |
| 86 | if a.Compare(b) <= 0 { |
| 87 | t.Fatal("lexical ordering") |
| 88 | } |
| 89 | } |
| 90 | |