compose names the pair credentials a node's composition reads as consumer and provider, and frombus asks them sealed before it composes (issue 1126, ADR 0550 §5)
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: g14: novox/mesh-catalog#505: checks/placement/go.mod names mesh-sdk f1d7c7377dc4, which is neither on mesh-sdk's trunk nor the grou…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: g14: novox/mesh-catalog#505: checks/placement/go.mod names mesh-sdk f1d7c7377dc4, which is neither on mesh-sdk's trunk nor the grou…
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// CredentialKey names one pair credential by the ends it joins: the provision, the consumer's node and module, the
|
||||
// provider's node, and the local name where a module keeps several (Credential's and Issued's key).
|
||||
type CredentialKey struct {
|
||||
Provision string `json:"provision"`
|
||||
Consumer string `json:"consumer"`
|
||||
ConsumerModule string `json:"consumer_module"`
|
||||
Provider string `json:"provider"`
|
||||
Local string `json:"local,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialsNeeded is every pair credential node's composition reads (novox/hq ADR 0547 §2, ADR 0550 §5, issue 1126):
|
||||
// as a consumer, each need of its resolution a provider's credential answers (a need answered by a record is a licence's,
|
||||
// not a pair's); as a provider, each credential it must carry a grant for (rowsFor). A node that composes its own asks the
|
||||
// vault for exactly these, sealed to its key, and composes with them; one it does not get is stood in for, which the
|
||||
// node's own apply refuses (CompositionsApplied).
|
||||
func CredentialsNeeded(in Input, node string) (asConsumer, asProvider []CredentialKey, err error) {
|
||||
c := newComposer(in)
|
||||
resolved, err := c.resolution(node)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, n := range resolved.Needs {
|
||||
if n.ByRecord || n.From == "" {
|
||||
continue
|
||||
}
|
||||
asConsumer = append(asConsumer, CredentialKey{Provision: n.Name, Consumer: node, ConsumerModule: n.For,
|
||||
Provider: n.From, Local: n.Local})
|
||||
}
|
||||
rows, err := c.rowsFor(node)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("compose: the credentials %s provides: %w", node, err)
|
||||
}
|
||||
for _, r := range rows {
|
||||
asProvider = append(asProvider, CredentialKey{Provision: r.Provision, Consumer: r.Consumer,
|
||||
ConsumerModule: r.ConsumerModule, Provider: node, Local: r.Local})
|
||||
}
|
||||
sortKeys(asConsumer)
|
||||
sortKeys(asProvider)
|
||||
return asConsumer, asProvider, nil
|
||||
}
|
||||
|
||||
func sortKeys(ks []CredentialKey) {
|
||||
sort.Slice(ks, func(i, j int) bool {
|
||||
a, b := ks[i], ks[j]
|
||||
switch {
|
||||
case a.Provision != b.Provision:
|
||||
return a.Provision < b.Provision
|
||||
case a.Provider != b.Provider:
|
||||
return a.Provider < b.Provider
|
||||
case a.Consumer != b.Consumer:
|
||||
return a.Consumer < b.Consumer
|
||||
case a.ConsumerModule != b.ConsumerModule:
|
||||
return a.ConsumerModule < b.ConsumerModule
|
||||
}
|
||||
return a.Local < b.Local
|
||||
})
|
||||
}
|
||||
@@ -172,6 +172,15 @@ func aGrantMesh(t *testing.T) Input {
|
||||
// the sealed copy a stand-in is composed and named (novox/hq plan.go grantsFor).
|
||||
func TestAGrantIsCreatedForEachCredentialMadeForAConsumer(t *testing.T) {
|
||||
in := aGrantMesh(t)
|
||||
// 1126 (ADR 0550 §5): the credentials each node's composition reads, by their ends: laptop's files consumes anchor's
|
||||
// s3-bucket, and anchor grants it; the node composing its own asks the vault for exactly these.
|
||||
pair := CredentialKey{Provision: "s3-bucket", Consumer: "laptop", ConsumerModule: "files", Provider: "anchor"}
|
||||
if consumer, provider, err := CredentialsNeeded(in, "laptop"); err != nil || len(provider) != 0 || len(consumer) != 1 || consumer[0] != pair {
|
||||
t.Errorf("laptop needs %v as consumer and %v as provider (%v), want %v as consumer", consumer, provider, err, pair)
|
||||
}
|
||||
if consumer, provider, err := CredentialsNeeded(in, "anchor"); err != nil || len(consumer) != 0 || len(provider) != 1 || provider[0] != pair {
|
||||
t.Errorf("anchor needs %v as consumer and %v as provider (%v), want %v as provider", consumer, provider, err, pair)
|
||||
}
|
||||
got := rendered(t, in, "anchor", Foreseeing)
|
||||
if len(got.Rendering.Grants) != 1 {
|
||||
t.Fatalf("grants are %+v", got.Rendering.Grants)
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
// - each module's config values on a node: mesh-config's resolved state, the composing node's slice alone unless the
|
||||
// caller composes every node (ADR 0547 §2, the prepare step's L4).
|
||||
//
|
||||
// **Own secrets** come from the vault sealed to the composing node's key (OwnSecrets), for that node alone. **Not yet
|
||||
// read** (each a later subtask of issue 1126): licences, pair credentials, issued credentials and core boxes. Without one
|
||||
// **Own secrets and pair credentials** come from the vault sealed to the composing node's key (OwnSecrets, Credentials),
|
||||
// for that node alone: as a consumer and as a provider, exactly the keys compose.CredentialsNeeded names. **Not yet
|
||||
// read** (each a later subtask of issue 1126): licences (none is placed today) and core boxes. Without one
|
||||
// the composition stands in for it, and Compose refuses a composition that stands in for anything (compose.ErrStoodIn):
|
||||
// a node never applies a stand-in. No node is joining before ADR 0547's third milestone (ADR 0550 §6), so Joining is
|
||||
// always empty.
|
||||
@@ -58,6 +59,10 @@ type Reader interface {
|
||||
// OwnSecrets is the own secrets of the composing node's modules, each sealed to that node's key, as the vault answers
|
||||
// the node's engine (the vault's verb `own`, ADR 0550 §5); asked only for the node composing itself.
|
||||
OwnSecrets(ctx context.Context, node string) ([]compose.OwnSecretRow, error)
|
||||
// Credentials is, for the composing node, each pair credential it reads as a consumer, held and sealed to its key,
|
||||
// and each it grants as a provider, with the copy sealed to it (the vault's `own` with the keys, ADR 0550 §5). A key
|
||||
// the vault holds no value for is left out; the composition then stands in for it, and Compose refuses it.
|
||||
Credentials(ctx context.Context, node string, asConsumer, asProvider []compose.CredentialKey) ([]compose.Credential, []compose.Issued, error)
|
||||
}
|
||||
|
||||
// Mesh is what is true of the whole mesh and is no part's state on the bus yet: the private network's range and the
|
||||
@@ -217,6 +222,16 @@ func Compose(ctx context.Context, r Reader, mesh Mesh, s Scope, fps map[string]s
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// The pair credentials its composition reads, asked of the vault sealed to it (ADR 0550 §5).
|
||||
asConsumer, asProvider, err := compose.CredentialsNeeded(in, s.Node)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(asConsumer)+len(asProvider) > 0 {
|
||||
if in.Credentials, in.Issued, err = r.Credentials(ctx, s.Node, asConsumer, asProvider); err != nil {
|
||||
return nil, nil, fmt.Errorf("frombus: the credentials of %s: %w", s.Node, err)
|
||||
}
|
||||
}
|
||||
return compose.CompositionsApplied(in, s.Node, fps)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ func (b *bus) Manifest(_ context.Context, digest string) ([]byte, error) {
|
||||
}
|
||||
func (b *bus) OwnSecrets(context.Context, string) ([]compose.OwnSecretRow, error) { return b.own, nil }
|
||||
|
||||
func (b *bus) Credentials(context.Context, string, []compose.CredentialKey, []compose.CredentialKey) ([]compose.Credential, []compose.Issued, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (b *bus) Values(_ context.Context, node, module string) (map[string]any, error) {
|
||||
b.valuesRead = append(b.valuesRead, node+"."+module)
|
||||
return b.values[node+"."+module], nil
|
||||
|
||||
Reference in New Issue
Block a user