Pins mesh-sdk 3028c7e (delivery.OwnNeedName). The fixtures carry the id shape captured from novox's shadow answer.
105 lines
5.0 KiB
Go
105 lines
5.0 KiB
Go
package ownresync
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// Issue 1126, cause C4: after a shadow answer saying the vault holds another value of an own secret than runs, the node
|
|
// hands the vault what runs, in the verb's two steps, once per running value; and only while it applies the
|
|
// controller's compositions and runs the one its want names.
|
|
func TestTheNodeHandsTheVaultWhatRunsOfADriftedOwnSecret(t *testing.T) {
|
|
const running = "the-value-that-runs"
|
|
digest := "sha256:" + strings.Repeat("a", 64)
|
|
fp := "src2:" + strings.Repeat("b", 64)
|
|
sum := sha256.Sum256([]byte(running))
|
|
answers := map[string]delivery.CandidateAnswer{"g": {Head: "h", Verdict: delivery.CandidateDiffers,
|
|
Differs: []delivery.ModuleDiffers{{Module: "only-office", Why: "the resource differs", Bodies: []delivery.ResourceBodies{
|
|
{ID: "only-office.needs-jwt", Composed: `{"sealed":"` + delivery.MaskedAnotherValue + `"}`, Running: `{"sealed":"` + delivery.MaskedAnotherValue + `"}`},
|
|
{ID: "only-office.needs-salt", Composed: `{"sealed":"<masked: another seal of the same value>"}`}}}}}}
|
|
for name, row := range map[string]struct {
|
|
composes string
|
|
runs string // the have's composition digest
|
|
refuse string // the vault's refusal
|
|
asks int // the vault's verb asked, both passes
|
|
says string
|
|
}{
|
|
"applies the controller's, runs its want": {delivery.ComposesController, digest, "", 2, "generation 1 replaced by 2"},
|
|
"composes its own": {delivery.ComposesOwn, digest, "", 0, "composes its own"},
|
|
"not decided yet": {"", digest, "", 0, ""},
|
|
"runs not yet what its want names": {delivery.ComposesController, "sha256:" + strings.Repeat("c", 64), "", 0, "not yet the composition"},
|
|
"the vault refuses": {delivery.ComposesController, digest, "switched off", 1, "switched off"},
|
|
} {
|
|
var asked []map[string]any
|
|
var said []string
|
|
want, _ := delivery.EncodeWant(delivery.Want{FP: fp, Composed: digest})
|
|
have, _ := delivery.EncodeHave(delivery.Have{FP: fp, Composed: row.runs, Phase: delivery.PhaseRunning, Health: delivery.HealthOK})
|
|
r := New(Env{Node: "novox", Composes: func() string { return row.composes },
|
|
Running: func() []delivery.Composed {
|
|
return []delivery.Composed{{Node: "novox", Module: "only-office", FP: fp, Resources: []json.RawMessage{
|
|
// As novox runs it (its shadow answer, 2026-10-28): the id carries the module's name.
|
|
json.RawMessage(`{"id":"only-office.needs-jwt","module":"only-office","type":"file",` +
|
|
`"path":"/var/lib/only-office/jwt.secret","sealed":"box-of-the-running-value"}`)}}}
|
|
},
|
|
Get: func(_ context.Context, bucket, key string) ([]byte, error) {
|
|
switch {
|
|
case bucket == delivery.AssignerWantsBucket && key == "want.novox.only-office":
|
|
return want, nil
|
|
case bucket == delivery.HaveBucket && key == "novox.only-office":
|
|
return have, nil
|
|
}
|
|
return nil, nil
|
|
},
|
|
Unseal: func(sealed string) ([]byte, error) {
|
|
if sealed != "box-of-the-running-value" {
|
|
return nil, errors.New("not this machine's")
|
|
}
|
|
return []byte(running), nil
|
|
},
|
|
Seal: func(to string, value []byte) (string, error) { return to + "(" + string(value) + ")", nil },
|
|
Ask: func(_ context.Context, verb string, args map[string]any) (json.RawMessage, error) {
|
|
asked = append(asked, args)
|
|
if verb != "move-in" || args["replace_running"] != true {
|
|
t.Errorf("%s: asked %s with %v, not move-in with replace_running", name, verb, args)
|
|
}
|
|
if row.refuse != "" {
|
|
return nil, errors.New(row.refuse)
|
|
}
|
|
if args["call"] == nil {
|
|
if args["module"] != "only-office" || args["name"] != "jwt" || args["node"] != nil {
|
|
t.Errorf("%s: the first step names %v", name, args)
|
|
}
|
|
return json.RawMessage(`{"call":"c1","seal_to":"key","secret":"own/novox/only-office/jwt"}`), nil
|
|
}
|
|
if args["sealed"] != "key("+running+")" {
|
|
t.Errorf("%s: handed %v, not what runs sealed to the call's key", name, args["sealed"])
|
|
}
|
|
return json.RawMessage(`{"secret":"own/novox/only-office/jwt","from_generation":1,"to_generation":2,` +
|
|
`"fingerprint":"sha256:` + hex.EncodeToString(sum[:]) + `"}`), nil
|
|
},
|
|
Say: func(s string) { said = append(said, s) }})
|
|
first := r.Take(context.Background(), answers)
|
|
again := r.Take(context.Background(), answers)
|
|
if len(asked) != row.asks {
|
|
t.Errorf("%s: the vault asked %d times, want %d: %v", name, len(asked), row.asks, asked)
|
|
}
|
|
if len(again) != 0 {
|
|
t.Errorf("%s: the same answer did or said something again: %+v", name, again)
|
|
}
|
|
all := strings.Join(said, "\n")
|
|
if row.says == "" && (len(first) != 0 || all != "") || row.says != "" && (len(first) != 1 || !strings.Contains(all, row.says)) {
|
|
t.Errorf("%s: did %+v and said %q, want %q", name, first, all, row.says)
|
|
}
|
|
if strings.Contains(all, running) {
|
|
t.Errorf("%s: the value is in what the node said", name)
|
|
}
|
|
}
|
|
}
|