Test the controller's seal under a silent vault, the bus machine's refusal and a served fact that says a setting (issues 1385, 1383, 1382)
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/compose"
|
||||
"github.com/novox/mesh-controller/internal/broker"
|
||||
)
|
||||
|
||||
// aCoreInput is an Input of one machine that takes a core module whose credential the vault has not boxed: the composition
|
||||
// stands in a vault box for it, and the machine holds the controller's own seal of it.
|
||||
func aCoreInput(t *testing.T) (*meshInput, string) {
|
||||
t.Helper()
|
||||
raw := make([]byte, 32)
|
||||
raw[0] = 9
|
||||
key := base64.StdEncoding.EncodeToString(raw)
|
||||
client := catalogue.Manifest{Module: "mesh-provisioner-client", Version: "1",
|
||||
Claims: []catalogue.Claim{{Name: broker.NodeProvisionerSeat, Scope: catalogue.ScopeNode}},
|
||||
OwnSecrets: catalogue.OwnSecrets{broker.ProvisionerSecret: {Path: "/var/lib/mesh-provisioner-client/broker"}}}
|
||||
return &meshInput{Input: compose.Input{
|
||||
Catalogue: map[string]catalogue.Manifest{client.Module: client},
|
||||
Nodes: []compose.Node{{Name: "laptop", Assigned: []string{client.Module}, SealingKey: key, Stated: true,
|
||||
Contract: compose.VaultBoxContract}},
|
||||
OwnSecrets: []compose.OwnSecretRow{{Node: "laptop", Module: client.Module, Name: broker.ProvisionerSecret,
|
||||
Origin: "made", SealedTo: key, Sealed: "SEALED-BY-THE-CONTROLLER"}},
|
||||
}}, client.Module
|
||||
}
|
||||
|
||||
// 1385 (the lead's answer 1): where the vault is silent and no box is kept, the declaration carries the controller's own seal
|
||||
// for the core credential, as ADR 0396 has it, and stands in for nothing. It failed first: the composition's declaration
|
||||
// carried the stand-in box, and a send was refused.
|
||||
func TestACoreCredentialTheVaultDidNotBoxIsSealedByTheController(t *testing.T) {
|
||||
in, module := aCoreInput(t)
|
||||
d, _, err := compose.ComposeIn(in.Input, "laptop", compose.Foreseeing)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(d.CoreBoxesWanted) != 1 || len(stillForeseen(d)) == 0 {
|
||||
t.Fatalf("the composition does not stand in for the vault's box: %v", d.CoreBoxesWanted)
|
||||
}
|
||||
sealed, _, err := sealedByTheController(in, "laptop", d, []string{module})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if left := stillForeseen(sealed); len(left) != 0 {
|
||||
t.Fatalf("the declaration still stands in for %v", left)
|
||||
}
|
||||
var file map[string]any
|
||||
for _, r := range sealed.Resources {
|
||||
if r["id"] == module+"."+catalogue.NeedID(broker.ProvisionerSecret) {
|
||||
file = r
|
||||
}
|
||||
}
|
||||
if file == nil || file["sealed"] != "SEALED-BY-THE-CONTROLLER" || file[catalogue.VaultSealedField] != nil {
|
||||
t.Fatalf("the core credential is carried as %v: the controller's own seal, not the vault's box", file)
|
||||
}
|
||||
// And a module the vault did answer for is left as it is.
|
||||
other, _, err := sealedByTheController(in, "laptop", d, []string{"another-module"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(stillForeseen(other)) == 0 {
|
||||
t.Fatal("a module the vault answered for was sealed by the controller")
|
||||
}
|
||||
}
|
||||
|
||||
// 1383 (the lead's answer 4): the machine that runs the bus is refused where no user has a credential, with busUserList's
|
||||
// words, and a machine that does not run the bus is not. It failed first: the Input stood a list in and the bus node composed.
|
||||
func TestTheMachineThatRunsTheBusIsRefusedWhereNoUserHasACredential(t *testing.T) {
|
||||
bus := catalogue.Manifest{Module: "nats", Version: "1", BusUsers: "/etc/nats/users.conf",
|
||||
Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh}}}
|
||||
other := catalogue.Manifest{Module: "files", Version: "1"}
|
||||
why := errors.New("this machine runs the bus and not one user has a credential, so the composed list would refuse every connection in the mesh")
|
||||
in := &meshInput{busRefused: why, Input: compose.Input{
|
||||
Catalogue: map[string]catalogue.Manifest{"nats": bus, "files": other},
|
||||
Nodes: []compose.Node{{Name: "anchor", Assigned: []string{"nats"}}, {Name: "laptop", Assigned: []string{"files"}}},
|
||||
}}
|
||||
if err := refusedBus(in, "anchor"); err == nil || !strings.Contains(err.Error(), "not one user has a credential") {
|
||||
t.Fatalf("the machine that runs the bus is not refused: %v", err)
|
||||
}
|
||||
if err := refusedBus(in, "laptop"); err != nil {
|
||||
t.Fatalf("a machine that does not run the bus is refused: %v", err)
|
||||
}
|
||||
if err := refusedBus(&meshInput{Input: in.Input}, "anchor"); err != nil {
|
||||
t.Fatalf("a bus with users is refused: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -328,3 +328,27 @@ func TestTheAdaptersInputCarriesTheBuildsAndTheMachinesKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The lead's answer 4: where the mesh holds no credential for any bus user, the machine that runs the bus is refused with
|
||||
// busUserList's words (the controller's composition refused it), the Input still builds, and another machine composes.
|
||||
func TestTheBusMachineIsRefusedWhereNoUserHasACredentialAndTheOthersCompose(t *testing.T) {
|
||||
open, _ := aMeshWithSecrets(t)
|
||||
ctx := t.Context()
|
||||
bus := catalogue.Manifest{Module: "nats", Version: "1", BusUsers: "/etc/nats/users.conf",
|
||||
Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh}}}
|
||||
register(t, open, bus)
|
||||
register(t, open, aWebModule())
|
||||
if _, err := assign(ctx, open, "anchor", "nats"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := assign(ctx, open, "laptop", "web"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := composeQuestion(ctx, open, "anchor", compose.Foreseeing, false); err == nil ||
|
||||
!strings.Contains(err.Error(), "not one user has a credential") {
|
||||
t.Fatalf("the bus machine is composed: %v", err)
|
||||
}
|
||||
if _, _, err := composeQuestion(ctx, open, "laptop", compose.Foreseeing, false); err != nil {
|
||||
t.Fatalf("a machine that does not run the bus is refused: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,3 +249,70 @@ func TestTheAdaptersInputCarriesTheNetworkAsTheInventoryHoldsIt(t *testing.T) {
|
||||
t.Error("the range moved with the environment")
|
||||
}
|
||||
}
|
||||
|
||||
// The lead's equality finding (build 1792487777757012513): a provider on one machine serves a fact that says ${setting:domain},
|
||||
// the setting is set for it, and a consumer on another machine takes that fact. The first pass of the adapter resolved every
|
||||
// node with no settings in the Input, so the served fact could not be settled and the whole Input failed. The settings are in
|
||||
// the Input before any node is resolved, and the Input builds, with the same plan for the consumer as the controller's own
|
||||
// composition. It failed first, with the finding's words.
|
||||
func TestAServedFactThatSaysASettingIsResolvedWithTheSettingInTheInput(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
register(t, open, catalogue.Manifest{Module: "step-ca", Version: "1",
|
||||
Provides: []catalogue.Offer{{Name: "acme-ca", Scope: catalogue.ScopeMesh}},
|
||||
Serves: map[string]map[string]any{"acme-ca": {"domain": "${setting:domain}"}}})
|
||||
register(t, open, catalogue.Manifest{Module: "route-proxy", Version: "1", Requires: []string{"acme-ca"}})
|
||||
if _, err := assign(ctx, open, "anchor", "step-ca"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := assign(ctx, open, "laptop", "route-proxy"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := open.inventory.SetSettings(ctx, "", "step-ca", map[string]any{"domain": "example.test"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
in, err := readComposeInput(ctx, open)
|
||||
if err != nil {
|
||||
t.Fatalf("the Input cannot be built: %v", err)
|
||||
}
|
||||
if anchor, _ := nodeOf(in.Input, "anchor"); len(anchor.Settings["step-ca"]) == 0 {
|
||||
t.Fatalf("the Input does not carry the setting of the provider: %v", anchor.Settings)
|
||||
}
|
||||
oldPlan, _, err := planFor(ctx, open, "laptop")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newPlan, _, err := resolveNode(withInputCache(ctx), open, "laptop")
|
||||
if err != nil {
|
||||
t.Fatalf("the consumer does not resolve: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(oldPlan.Needs, newPlan.Needs) {
|
||||
t.Errorf("the consumer's needs are %+v, the controller's own %+v", newPlan.Needs, oldPlan.Needs)
|
||||
}
|
||||
}
|
||||
|
||||
// A setting that nothing sets, in a fact a provider serves, is not the adapter's to hide: the controller's own composition
|
||||
// refuses it as well (theRestOfTheMesh returns servedOnNode's error), and the adapter refuses with the same words rather than
|
||||
// building an Input that composes a placeholder.
|
||||
func TestAServedFactThatSaysAnUnsetSettingIsRefusedAsTheControllerRefusesIt(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
register(t, open, catalogue.Manifest{Module: "step-ca", Version: "1",
|
||||
Provides: []catalogue.Offer{{Name: "acme-ca", Scope: catalogue.ScopeMesh}},
|
||||
Serves: map[string]map[string]any{"acme-ca": {"domain": "${setting:domain}"}}})
|
||||
register(t, open, catalogue.Manifest{Module: "route-proxy", Version: "1", Requires: []string{"acme-ca"}})
|
||||
if _, err := assign(ctx, open, "anchor", "step-ca"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := assign(ctx, open, "laptop", "route-proxy"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _, oldErr := planFor(ctx, open, "laptop")
|
||||
_, newErr := readComposeInput(ctx, open)
|
||||
if (oldErr == nil) != (newErr == nil) {
|
||||
t.Fatalf("the controller's composition says %v, the adapter says %v", oldErr, newErr)
|
||||
}
|
||||
if oldErr != nil && !strings.Contains(newErr.Error(), "nothing sets \"domain\"") {
|
||||
t.Errorf("the adapter's words are %q, the controller's %q", newErr, oldErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,17 +146,31 @@ func TestPerformMakesTheOwnSecretsAndThePairCredentialsTheCompositionNamed(t *te
|
||||
}
|
||||
}
|
||||
|
||||
// 1385/AC3: with the vault unreachable and no box of the credential kept, the send fails naming the module, and composes
|
||||
// nothing to send.
|
||||
func TestAVaultThatIsSilentRefusesTheSendWhereNoBoxIsKept(t *testing.T) {
|
||||
// 1385/AC3 (the lead's answer 1): with the vault unreachable and no box of the credential kept, the send carries the
|
||||
// controller's own seal for it (ADR 0396), says so as a condition, and keeps no box.
|
||||
func TestAVaultThatIsSilentSealsTheSendByTheControllerWhereNoBoxIsKept(t *testing.T) {
|
||||
open, vault := aCoreMesh(t, link.VaultBoxContract)
|
||||
vault.silent = errors.New("no responders")
|
||||
declared, err := composeSend(t.Context(), open, "laptop")
|
||||
if err == nil || !strings.Contains(err.Error(), "mesh-provisioner-client") {
|
||||
t.Fatalf("the send was not refused, naming the module: %v", err)
|
||||
if err != nil {
|
||||
t.Fatalf("the send is refused: %v", err)
|
||||
}
|
||||
if len(declared.Resources) != 0 {
|
||||
t.Fatal("a declaration came with the refusal")
|
||||
f := clientsBroker(declared.Resources)
|
||||
if f == nil || f["sealed"] == nil || f[catalogue.VaultSealedField] != nil {
|
||||
t.Fatalf("the core credential is composed as %v: the controller's own seal, until the vault holds it", f)
|
||||
}
|
||||
if len(declared.foreseen) != 0 {
|
||||
t.Fatalf("the declaration still names %v foreseen", declared.foreseen)
|
||||
}
|
||||
if c, found := vaultSilence(t, "laptop"); !found || !strings.Contains(c.Summary, "sealed by the controller until the vault holds it") {
|
||||
t.Fatalf("no condition says why: %+v", c)
|
||||
}
|
||||
kept, err := open.inventory.KeptCoreBoxes(t.Context(), "laptop")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(kept["mesh-provisioner-client"]) != 0 {
|
||||
t.Fatalf("a box was kept for a credential the vault never boxed: %v", kept)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user