A provision names the engine, because a consumer is coupled to one
Provisions were named after roles: provides "database", requires "database". Nothing distinguished engines, so a module written against PostgreSQL could be matched to a provider of SQL Server, resolve as satisfied, deploy, and fail on its first query — with nothing connecting that error back to a match made elsewhere by something that believed it had done its job. The failure is in the direction that hides. Refusing on ambiguity exists precisely so this does not happen, and the generic name walked around it: with one provider of each name nothing is ambiguous, so nothing is asked. How it got in: every resolver test had exactly one provider per name, so no mismatch was expressible and none was caught. The fixtures agreed with the design — the same fault as the imagined test output in 04-ISSUES/005, at the level of a name. Refused rather than documented, because the old naming *was* the documented convention. Providing database/db/sql/sql-database is now a parse error naming what to write instead. The rule is about coupling, not specificity everywhere: route and resolver stay role-named, because a consumer genuinely cannot tell which proxy answered. novox/hq ADR 0027.
This commit is contained in:
@@ -54,11 +54,11 @@ func TestASecretIsMadeOnceAndKept(t *testing.T) {
|
||||
// Regenerating on every declaration would restart both ends on every push, and — worse — the
|
||||
// password a provider was told to create would never be the one its consumer was given.
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
first, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
first, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
second, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func TestTheStoredSecretIsNotTheSecret(t *testing.T) {
|
||||
// what an encrypted column does not achieve, because whoever runs the control plane can read
|
||||
// through it.
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
got, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
got, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -105,7 +105,7 @@ func TestANewSealingKeyMeansANewSecret(t *testing.T) {
|
||||
// A node that rejoined generated a new key and can no longer open what was sealed to the old
|
||||
// one. Keeping the blob would deliver something unreadable for ever, reported as configured.
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
before, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
before, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func TestANewSealingKeyMeansANewSecret(t *testing.T) {
|
||||
if err := inv.RecordSealingKey(ctx, node.ID, fresh); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
after, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
after, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -133,14 +133,14 @@ func TestANewSealingKeyMeansANewSecret(t *testing.T) {
|
||||
|
||||
func TestRotatingReachesBothEnds(t *testing.T) {
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
before, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
before, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inv.RotateSecret(ctx, "database", "consumer", "provider"); err != nil {
|
||||
if err := inv.RotateSecret(ctx, "postgres-database", "consumer", "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
after, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
after, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -175,7 +175,7 @@ func TestAProviderIsToldEveryCredentialItMustCreate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, who := range []string{"consumer", "second-consumer"} {
|
||||
if _, err := inv.SecretFor(ctx, "database", who, "provider"); err != nil {
|
||||
if _, err := inv.SecretFor(ctx, "postgres-database", who, "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func TestANodeWithNoSealingKeyCannotBeGivenASecret(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
_, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
_, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err == nil {
|
||||
t.Fatal("a credential was made for nodes that cannot open one")
|
||||
}
|
||||
@@ -217,7 +217,7 @@ func TestANodeWithNoSealingKeyCannotBeGivenASecret(t *testing.T) {
|
||||
|
||||
func TestSecretsGoWhenANodeLeaves(t *testing.T) {
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
if _, err := inv.SecretFor(ctx, "database", "consumer", "provider"); err != nil {
|
||||
if _, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.store.Pool().Exec(ctx, `delete from node where name = 'consumer'`); err != nil {
|
||||
@@ -333,14 +333,14 @@ func TestACredentialGoesWhenTheConsumerStopsAskingForIt(t *testing.T) {
|
||||
// only fires if the mesh stops asking.
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
if err := inv.RegisterModule(ctx, catalogue.Manifest{
|
||||
Module: "meshboard", Version: "1", Requires: []string{"database"},
|
||||
Module: "meshboard", Version: "1", Requires: []string{"postgres-database"},
|
||||
}, Source{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inv.Assign(ctx, "consumer", "meshboard"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.SecretFor(ctx, "database", "consumer", "provider"); err != nil {
|
||||
if _, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ func TestACredentialGoesWhenEitherMachineDoes(t *testing.T) {
|
||||
// The case that must not leave a live login behind: a machine removed from the mesh. Its
|
||||
// credentials go with it, and the provider stops being told to keep them.
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
if _, err := inv.SecretFor(ctx, "database", "consumer", "provider"); err != nil {
|
||||
if _, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := inv.store.Pool().Exec(ctx, `delete from node where name = 'consumer'`); err != nil {
|
||||
@@ -464,7 +464,7 @@ func TestEveryHolderOfACredentialCanBeNamed(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, consumer := range []string{"consumer", "third"} {
|
||||
if _, err := inv.SecretFor(ctx, "database", consumer, "provider"); err != nil {
|
||||
if _, err := inv.SecretFor(ctx, "postgres-database", consumer, "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -473,7 +473,7 @@ func TestEveryHolderOfACredentialCanBeNamed(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
holders, err := inv.HoldersOf(ctx, "database", "")
|
||||
holders, err := inv.HoldersOf(ctx, "postgres-database", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -481,14 +481,14 @@ func TestEveryHolderOfACredentialCanBeNamed(t *testing.T) {
|
||||
t.Fatalf("a holder of the credential was not named: %+v", holders)
|
||||
}
|
||||
for _, h := range holders {
|
||||
if h.Provision != "database" {
|
||||
if h.Provision != "postgres-database" {
|
||||
t.Fatalf("rotating one provision would have touched %q", h.Provision)
|
||||
}
|
||||
}
|
||||
|
||||
// One machine's, when that is what was asked for. Rotating the other nine because one is
|
||||
// suspected is a great deal of disruption for one suspicion.
|
||||
one, err := inv.HoldersOf(ctx, "database", "third")
|
||||
one, err := inv.HoldersOf(ctx, "postgres-database", "third")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -500,14 +500,14 @@ func TestEveryHolderOfACredentialCanBeNamed(t *testing.T) {
|
||||
// And rotating gives both ends a new credential, together — the same one.
|
||||
func TestRotatingGivesBothEndsTheSameNewCredential(t *testing.T) {
|
||||
inv, ctx := twoNodesWithKeys(t)
|
||||
before, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
before, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inv.RotateSecret(ctx, "database", "consumer", "provider"); err != nil {
|
||||
if err := inv.RotateSecret(ctx, "postgres-database", "consumer", "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
after, err := inv.SecretFor(ctx, "database", "consumer", "provider")
|
||||
after, err := inv.SecretFor(ctx, "postgres-database", "consumer", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -534,14 +534,14 @@ func TestRotatingGivesBothEndsTheSameNewCredential(t *testing.T) {
|
||||
if err := inv.RecordSealingKey(ctx, third.ID, key); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
untouched, err := inv.SecretFor(ctx, "database", "third", "provider")
|
||||
untouched, err := inv.SecretFor(ctx, "postgres-database", "third", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := inv.RotateSecret(ctx, "database", "consumer", "provider"); err != nil {
|
||||
if err := inv.RotateSecret(ctx, "postgres-database", "consumer", "provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
again, err := inv.SecretFor(ctx, "database", "third", "provider")
|
||||
again, err := inv.SecretFor(ctx, "postgres-database", "third", "provider")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user