package main import ( "strings" "testing" "time" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // A provider whose wait fails the controller's check lists who waits on it (novox/hq issue 450). // // A wait that does not check out is judged unhealthy (ADR 0283 decision 3), so the provider raises its unhealthy // condition and its consumers are held under it (ADR 0240 rule 5). What is stored is what the machine said, the // wait unchecked: read as said, the provider seems to wait for the operator, and the held consumers were never // listed on the condition that is open. // refusedWait is a wait for a secret the database's manifest does not declare: it fails the check. var refusedWait = inventory.Wait{Part: "postgres-database", Secret: "certificate", What: "the database's certificate"} // judgedRefused is the provider's resource as the controller judges it: unhealthy, saying why. func judgedRefused() inventory.ResourceHealth { r := waitingDatabaseFor(refusedWait) r.State, r.Waits = link.StateUnhealthy, nil r.Reason = "says it waits for the secret certificate, which db does not declare" return r } // The consumer's statement arrives after the provider's, so it is the consumer's judging (sayWaiters) that must list // it at the provider's unhealthy condition, made urgent by who waits on it. func TestAProviderWhoseWaitFailedItsCheckListsWhoWaitsOnIt(t *testing.T) { k, _ := withConditionsInMemory(t) stored := waitingDatabaseFor(refusedWait) open := judgeBoth(t, k, []inventory.ResourceHealth{judgedRefused()}, map[string]inventory.NodeHealth{"anchor": {Node: "anchor", Resources: []inventory.ResourceHealth{stored}}}, shopFailingBeside(stored)) provider := conditionOf(open, moduleUnhealthyKey("db", "anchor")) if len(open) != 1 || provider == nil { t.Fatalf("a provider whose wait failed its check and a consumer of it raised %v; want the provider's "+ "unhealthy alone", openKeysOf(open)) } if said := provider.Evidence[0].Said; !strings.Contains(said, "shop on laptop") { t.Fatalf("the provider's unhealthy condition does not list shop on laptop as waiting on it: %s", said) } if provider.Severity != conditions.Urgent { t.Fatalf("the provider's unhealthy condition is %s with a consumer waiting on it; want urgent", provider.Severity) } } // A provider that waits for a secret already given is unhealthy to its consumers too, before its own condition opens: // they are held under it as under any unhealthy provider, never as waiting for the operator, and its condition, when // open, is said as unhealthy with who waits on it. func TestAProviderWaitingForASecretAlreadyGivenIsUnhealthyToItsConsumers(t *testing.T) { given := holdingOf(nil, shopFailingBeside(waitingDatabase()), shopOnTheDatabase) given.waits.given["db@anchor"] = map[string]time.Time{"licence": time.Now().Add(-time.Hour)} by, held := given.heldWith("laptop", "shop", []inventory.ResourceHealth{failingConsumer("shop")}) if !held || by.provider != theDatabase || len(by.waits) > 0 { t.Fatalf("shop under a database waiting for a licence already given: held %v under %v with waits %v; want "+ "held under db on anchor as unhealthy, no waits", held, by.provider, by.waits) } if _, uncovered := given.waitingUncovered("laptop", "shop", []inventory.ResourceHealth{failingConsumer("shop")}); uncovered { t.Fatalf("a provider whose wait failed its check is said as waiting for another part") } }