Store optional requirements beside a plain requires list (hq issue 166, review of #281)
mesh/merge-gate head 79a3396340a7 merged into trunk 454a3db69a1e — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 79a3396340a7 merged into trunk 454a3db69a1e — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/merge-gate head 79a3396340a7 merged into trunk 454a3db69a1e — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 79a3396340a7 merged into trunk 454a3db69a1e — pass: its merge-check.sh passed
mesh/delivery delivered
A controller older than optional requirements reads `requires` as names, and Inventory.Catalogue stops at the first manifest it cannot read: the long form stored inside `requires` would have left a rollback below this controller composing no machine. Stored, `requires` stays plain and the optional names sit in `optional-requires`, a key an older controller sets aside, leaving out only the module carrying it. A refusal of a hard requirement now names a module that wants it hard.
This commit is contained in:
@@ -49,8 +49,56 @@ func TestARequirementIsHardUnlessMarkedOptional(t *testing.T) {
|
||||
if !again.IsOptional("influxdb-api") || again.IsOptional("route") || len(again.Requires) != 3 {
|
||||
t.Fatalf("written and read back, it says %v optional %v (%s)", again.Requires, again.OptionalRequires, raw)
|
||||
}
|
||||
if strings.Contains(string(raw), `"name":"route"`) {
|
||||
t.Errorf("a hard requirement was written in the long form: %s", raw)
|
||||
if strings.Contains(string(raw), `"name":`) {
|
||||
t.Errorf("a requirement was stored in the long form: %s", raw)
|
||||
}
|
||||
}
|
||||
|
||||
// The stored form reads on a controller older than optional requirements (review of mesh-controller
|
||||
// #281). Inventory.Catalogue stops at the first manifest it cannot read, so a long-form entry inside
|
||||
// `requires` would have stopped every plan after a rollback. Stored, `requires` is plain names, as main's
|
||||
// manifest type reads it, and which are optional sits beside it in a key such a controller sets aside.
|
||||
func TestTheStoredFormReadsWhereRequiresIsAListOfNames(t *testing.T) {
|
||||
m := mustParse(t, `{"module":"grafana","version":"1",
|
||||
"requires":["route",{"name":"influxdb-api","optional":true}]}`)
|
||||
raw, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// main's field, exactly: `Requires []string`.
|
||||
var older struct {
|
||||
Requires []string `json:"requires"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &older); err != nil {
|
||||
t.Fatalf("a controller reading requires as names cannot read the stored form: %v\n%s", err, raw)
|
||||
}
|
||||
if strings.Join(older.Requires, " ") != "route influxdb-api" {
|
||||
t.Fatalf("it reads the requirements as %v", older.Requires)
|
||||
}
|
||||
var keys map[string]json.RawMessage
|
||||
if err := json.Unmarshal(raw, &keys); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(keys[OptionalRequiresKey]) != `["influxdb-api"]` {
|
||||
t.Fatalf("which are optional is stored as %s", keys[OptionalRequiresKey])
|
||||
}
|
||||
// And this controller reads the stored form back the same, leniently and strictly.
|
||||
var back Manifest
|
||||
if err := json.Unmarshal(raw, &back); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
strict, err := ParseManifest(raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, got := range []Manifest{back, strict} {
|
||||
if !got.IsOptional("influxdb-api") || got.IsOptional("route") || len(got.Requires) != 2 {
|
||||
t.Fatalf("read back as %v optional %v", got.Requires, got.OptionalRequires)
|
||||
}
|
||||
}
|
||||
// A stored list naming something the module does not require is refused.
|
||||
if _, err := ParseManifest([]byte(`{"module":"x","version":"1","requires":["route"],"optional-requires":["mqtt-topic"]}`)); err == nil {
|
||||
t.Error("an optional requirement the module does not require passed the check")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +168,10 @@ func TestOneConsumersOptionalRequirementIsAnothersHardOne(t *testing.T) {
|
||||
if err == nil || !strings.Contains(err.Error(), "assign influxdb") {
|
||||
t.Fatalf("a hard requirement with no provider was not refused naming the candidate: %v", err)
|
||||
}
|
||||
// And the refusal names telegraf, whose remedy it is, not grafana, which met the name first.
|
||||
if !strings.Contains(err.Error(), "wanted by telegraf") {
|
||||
t.Errorf("the refusal does not name the module that wants it hard: %v", err)
|
||||
}
|
||||
// And a module pulled in later (suite → editor) that wants the shell hard installs it, even
|
||||
// though grafana, met first, wanted it only optionally — and grafana is then bound to it.
|
||||
got, err := Resolve(optionalShelf(), []string{"grafana", "suite"}, workstation(), World{})
|
||||
|
||||
Reference in New Issue
Block a user