Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
237a534ca4 |
@@ -10,6 +10,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -424,3 +425,72 @@ func hands(raw []byte) bool {
|
|||||||
h, err := handsOf(raw)
|
h, err := handsOf(raw)
|
||||||
return err == nil && h
|
return err == nil && h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandedJudged is how the vault judges one module of a machine for `own`: handed (its own secrets left out, for the
|
||||||
|
// provisioner's client to fill from `get`), and why, as the plan in its assignment says it; or why it could not judge.
|
||||||
|
type HandedJudged struct {
|
||||||
|
Module string `json:"module"`
|
||||||
|
Handed bool `json:"handed"`
|
||||||
|
Why string `json:"why"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandedOf is the vault's judgement of each module of node, as `own` would answer it now (issue 1966): every module it
|
||||||
|
// keeps an own secret of there and every module placed there, sorted. Read from the same plans `own` reads, so what it
|
||||||
|
// says is what the next answer leaves out. A node composes by its manifest, its build and the machine, and the plans it
|
||||||
|
// makes are what this reads next: a module named here as not handed for want of a plan is handed by its node's next
|
||||||
|
// composition, where the machine takes it.
|
||||||
|
func (v *Vault) HandedOf(node string) ([]HandedJudged, error) {
|
||||||
|
if !namePart.MatchString(node) {
|
||||||
|
return nil, fmt.Errorf("%q is not a machine's name", node)
|
||||||
|
}
|
||||||
|
all, err := v.Store.ListPrefix("own/" + node + "/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
modules := map[string]bool{}
|
||||||
|
for _, r := range all {
|
||||||
|
if parts := strings.Split(r.Name, "/"); len(parts) == 4 {
|
||||||
|
modules[parts[2]] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plan := v.plansOf(node)
|
||||||
|
placed, placedErr := plan.placedModules()
|
||||||
|
for _, m := range placed {
|
||||||
|
modules[m] = true
|
||||||
|
}
|
||||||
|
out := make([]HandedJudged, 0, len(modules))
|
||||||
|
for _, module := range slices.Sorted(maps.Keys(modules)) {
|
||||||
|
out = append(out, plan.judged(module))
|
||||||
|
}
|
||||||
|
if placedErr != nil && len(out) == 0 {
|
||||||
|
return nil, fmt.Errorf("the placement of %s could not be read and the vault keeps no own secret there: %w", node, placedErr)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// judged is handed with its why, as HandedOf says it.
|
||||||
|
func (p *planReader) judged(module string) HandedJudged {
|
||||||
|
j := HandedJudged{Module: module}
|
||||||
|
raw, why, err := p.read(module)
|
||||||
|
switch {
|
||||||
|
case why != "":
|
||||||
|
j.Why = "not judged: " + why
|
||||||
|
return j
|
||||||
|
case err != nil:
|
||||||
|
j.Why = fmt.Sprintf("not judged: the assignment %s.%s could not be read: %v", p.node, module, err)
|
||||||
|
return j
|
||||||
|
case raw == nil:
|
||||||
|
j.Why = "no assignment holds a plan of it yet: its own secrets are answered to its node"
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
hands, err := handsOf(raw)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
j.Why = fmt.Sprintf("not judged: the assignment %s.%s is not the controller's: %v", p.node, module, err)
|
||||||
|
case hands:
|
||||||
|
j.Handed, j.Why = true, "its plan hands it: its own secrets are its provisioner client's, from get"
|
||||||
|
default:
|
||||||
|
j.Why = "its plan hands nothing (none, or provisions alone): its own secrets are answered to its node"
|
||||||
|
}
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
|||||||
@@ -291,4 +291,27 @@ func TestOwnLeavesOutOnlyAModuleWhosePlanHandsIt(t *testing.T) {
|
|||||||
if want := map[string]bool{"grafana": false, "web": true, "db": true}; !reflect.DeepEqual(got.Handed, want) {
|
if want := map[string]bool{"grafana": false, "web": true, "db": true}; !reflect.DeepEqual(got.Handed, want) {
|
||||||
t.Errorf("own judged %v handed, want %v", got.Handed, want)
|
t.Errorf("own judged %v handed, want %v", got.Handed, want)
|
||||||
}
|
}
|
||||||
|
// Issue 1966: the vault's judgement is read by a tool, each module with why; one with no plan yet is not handed, for
|
||||||
|
// want of a plan its node's next composition makes.
|
||||||
|
if _, err := moveIn(t, v, "node.anchor", MoveOf{Node: "anchor", Module: "builder", Name: "token", Origin: "made",
|
||||||
|
Generation: "g-1", SealingKey: pub}, "v-builder"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
answer, err := toolNamed(t, definitions(&Env{Vault: v.Vault}), "own_handed").Run(map[string]any{"node": "anchor"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
judged := map[string]HandedJudged{}
|
||||||
|
for _, j := range answer.(map[string]any)["modules"].([]HandedJudged) {
|
||||||
|
judged[j.Module] = j
|
||||||
|
}
|
||||||
|
for module, want := range map[string][2]string{"builder": {"false", "no assignment holds a plan"},
|
||||||
|
"grafana": {"false", "hands nothing"}, "web": {"true", "its plan hands it"}, "db": {"true", "its plan hands it"}} {
|
||||||
|
if j := judged[module]; fmt.Sprint(j.Handed) != want[0] || !strings.Contains(j.Why, want[1]) {
|
||||||
|
t.Errorf("own_handed says %s is %+v, want handed %s, why saying %q", module, j, want[0], want[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(judged) != 4 {
|
||||||
|
t.Errorf("own_handed names %v, want builder, db, grafana and web", judged)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -399,6 +399,23 @@ func definitions(env *Env) []toolDef {
|
|||||||
given, _ := a["fingerprint"].(string)
|
given, _ := a["fingerprint"].(string)
|
||||||
return verifyAnswer(env, as, given), nil
|
return verifyAnswer(env, as, given), nil
|
||||||
}},
|
}},
|
||||||
|
{Name: "own_handed",
|
||||||
|
Description: "Which modules of a machine the vault judges handed to the provisioner, as its verb `own` answers that " +
|
||||||
|
"machine's node-engine now (issue 1966): for every module it keeps an own secret of there and every module " +
|
||||||
|
"placed there, handed or not and why (its plan hands it, hands nothing, no plan yet, or why it could not " +
|
||||||
|
"judge). A handed module's own secrets are left out of `own`, for its client to fill from `get`. Never a value.",
|
||||||
|
Input: map[string]any{"node": str("the machine")},
|
||||||
|
run: func(_ string, a map[string]any) (any, error) {
|
||||||
|
v, err := env.ready()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
judged, err := v.HandedOf(arg(a, "node"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return map[string]any{"node": arg(a, "node"), "modules": judged}, nil
|
||||||
|
}},
|
||||||
{Name: "secret_handed",
|
{Name: "secret_handed",
|
||||||
Description: "Who was handed which secret by `get`, and when, and who was refused, newest first, as the " +
|
Description: "Who was handed which secret by `get`, and when, and who was refused, newest first, as the " +
|
||||||
"vault's own store records it. Never a value.",
|
"vault's own store records it. Never a value.",
|
||||||
|
|||||||
@@ -173,6 +173,7 @@
|
|||||||
"secret_holders",
|
"secret_holders",
|
||||||
"secret_verify",
|
"secret_verify",
|
||||||
"secret_handed",
|
"secret_handed",
|
||||||
|
"own_handed",
|
||||||
"secret_own",
|
"secret_own",
|
||||||
"account_hash",
|
"account_hash",
|
||||||
"account_adopt",
|
"account_adopt",
|
||||||
|
|||||||
Reference in New Issue
Block a user