mesh/merge-gate head c9dd07f919a2 merged into trunk 404b4409e70e — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head c9dd07f919a2 merged into trunk 404b4409e70e — pass: its merge-check.sh passed (run 2)
mesh/delivery delivered
A Servarr app's API key, an indexer's key and a downloader's password are values the provider's
software holds; the pair path made a random value for every consumer of them, sealed it to both ends
and reported success, and the provider refused it. An offer may now say `"credential": {"issued-by":
"outside"}`: SecretFor never makes a value for it, the plan reads such a pair as given or not given
and sends the consumer no credential file until a person gives it, without refusing its machine;
remakeShared and SharedSecretFor never make a provider's own secret issued outside either.
`secret accept <provider's node> <provider module> <provision>` seals one value to every consumer that
binds the provision from that provider, the provider and the operator, under one generation; the
per-pair form is refused for such a provision, and rotate names the accept. A consumer may wait for a
provision's credential: the controller excuses the wait only when the store bears it out, and its
needs-operator condition names the one accept line.
Adds the replay of issue 164 (TestReplay164), written with only what the controller had before.
84 lines
3.4 KiB
Go
84 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// One accept for a provider reaches every consumer that binds the provision from it, and each consumer's send
|
|
// places it; the per-pair form naming a consumer is refused for such a provision, naming the provider's form; a
|
|
// value the mesh made before the rule is not placed (novox/hq ADR 0337 §2, §3, §4, issue 164, issue 165). Needs the
|
|
// store (MESH_TEST_POSTGRES), and is named in testdata/store-tests.
|
|
func TestOneAcceptForAProviderIsPlacedForEveryConsumer(t *testing.T) {
|
|
open := aMesh(t)
|
|
ctx := t.Context()
|
|
// Before the rule: tv's offer said nothing of its credential, and the send made one per pair.
|
|
register(t, open, catalogue.Manifest{Module: "tv", Version: "1",
|
|
Provides: []catalogue.Offer{{Name: "tv-api", Scope: catalogue.ScopeMesh}}})
|
|
for _, m := range []string{"requests", "indexer"} {
|
|
register(t, open, catalogue.Manifest{Module: m, Version: "1", Requires: []string{"tv-api"},
|
|
Secrets: map[string]string{"tv-api": "/var/lib/mesh/" + m + "/tv-api.secret"}})
|
|
}
|
|
for _, a := range [][2]string{{"anchor", "tv"}, {"laptop", "requests"}, {"laptop", "indexer"}} {
|
|
if _, err := assign(ctx, open, a[0], a[1]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
placed := func(module string) bool {
|
|
t.Helper()
|
|
plan, settings, err := planFor(ctx, open, "laptop")
|
|
if err != nil {
|
|
t.Fatalf("laptop was refused: %v", err)
|
|
}
|
|
declared, err := declarationFor(ctx, open, "laptop", plan, settings)
|
|
if err != nil {
|
|
t.Fatalf("laptop did not compose: %v", err)
|
|
}
|
|
for _, r := range declared.Resources {
|
|
if r["path"] == "/var/lib/mesh/"+module+"/tv-api.secret" && r["sealed"] != nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
if !placed("requests") || !placed("indexer") {
|
|
t.Fatal("before the rule, the send made and placed a credential per pair")
|
|
}
|
|
// The offer now says only a person gives it: the values the mesh made are not placed.
|
|
register(t, open, catalogue.Manifest{Module: "tv", Version: "2",
|
|
Provides: []catalogue.Offer{{Name: "tv-api", Scope: catalogue.ScopeMesh,
|
|
Credential: &catalogue.OfferCredential{IssuedBy: catalogue.IssuedOutside}}}})
|
|
if placed("requests") || placed("indexer") {
|
|
t.Fatal("a credential nobody gave was placed")
|
|
}
|
|
|
|
// The per-pair form is refused, naming the provider's form, and reads nothing.
|
|
file := filepath.Join(t.TempDir(), "key")
|
|
if err := os.WriteFile(file, []byte("the app's key\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err := secretCommand(ctx, []string{"accept", "laptop", "requests", "tv-api", "--provider", "anchor", "--from", file})
|
|
if err == nil || !strings.Contains(err.Error(), "secret accept anchor tv tv-api") {
|
|
t.Fatalf("the per-pair form was not refused naming the provider's: %v", err)
|
|
}
|
|
|
|
consumers, unresolved, err := consumersFrom(ctx, open, "anchor", "tv", "tv-api")
|
|
if err != nil || len(unresolved) != 0 {
|
|
t.Fatalf("the consumers of tv on anchor could not be found: %v %v", err, unresolved)
|
|
}
|
|
if len(consumers) != 2 {
|
|
t.Fatalf("the consumers of tv on anchor are %+v; want requests and indexer on laptop", consumers)
|
|
}
|
|
if _, err := open.inventory.AcceptSecretForProvision(ctx, "tv-api", "anchor", "tv", "the app's key", consumers); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !placed("requests") || !placed("indexer") {
|
|
t.Fatal("the value given once for the provider was not placed for every consumer")
|
|
}
|
|
}
|