| 1 | package hostactors |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "github.com/carlosframework/platform/internal/hostfloor" |
| 7 | "github.com/carlosframework/platform/internal/release" |
| 8 | "strings" |
| 9 | "testing" |
| 10 | "time" |
| 11 | ) |
| 12 | |
| 13 | type reportsFixture struct { |
| 14 | d Directory |
| 15 | box, console Report |
| 16 | wire []byte |
| 17 | } |
| 18 | |
| 19 | func (e reportsFixture) BoxReport(context.Context, string, Box) (Report, error) { return e.box, nil } |
| 20 | func (e reportsFixture) ConsoleReport(context.Context, string, Console) (Report, error) { |
| 21 | return e.console, nil |
| 22 | } |
| 23 | func (e reportsFixture) LocalConsole(context.Context) (Report, error) { return e.console, nil } |
| 24 | func (e reportsFixture) Identity(context.Context, string) ([]byte, error) { return e.wire, nil } |
| 25 | func (e reportsFixture) Fence(ctx context.Context, o Observed, ref string, now time.Time) (bool, error) { |
| 26 | return e.d.TerminationFence(ctx, o, ref, now) |
| 27 | } |
| 28 | func censusFixture(t *testing.T) (Directory, Observed, reportsFixture, hostfloor.Verified, Scope, time.Time) { |
| 29 | t.Helper() |
| 30 | ctx := context.Background() |
| 31 | d, sg, o := fixture(t) |
| 32 | now := time.Now().UTC() |
| 33 | tag := "v1.2.3" |
| 34 | hash := strings.Repeat("a", 64) |
| 35 | rel := release.SystemRelease{ConsoleArtifacts: map[string]release.SystemArtifact{"linux-amd64": {Key: "bootstrap/console/build123/carlos-linux-amd64", SHA256: hash}}, Version: "build123", ReleaseTag: tag, MovesProtocol: 1, Artifacts: map[string]release.SystemArtifact{"linux-amd64": {Key: release.SystemArtifactKey("build123", "linux-amd64"), SHA256: hash}}} |
| 36 | if _, err := release.PromoteSystem(ctx, d.Store, sg, "router", "stable", rel, release.SystemPromoteOptions{}); err != nil { |
| 37 | t.Fatal(err) |
| 38 | } |
| 39 | wire, _, err := d.Store.Get(ctx, release.SystemPointerKey("router", "stable")) |
| 40 | if err != nil { |
| 41 | t.Fatal(err) |
| 42 | } |
| 43 | key, err := release.IdentityKey(tag) |
| 44 | if err != nil { |
| 45 | t.Fatal(err) |
| 46 | } |
| 47 | if err := release.CreateExact(ctx, d.Store, key, wire); err != nil { |
| 48 | t.Fatal(err) |
| 49 | } |
| 50 | floor := hostfloor.Floor{Min: 1, MinRelease: tag} |
| 51 | raw, _ := json.Marshal(floor) |
| 52 | f := hostfloor.Verified{Floor: floor, Digest: hostfloor.Digest(raw)} |
| 53 | o.Roster.Regions = map[string]Region{"ie": {Account: "123", AWSRegion: "eu-west-1", Namespaces: []string{"platform"}, Pools: []string{"default"}}} |
| 54 | o.Roster.Publication = "published" |
| 55 | o.Roster.FloorSHA256 = f.Digest |
| 56 | c := Console{Enrollment: strings.Repeat("c", 32), InstanceID: "console-1", Namespaces: []string{"platform"}, DeploymentBucket: d.Bucket} |
| 57 | o.Roster.Consoles["console-1"] = c |
| 58 | box := Report{Component: "router", GeneratedAt: now, Actor: "box", Enrollment: o.Roster.Boxes["box"].Enrollment, InstanceID: "i-box", Release: "build123", ReleaseTag: tag, Platform: "linux-amd64", BinarySHA256: hash, MovesProtocol: 1, BootID: "boot", FloorSHA256: f.Digest, FloorGuardReady: true, HostClaimEnabled: true} |
| 59 | console := box |
| 60 | console.Component = "console" |
| 61 | console.Actor = "console-1" |
| 62 | console.Enrollment = c.Enrollment |
| 63 | console.InstanceID = "console-1" |
| 64 | console.ProcessID = "process-1" |
| 65 | return d, o, reportsFixture{d: d, box: box, console: console, wire: wire}, f, Scope{Namespace: "platform", Regions: []string{"ie"}, Pools: []string{"default"}, ConsoleID: "console-1"}, now |
| 66 | } |
| 67 | func TestCensusRejectsMissingStaleAndWrongImages(t *testing.T) { |
| 68 | for _, field := range []string{"good", "old-box", "old-console", "future", "image", "floor", "flag", "enrollment", "scope", "unknown-participant", "unknown-region", "unknown-pool", "unknown-namespace"} { |
| 69 | t.Run(field, func(t *testing.T) { |
| 70 | d, o, e, f, scope, now := censusFixture(t) |
| 71 | switch field { |
| 72 | case "old-box": |
| 73 | e.box.GeneratedAt = now.Add(-16 * time.Minute) |
| 74 | case "old-console": |
| 75 | e.console.GeneratedAt = now.Add(-16 * time.Minute) |
| 76 | case "future": |
| 77 | e.box.GeneratedAt = now.Add(6 * time.Second) |
| 78 | case "image": |
| 79 | e.box.BinarySHA256 = strings.Repeat("b", 64) |
| 80 | case "floor": |
| 81 | e.console.FloorSHA256 = "" |
| 82 | case "flag": |
| 83 | e.box.HostClaimEnabled = false |
| 84 | case "enrollment": |
| 85 | e.box.Enrollment = "another" |
| 86 | case "scope": |
| 87 | scope.Regions = nil |
| 88 | case "unknown-region": |
| 89 | scope.Regions = []string{"nowhere"} |
| 90 | case "unknown-pool": |
| 91 | scope.Pools = []string{"nowhere"} |
| 92 | case "unknown-namespace": |
| 93 | scope.Namespace = "unknown" |
| 94 | case "unknown-participant": |
| 95 | scope.Participants = []string{"unknown"} |
| 96 | } |
| 97 | _, err := Census(context.Background(), o, e, d.PublicKey, f, scope, now) |
| 98 | if (err == nil) != (field == "good") { |
| 99 | t.Fatal(field, err) |
| 100 | } |
| 101 | }) |
| 102 | } |
| 103 | } |
| 104 | |