Provision and provider users take the provisioner's client; mesh-vault writes its findings through the conditions client (K-4..K-20, K-21 part; ADR 0634 §4; issue 1966)
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
|
||||
"gitea/internal/forgehttp"
|
||||
)
|
||||
|
||||
@@ -29,8 +29,8 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
)
|
||||
|
||||
const bundleVar = "MESH_FORGE_ACCOUNT_TEST_BUNDLE"
|
||||
@@ -301,7 +301,7 @@ func TestTheProviderWholeAnswersAMachineThatReportedItsKey(t *testing.T) {
|
||||
"plan": map[string]any{"provisions": []map[string]any{{"provision": "forge-account", "provider": "novox",
|
||||
"provider-module": "gitea", "as": "mesh_g14_ssh_client"}}}})
|
||||
rt.put("mesh-provisioner.provisions", "g14.ssh-client.forge-account",
|
||||
json.RawMessage(provisionKey("g14", "ssh-client", provisions.Asked, nil)))
|
||||
json.RawMessage(provisionKey("g14", "ssh-client", meshprovisionerclient.Asked, nil)))
|
||||
rt.put("ssh-client.keys", "g14.nox", map[string]string{"node": "g14", "account": "nox", "kind": "machine",
|
||||
"public": pubA + " nox@g14 (mesh)", "fingerprint": "SHA256:x", "at": "2026-10-16T10:00:00Z"})
|
||||
rt.start([]string{"MESH_NODE=novox", "MESH_MODULE=gitea", "MESH_SERVED_MODULE=gitea",
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
|
||||
"gitea/internal/forgeaccount"
|
||||
"gitea/internal/forgehttp"
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
|
||||
"gitea/internal/forgeaccount"
|
||||
"gitea/internal/forgehttp"
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
|
||||
"gitea/internal/forgehttp"
|
||||
)
|
||||
@@ -84,7 +84,7 @@ func (b *fakeBus) entry(t *testing.T, key string) provider.Entry {
|
||||
// values (ADR 0570 clause 3: an entry the provider could not make is kept, failed, serving nothing; one it is not
|
||||
// ready to make waits and keeps serving).
|
||||
func (b *fakeBus) serves(key string, state ...string) bool {
|
||||
want := provisions.Provided
|
||||
want := meshprovisionerclient.Provided
|
||||
if len(state) > 0 {
|
||||
want = state[0]
|
||||
}
|
||||
@@ -110,29 +110,29 @@ func (b *fakeBus) bindClient(node string) {
|
||||
// bindPlan makes module on node bind gitea's forge-account as the seat mesh-provisioner keeps it: its plan, and the
|
||||
// provision key asked of gitea on novox (novox/hq ADR 0570 clauses 1 to 3).
|
||||
func (b *fakeBus) bindPlan(node, module, as string) {
|
||||
b.set(provisions.PlansBucket, node+"."+module, []byte(fmt.Sprintf(
|
||||
b.set(meshprovisionerclient.PlansBucket, node+"."+module, []byte(fmt.Sprintf(
|
||||
`{"v":1,"node":%q,"module":%q,"plan":{"provisions":[{"provision":"forge-account","provider":"novox","provider-module":"gitea","as":%q}]}}`,
|
||||
node, module, as)))
|
||||
b.set(provisions.ProvisionsBucket, node+"."+module+".forge-account", provisionKey(node, module, provisions.Asked, nil))
|
||||
b.set(meshprovisionerclient.ProvisionsBucket, node+"."+module+".forge-account", provisionKey(node, module, meshprovisionerclient.Asked, nil))
|
||||
}
|
||||
|
||||
// retire is the operator's yes to the retiring of module's forge-account on node: its plan gone, its key retiring
|
||||
// with the warrant (ADR 0570 clause 5), the only way the provider lets an entry go.
|
||||
func (b *fakeBus) retire(node, module string) {
|
||||
b.mu.Lock()
|
||||
delete(b.buckets[provisions.PlansBucket], node+"."+module)
|
||||
delete(b.buckets[meshprovisionerclient.PlansBucket], node+"."+module)
|
||||
b.mu.Unlock()
|
||||
b.set(provisions.ProvisionsBucket, node+"."+module+".forge-account", provisionKey(node, module, provisions.Retiring,
|
||||
&provisions.Warrant{Ask: "ask-1", Option: "yes", By: "operator", At: time.Date(2026, 10, 29, 12, 0, 0, 0, time.UTC)}))
|
||||
b.set(meshprovisionerclient.ProvisionsBucket, node+"."+module+".forge-account", provisionKey(node, module, meshprovisionerclient.Retiring,
|
||||
&meshprovisionerclient.Warrant{Ask: "ask-1", Option: "yes", By: "operator", At: time.Date(2026, 10, 29, 12, 0, 0, 0, time.UTC)}))
|
||||
}
|
||||
|
||||
func provisionKey(node, module, state string, w *provisions.Warrant) []byte {
|
||||
pr := provisions.Provision{Provision: "forge-account", State: state, Warrant: w,
|
||||
Consumer: provisions.End{Node: node, Module: module}, Provider: provisions.End{Node: "novox", Module: "gitea"}}
|
||||
if state == provisions.Retiring {
|
||||
func provisionKey(node, module, state string, w *meshprovisionerclient.Warrant) []byte {
|
||||
pr := meshprovisionerclient.Provision{Provision: "forge-account", State: state, Warrant: w,
|
||||
Consumer: meshprovisionerclient.End{Node: node, Module: module}, Provider: meshprovisionerclient.End{Node: "novox", Module: "gitea"}}
|
||||
if state == meshprovisionerclient.Retiring {
|
||||
pr.Why = "no want needs it"
|
||||
}
|
||||
raw, err := provisions.Encode(pr)
|
||||
raw, err := meshprovisionerclient.Encode(pr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -527,7 +527,7 @@ func TestAPassBeforeTheKeysAreLoadedRemovesAndDeletesNothing(t *testing.T) {
|
||||
if got := strings.Join(r.forge.titles("nox-agent"), ","); got != "mesh:g14.nox,my own" {
|
||||
t.Fatalf("keys = %s", got)
|
||||
}
|
||||
if !r.bus.serves("g14.ssh-client.forge-account", provisions.Waits) {
|
||||
if !r.bus.serves("g14.ssh-client.forge-account", meshprovisionerclient.Waits) {
|
||||
t.Fatal("the entry stopped serving before the keys were read; it waits and keeps serving")
|
||||
}
|
||||
if len(out) != 1 || !strings.Contains(out[0].Why, "not read yet") {
|
||||
|
||||
@@ -3,6 +3,7 @@ module gitea
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry v0.0.0-00010101000000-000000000000
|
||||
|
||||
@@ -8,7 +8,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
)
|
||||
|
||||
// grantGives writes a grant file (MESH_RECEIVES) naming consumers the plans do not move (issue 937): each one's
|
||||
@@ -49,22 +49,22 @@ func noOpPlan(bus *bucketBus, node, module string) {
|
||||
// keycloak on novox the key the seat composes, asked (novox/hq ADR 0570 clauses 1 to 3).
|
||||
func seatPlan(bus *bucketBus, node, module string, plan map[string]any) {
|
||||
raw, _ := json.Marshal(map[string]any{"v": 1, "node": node, "module": module, "plan": plan})
|
||||
_ = bus.Put(ctx, provisions.PlansBucket, node+"."+module, raw)
|
||||
_ = bus.Put(ctx, meshprovisionerclient.PlansBucket, node+"."+module, raw)
|
||||
list, _ := plan["provisions"].([]any)
|
||||
for _, e := range list {
|
||||
entry := e.(map[string]any)
|
||||
name, _ := entry["provision"].(string)
|
||||
local, _ := entry["local"].(string)
|
||||
key, err := provisions.Key(node, module, name, local)
|
||||
key, err := meshprovisionerclient.Key(node, module, name, local)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
asked, err := provisions.Encode(provisions.Provision{Provision: name, Local: local, State: provisions.Asked,
|
||||
Consumer: provisions.End{Node: node, Module: module}, Provider: provisions.End{Node: "novox", Module: "keycloak"}})
|
||||
asked, err := meshprovisionerclient.Encode(meshprovisionerclient.Provision{Provision: name, Local: local, State: meshprovisionerclient.Asked,
|
||||
Consumer: meshprovisionerclient.End{Node: node, Module: module}, Provider: meshprovisionerclient.End{Node: "novox", Module: "keycloak"}})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_ = bus.Put(ctx, provisions.ProvisionsBucket, key, asked)
|
||||
_ = bus.Put(ctx, meshprovisionerclient.ProvisionsBucket, key, asked)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ package main
|
||||
// retirement to the harness's guards (the SDK's harness retirement): the client is disabled and marked to delete, never removed.
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
)
|
||||
|
||||
// ProvisionName is what keycloak provides, as its manifest says.
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
)
|
||||
|
||||
const testIssuer = "https://id.example.org/realms/Novox"
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
)
|
||||
|
||||
// Mark is the attribute marking a client as the mesh's own work.
|
||||
|
||||
@@ -7,7 +7,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ go 1.26.0
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104174344-ae5266e77639 // indirect
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||
|
||||
@@ -3,6 +3,7 @@ module mesh-config
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel v0.0.0-00010101000000-000000000000
|
||||
|
||||
@@ -3,6 +3,7 @@ module mesh-delivery-gate
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
)
|
||||
|
||||
// The provisioner's client on one node (novox/hq ADR 0349 clauses 4 and 5, design 57 P5, issue 671).
|
||||
@@ -44,7 +44,7 @@ import (
|
||||
const (
|
||||
// PlansBucket is where each module's provisioning plan on each node is read: the seat mesh-provisioner's
|
||||
// `plans` (novox/hq ADR 0570 clause 2), read as an assignment's value carried it (asAssignment).
|
||||
PlansBucket = provisions.PlansBucket
|
||||
PlansBucket = meshprovisionerclient.PlansBucket
|
||||
// PlacementBucket is the seat mesh-module-assigner's placement (novox/hq ADR 0567): a module it names on this node stays
|
||||
// here while the seat mesh-provisioner holds no plan of it (nats on novox runs without a want), filled from its
|
||||
// last plan, and leaves only when neither names it (issue 1126 B12; the controller's assignments mirror, read for
|
||||
@@ -1214,7 +1214,7 @@ func (c *Client) takePlacement(s *state, key string, value []byte, revision uint
|
||||
// asAssignment is a plan of the seat mesh-provisioner as the client's rules read an assignment's value:
|
||||
// `{node, module, provisioning: plan}`.
|
||||
func asAssignment(raw []byte) ([]byte, error) {
|
||||
if _, err := provisions.DecodePlan(raw); err != nil {
|
||||
if _, err := meshprovisionerclient.DecodePlan(raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The plan as it is kept, its bytes unchanged: the client reads it strictly (planOf).
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
)
|
||||
|
||||
@@ -55,9 +55,9 @@ func (m *memBus) Delete(_ context.Context, bucket, key string) error {
|
||||
// as the seat mesh-provisioner keeps it (ADR 0570 clause 2).
|
||||
func routeEntryFor(t *testing.T, module, planEntry string) string {
|
||||
t.Helper()
|
||||
bus := &memBus{buckets: map[string]map[string][]byte{provisions.PlansBucket: {
|
||||
bus := &memBus{buckets: map[string]map[string][]byte{meshprovisionerclient.PlansBucket: {
|
||||
node + "." + module: []byte(`{"v":1,"node":"` + node + `","module":"` + module + `","plan":{"provisions":[` +
|
||||
planEntry + `]}}`)}, provisions.ProvisionsBucket: {}}}
|
||||
planEntry + `]}}`)}, meshprovisionerclient.ProvisionsBucket: {}}}
|
||||
// The key the seat composed for that entry, provided-for: asked of route-proxy (ADR 0570 clause 3).
|
||||
var b struct {
|
||||
Provision string `json:"provision"`
|
||||
@@ -66,17 +66,17 @@ func routeEntryFor(t *testing.T, module, planEntry string) string {
|
||||
if err := json.Unmarshal([]byte(planEntry), &b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pr := provisions.Provision{Provision: b.Provision, Local: b.Local, State: provisions.Asked,
|
||||
Consumer: provisions.End{Node: node, Module: module}, Provider: provisions.End{Node: "novox", Module: "route-proxy"}}
|
||||
pr := meshprovisionerclient.Provision{Provision: b.Provision, Local: b.Local, State: meshprovisionerclient.Asked,
|
||||
Consumer: meshprovisionerclient.End{Node: node, Module: module}, Provider: meshprovisionerclient.End{Node: "novox", Module: "route-proxy"}}
|
||||
key, err := pr.Key()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, err := provisions.Encode(pr)
|
||||
raw, err := meshprovisionerclient.Encode(pr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bus.buckets[provisions.ProvisionsBucket][key] = raw
|
||||
bus.buckets[meshprovisionerclient.ProvisionsBucket][key] = raw
|
||||
p := provider.New(provider.Options{Node: "novox", Module: "route-proxy", Bus: bus, Logf: t.Logf,
|
||||
Offers: map[string]provider.Offer{"route": {Credential: provider.NoCredential, Serves: map[string]any{}}}})
|
||||
out, err := p.Pass(context.Background())
|
||||
|
||||
@@ -3,6 +3,7 @@ module mesh-provisioner-client
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
|
||||
@@ -39,17 +39,17 @@ import (
|
||||
|
||||
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
|
||||
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
)
|
||||
|
||||
// The states it keeps, beside Registry, Needs, Applied and Collect. RefusedState holds, by `<node>.<module>`, why a
|
||||
// placed module has no plan composed (issue 774): written while it is held, deleted once it composes again.
|
||||
const (
|
||||
ProvisionsState = provisions.ProvisionsState
|
||||
PlansState = provisions.PlansState
|
||||
ProvisionsState = meshprovisionerclient.ProvisionsState
|
||||
PlansState = meshprovisionerclient.PlansState
|
||||
RefusedState = "refused"
|
||||
)
|
||||
|
||||
@@ -169,7 +169,7 @@ func TookSaid(took []Took) string {
|
||||
return strings.Join(said, ", ")
|
||||
}
|
||||
|
||||
// ComposeNode composes one node: its plans and its provisions.
|
||||
// ComposeNode composes one node: its plans and its meshprovisionerclient.
|
||||
func (h *Holder) ComposeNode(ctx context.Context, node string) (Composed, error) {
|
||||
return h.ComposeNodeWith(ctx, node, false)
|
||||
}
|
||||
@@ -250,8 +250,8 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
|
||||
out.Problems = append(out.Problems, err.Error())
|
||||
}
|
||||
lap("plans written")
|
||||
needed := map[string]provisions.Provision{}
|
||||
elsewhere := map[string]provisions.Provision{}
|
||||
needed := map[string]meshprovisionerclient.Provision{}
|
||||
elsewhere := map[string]meshprovisionerclient.Provision{}
|
||||
// ahead are the keys only the newest build of their consumer requires: the plan written, its wanted build's, names
|
||||
// them not yet, and the provider binds them from the entry they carry (issue 1126).
|
||||
ahead := map[string]string{}
|
||||
@@ -259,10 +259,10 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
|
||||
for i, plans := range []map[string]catalogue.Plan{atWants, newest} {
|
||||
for module, plan := range plans {
|
||||
for _, p := range plan.Provisions {
|
||||
consumer := provisions.End{Module: module, Node: node}
|
||||
provider := provisions.End{Module: p.ProviderModule, Node: p.Provider}
|
||||
pv := provisions.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider,
|
||||
DependsOn: provisions.DependsOn(consumer, p, h.providerPlan(p, node, atWants))}
|
||||
consumer := meshprovisionerclient.End{Module: module, Node: node}
|
||||
provider := meshprovisionerclient.End{Module: p.ProviderModule, Node: p.Provider}
|
||||
pv := meshprovisionerclient.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider,
|
||||
DependsOn: meshprovisionerclient.DependsOn(consumer, p, h.providerPlan(p, node, atWants))}
|
||||
key, err := pv.Key()
|
||||
if err != nil {
|
||||
out.Problems = append(out.Problems, err.Error())
|
||||
@@ -281,11 +281,11 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
|
||||
}
|
||||
// Its credential is a provision of its own, the vault's (clause 6): on the consumer's node for a value of the
|
||||
// consumer's own, on the provider's for the provider's one shared value.
|
||||
ck, ok := provisions.CredentialKey(p.CredentialForm, consumer, provider, p.Provision, p.Local)
|
||||
ck, ok := meshprovisionerclient.CredentialKey(p.CredentialForm, consumer, provider, p.Provision, p.Local)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
cn, cm, cp, cl, err := provisions.ParseKey(ck)
|
||||
cn, cm, cp, cl, err := meshprovisionerclient.ParseKey(ck)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -293,8 +293,8 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
|
||||
out.Problems = append(out.Problems, "no node runs mesh-vault, so the credential "+ck+" has no provider")
|
||||
continue
|
||||
}
|
||||
cred := provisions.Provision{Provision: cp, Local: cl, Consumer: provisions.End{Module: cm, Node: cn},
|
||||
Provider: provisions.End{Module: provisions.VaultModule, Node: vaultNode}}
|
||||
cred := meshprovisionerclient.Provision{Provision: cp, Local: cl, Consumer: meshprovisionerclient.End{Module: cm, Node: cn},
|
||||
Provider: meshprovisionerclient.End{Module: meshprovisionerclient.VaultModule, Node: vaultNode}}
|
||||
if cn == node {
|
||||
if _, seen := needed[ck]; !seen {
|
||||
needed[ck] = cred
|
||||
@@ -329,7 +329,7 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
|
||||
func (h *Holder) writeRefused(node string, held map[string]string) error {
|
||||
var problems []error
|
||||
for _, module := range sortedKeys(held) {
|
||||
key, err := provisions.PlanKey(node, module)
|
||||
key, err := meshprovisionerclient.PlanKey(node, module)
|
||||
if err != nil {
|
||||
problems = append(problems, err)
|
||||
continue
|
||||
@@ -382,7 +382,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
key, err := provisions.PlanKey(p.Provider, p.ProviderModule)
|
||||
key, err := meshprovisionerclient.PlanKey(p.Provider, p.ProviderModule)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -390,7 +390,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
|
||||
if err != nil || e == nil {
|
||||
return nil
|
||||
}
|
||||
plan, err := provisions.DecodePlan(e.Value)
|
||||
plan, err := meshprovisionerclient.DecodePlan(e.Value)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -399,7 +399,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
|
||||
|
||||
// vaultNode is the node mesh-vault is placed on: the provider of every credential.
|
||||
func (h *Holder) vaultNode() string {
|
||||
nodes := h.placedOn(provisions.VaultModule)
|
||||
nodes := h.placedOn(meshprovisionerclient.VaultModule)
|
||||
if len(nodes) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -421,7 +421,7 @@ func (h *Holder) dependedOn(node string) []string {
|
||||
if err != nil || e == nil {
|
||||
continue
|
||||
}
|
||||
if p, err := provisions.Decode(e.Value); err == nil {
|
||||
if p, err := meshprovisionerclient.Decode(e.Value); err == nil {
|
||||
for _, d := range p.DependsOn {
|
||||
if strings.HasPrefix(d, node+".") {
|
||||
out = append(out, d)
|
||||
@@ -434,13 +434,13 @@ func (h *Holder) dependedOn(node string) []string {
|
||||
|
||||
// judgeElsewhere writes each needed key of another node (a provider's shared credential), its state from its outcome;
|
||||
// that node's own composition keeps it while a provision depends on it.
|
||||
func (h *Holder) judgeElsewhere(keys map[string]provisions.Provision, out *Composed) {
|
||||
func (h *Holder) judgeElsewhere(keys map[string]meshprovisionerclient.Provision, out *Composed) {
|
||||
for _, key := range sortedKeys(keys) {
|
||||
next := keys[key]
|
||||
var have provisions.Provision
|
||||
var have meshprovisionerclient.Provision
|
||||
e, err := h.Store.Get(ProvisionsState, key)
|
||||
if err == nil && e != nil {
|
||||
if have, err = provisions.Decode(e.Value); err == nil {
|
||||
if have, err = meshprovisionerclient.Decode(e.Value); err == nil {
|
||||
next.Reference = have.Reference
|
||||
}
|
||||
}
|
||||
@@ -505,12 +505,12 @@ func (h *Holder) runningOn(node string) (map[string]string, error) {
|
||||
func (h *Holder) writePlans(node string, plans map[string]catalogue.Plan, held map[string]string, out *Composed) error {
|
||||
var problems []error
|
||||
for _, module := range sortedKeys(plans) {
|
||||
key, err := provisions.PlanKey(node, module)
|
||||
key, err := meshprovisionerclient.PlanKey(node, module)
|
||||
if err != nil {
|
||||
problems = append(problems, err)
|
||||
continue
|
||||
}
|
||||
raw, err := provisions.EncodePlan(provisions.Plan{Node: node, Module: module, Plan: plans[module]})
|
||||
raw, err := meshprovisionerclient.EncodePlan(meshprovisionerclient.Plan{Node: node, Module: module, Plan: plans[module]})
|
||||
if err != nil {
|
||||
problems = append(problems, err)
|
||||
continue
|
||||
@@ -552,7 +552,7 @@ func (h *Holder) writePlans(node string, plans map[string]catalogue.Plan, held m
|
||||
// untouched (held on the node, or with a want or a have and no plan read in this pass) that its composition no longer
|
||||
// names is left as it is: not composed is not unneeded. A pass that would retire most of the node's consumers at once retires none, said as a fault of
|
||||
// the mesh (issue 940), unless retireMany.
|
||||
func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision, ahead map[string]string,
|
||||
func (h *Holder) judgeNode(node string, needed map[string]meshprovisionerclient.Provision, ahead map[string]string,
|
||||
untouched map[string]string, retireMany bool, out *Composed) {
|
||||
held := h.held(node)
|
||||
consumers := map[string]bool{}
|
||||
@@ -577,7 +577,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
var newly []string
|
||||
leaving := map[string]bool{}
|
||||
for key, have := range held {
|
||||
if _, isNeeded := needed[key]; !isNeeded && have.State != provisions.Retiring {
|
||||
if _, isNeeded := needed[key]; !isNeeded && have.State != meshprovisionerclient.Retiring {
|
||||
newly = append(newly, key)
|
||||
leaving[have.Consumer.Module] = true
|
||||
}
|
||||
@@ -607,7 +607,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
for _, key := range unionKeys(needed, held) {
|
||||
want, isNeeded := needed[key]
|
||||
have, isHeld := held[key]
|
||||
var next provisions.Provision
|
||||
var next meshprovisionerclient.Provision
|
||||
switch {
|
||||
case isNeeded:
|
||||
next = want
|
||||
@@ -615,7 +615,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
next.Reference = have.Reference
|
||||
}
|
||||
h.outcome(key, &next)
|
||||
if module, isAhead := ahead[key]; isAhead && next.State == provisions.Asked {
|
||||
if module, isAhead := ahead[key]; isAhead && next.State == meshprovisionerclient.Asked {
|
||||
// The reason the provider's helper says too: no plan names it yet, and it binds it from this key's entry.
|
||||
why := "no plan names it yet: the newest build of " + module + " requires it and its wanted build does " +
|
||||
"not, so its provider binds it from this key's plan entry"
|
||||
@@ -624,7 +624,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
}
|
||||
next.Why = why
|
||||
}
|
||||
case have.State == provisions.Retiring:
|
||||
case have.State == meshprovisionerclient.Retiring:
|
||||
next = have
|
||||
if next.Ask != "" && next.Warrant == nil && !h.askHeld(next) {
|
||||
out.Problems = append(out.Problems, "the router holds no ask "+next.Ask+" of "+key+": it is raised again")
|
||||
@@ -643,7 +643,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
}
|
||||
default:
|
||||
next = have
|
||||
next.State, next.Why = provisions.Retiring, fmt.Sprintf("no build wanted or placed on %s requires %s of %s on %s "+
|
||||
next.State, next.Why = meshprovisionerclient.Retiring, fmt.Sprintf("no build wanted or placed on %s requires %s of %s on %s "+
|
||||
"any more", node, have.Provision, have.Provider.Module, have.Provider.Node)
|
||||
next.Warrant = nil
|
||||
h.raise(&next, key, out)
|
||||
@@ -652,7 +652,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
continue
|
||||
}
|
||||
next.At = h.Now().UTC()
|
||||
raw, err := provisions.Encode(next)
|
||||
raw, err := meshprovisionerclient.Encode(next)
|
||||
if err != nil {
|
||||
out.Problems = append(out.Problems, err.Error())
|
||||
continue
|
||||
@@ -666,8 +666,8 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
||||
}
|
||||
|
||||
// held is the node's provision keys as kept.
|
||||
func (h *Holder) held(node string) map[string]provisions.Provision {
|
||||
out := map[string]provisions.Provision{}
|
||||
func (h *Holder) held(node string) map[string]meshprovisionerclient.Provision {
|
||||
out := map[string]meshprovisionerclient.Provision{}
|
||||
keys, err := h.Store.Keys(ProvisionsState)
|
||||
if err != nil {
|
||||
return out
|
||||
@@ -680,7 +680,7 @@ func (h *Holder) held(node string) map[string]provisions.Provision {
|
||||
if err != nil || e == nil {
|
||||
continue
|
||||
}
|
||||
if p, err := provisions.Decode(e.Value); err == nil {
|
||||
if p, err := meshprovisionerclient.Decode(e.Value); err == nil {
|
||||
out[k] = p
|
||||
}
|
||||
}
|
||||
@@ -688,7 +688,7 @@ func (h *Holder) held(node string) map[string]provisions.Provision {
|
||||
}
|
||||
|
||||
// same says two provisions say the same, their time aside.
|
||||
func same(a, b provisions.Provision) bool {
|
||||
func same(a, b meshprovisionerclient.Provision) bool {
|
||||
a.At, b.At, a.V, b.V = time.Time{}, time.Time{}, 0, 0
|
||||
ra, _ := json.Marshal(a)
|
||||
rb, _ := json.Marshal(b)
|
||||
@@ -710,11 +710,11 @@ type entry struct {
|
||||
// outcome sets a needed provision's state from its provider's entry: provided, failed or waits; asked while it has
|
||||
// none. The holder never sets provided by itself (clause 3): a provider whose entries it does not read keeps the key
|
||||
// asked, said so.
|
||||
func (h *Holder) outcome(key string, p *provisions.Provision) {
|
||||
func (h *Holder) outcome(key string, p *meshprovisionerclient.Provision) {
|
||||
p.Ask, p.Warrant = "", nil
|
||||
keeps, err := h.keepsEntries(p.Provider.Module, p.Provision)
|
||||
if err != nil {
|
||||
p.State, p.Why = provisions.Asked, "the manifest of "+p.Provider.Module+" cannot be read: "+err.Error()
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "the manifest of "+p.Provider.Module+" cannot be read: "+err.Error()
|
||||
return
|
||||
}
|
||||
if !keeps {
|
||||
@@ -722,40 +722,40 @@ func (h *Holder) outcome(key string, p *provisions.Provision) {
|
||||
return
|
||||
}
|
||||
if !slices.Contains(BoundReads, p.Provider.Module) {
|
||||
p.State, p.Why = provisions.Asked, p.Provider.Module+" keeps its outcome in its entries, which mesh-provisioner's "+
|
||||
p.State, p.Why = meshprovisionerclient.Asked, p.Provider.Module+" keeps its outcome in its entries, which mesh-provisioner's "+
|
||||
"manifest does not read yet"
|
||||
return
|
||||
}
|
||||
e, err := h.Store.Get(boundRead(p.Provider.Module), key)
|
||||
if err != nil {
|
||||
p.State, p.Why = provisions.Asked, "the provider's entries cannot be read: "+err.Error()
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "the provider's entries cannot be read: "+err.Error()
|
||||
return
|
||||
}
|
||||
if e == nil {
|
||||
p.State, p.Why = provisions.Asked, h.unread(p.Provider.Module)
|
||||
p.State, p.Why = meshprovisionerclient.Asked, h.unread(p.Provider.Module)
|
||||
return
|
||||
}
|
||||
var en entry
|
||||
if err := json.Unmarshal(e.Value, &en); err != nil {
|
||||
p.State, p.Why = provisions.Asked, "the provider's entry is not readable"
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "the provider's entry is not readable"
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case en.State == provisions.Failed:
|
||||
p.State, p.Why = provisions.Failed, orSaid(en.Why, en.Refused, "the provider said it failed")
|
||||
case en.State == provisions.Waits:
|
||||
p.State, p.Why = provisions.Waits, orSaid(en.Why, en.Waits, "the provider waits for a person")
|
||||
case en.State == meshprovisionerclient.Failed:
|
||||
p.State, p.Why = meshprovisionerclient.Failed, orSaid(en.Why, en.Refused, "the provider said it failed")
|
||||
case en.State == meshprovisionerclient.Waits:
|
||||
p.State, p.Why = meshprovisionerclient.Waits, orSaid(en.Why, en.Waits, "the provider waits for a person")
|
||||
case en.State == "" && en.Refused != "":
|
||||
p.State, p.Why = provisions.Failed, en.Refused
|
||||
p.State, p.Why = meshprovisionerclient.Failed, en.Refused
|
||||
case en.State == "" && en.Waits != "":
|
||||
p.State, p.Why = provisions.Waits, en.Waits
|
||||
case en.State != "" && en.State != provisions.Provided:
|
||||
p.State, p.Why = provisions.Asked, "the provider's entry says "+en.State+", which no state of a provision is"
|
||||
p.State, p.Why = meshprovisionerclient.Waits, en.Waits
|
||||
case en.State != "" && en.State != meshprovisionerclient.Provided:
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "the provider's entry says "+en.State+", which no state of a provision is"
|
||||
return
|
||||
default:
|
||||
p.State, p.Why = provisions.Provided, ""
|
||||
p.State, p.Why = meshprovisionerclient.Provided, ""
|
||||
if en.VaultName != "" || en.Generation > 0 {
|
||||
p.Reference = &provisions.Reference{ID: en.VaultName, Generation: en.Generation}
|
||||
p.Reference = &meshprovisionerclient.Reference{ID: en.VaultName, Generation: en.Generation}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -773,44 +773,44 @@ func (h *Holder) unread(module string) string {
|
||||
var decl struct {
|
||||
Reads []string `json:"reads"`
|
||||
}
|
||||
if json.Unmarshal([]byte(manifest), &decl) != nil || slices.Contains(decl.Reads, provisions.PlansRead) {
|
||||
if json.Unmarshal([]byte(manifest), &decl) != nil || slices.Contains(decl.Reads, meshprovisionerclient.PlansRead) {
|
||||
return ""
|
||||
}
|
||||
return module + " reads no plans of the seat (its manifest's reads do not name " + provisions.PlansRead +
|
||||
return module + " reads no plans of the seat (its manifest's reads do not name " + meshprovisionerclient.PlansRead +
|
||||
"), so its helper never answers this key"
|
||||
}
|
||||
|
||||
// haveOutcome is the outcome of a provider that makes no credential per consumer: its own have on its node (the lead's
|
||||
// reading of ADR 0570 clause 3, recorded as an insight). Running with health ok at the build its want names is
|
||||
// provided; failed is failed with the have's words; anything else is asked, waiting for the provider to run.
|
||||
func (h *Holder) haveOutcome(p *provisions.Provision) {
|
||||
func (h *Holder) haveOutcome(p *meshprovisionerclient.Provision) {
|
||||
where := p.Provider.Module + " on " + p.Provider.Node
|
||||
// It makes what the consumer needs only once all it depends on is provided (its credential first).
|
||||
for _, dep := range p.DependsOn {
|
||||
state := "not composed yet"
|
||||
if e, err := h.Store.Get(ProvisionsState, dep); err == nil && e != nil {
|
||||
if d, err := provisions.Decode(e.Value); err == nil {
|
||||
if d, err := meshprovisionerclient.Decode(e.Value); err == nil {
|
||||
state = d.State
|
||||
}
|
||||
}
|
||||
if state != provisions.Provided {
|
||||
p.State, p.Why = provisions.Asked, "waits for "+dep+" ("+state+") before "+where+" can provide it"
|
||||
if state != meshprovisionerclient.Provided {
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "waits for "+dep+" ("+state+") before "+where+" can provide it"
|
||||
return
|
||||
}
|
||||
}
|
||||
key := p.Provider.Node + "." + p.Provider.Module
|
||||
e, err := h.Store.Get(HaveRead, key)
|
||||
if err != nil {
|
||||
p.State, p.Why = provisions.Asked, "the have of "+where+" cannot be read: "+err.Error()
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "the have of "+where+" cannot be read: "+err.Error()
|
||||
return
|
||||
}
|
||||
if e == nil {
|
||||
p.State, p.Why = provisions.Asked, "waits for "+where+" to run: it says nothing yet"
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "waits for "+where+" to run: it says nothing yet"
|
||||
return
|
||||
}
|
||||
have, err := delivery.DecodeHave(e.Value)
|
||||
if err != nil {
|
||||
p.State, p.Why = provisions.Asked, "the have of "+where+" is not readable"
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "the have of "+where+" is not readable"
|
||||
return
|
||||
}
|
||||
wantFP := ""
|
||||
@@ -821,19 +821,19 @@ func (h *Holder) haveOutcome(p *provisions.Provision) {
|
||||
}
|
||||
switch {
|
||||
case have.Phase == delivery.PhaseFailed || have.Health == delivery.HealthFailed:
|
||||
p.State, p.Why = provisions.Failed, orSaid(have.Detail, where+" failed")
|
||||
p.State, p.Why = meshprovisionerclient.Failed, orSaid(have.Detail, where+" failed")
|
||||
case have.Phase == delivery.PhaseRunning && have.Health == delivery.HealthOK && (wantFP == "" || have.FP == wantFP):
|
||||
// A provider's have counts as its outcome only from a build whose manifest offers the provision (ADR 0603 clause 5).
|
||||
// Refused only when that manifest is known and does not offer it: a have with no builder key (an old-form build, an
|
||||
// adopted module) or an unreadable manifest keeps the outcome it had.
|
||||
if offers, known := h.buildOffers(p.Provider.Module, have.FP, p.Provision); known && !offers {
|
||||
p.State, p.Why = provisions.Asked, "waits for "+where+" to run a build that offers "+p.Provision+": the build it runs ("+
|
||||
p.State, p.Why = meshprovisionerclient.Asked, "waits for "+where+" to run a build that offers "+p.Provision+": the build it runs ("+
|
||||
have.FP+") does not"
|
||||
return
|
||||
}
|
||||
p.State, p.Why = provisions.Provided, ""
|
||||
p.State, p.Why = meshprovisionerclient.Provided, ""
|
||||
default:
|
||||
p.State, p.Why = provisions.Asked, fmt.Sprintf("waits for %s to run healthy at the build its want names (it is %s, "+
|
||||
p.State, p.Why = meshprovisionerclient.Asked, fmt.Sprintf("waits for %s to run healthy at the build its want names (it is %s, "+
|
||||
"health %s)", where, have.Phase, have.Health)
|
||||
}
|
||||
}
|
||||
@@ -975,7 +975,7 @@ func (h *Holder) providerManifest(module string) (string, error) {
|
||||
}
|
||||
|
||||
// retired says a retiring key the operator approved is retired: its provider's entry is gone.
|
||||
func (h *Holder) retired(key string, p provisions.Provision) (bool, string) {
|
||||
func (h *Holder) retired(key string, p meshprovisionerclient.Provision) (bool, string) {
|
||||
if p.Warrant == nil || p.Warrant.Option != OptionApprove {
|
||||
return false, ""
|
||||
}
|
||||
@@ -1003,7 +1003,7 @@ const (
|
||||
const askLasts = occ.ApproveLasts - 10*time.Minute
|
||||
|
||||
// retiringAsk is the ask of one retiring provision, composed from the key alone.
|
||||
func retiringAsk(key string, p provisions.Provision, at time.Time, id string) oc.Ask {
|
||||
func retiringAsk(key string, p meshprovisionerclient.Provision, at time.Time, id string) oc.Ask {
|
||||
what := fmt.Sprintf("%s's access to %s on %s, for %s on %s", p.Consumer.Module, p.Provision, p.Provider.Node,
|
||||
p.Consumer.Module, p.Consumer.Node)
|
||||
a := oc.Ask{ID: id, Headline: "Remove " + p.Consumer.Module + "'s " + p.Provision + " access?",
|
||||
@@ -1011,7 +1011,7 @@ func retiringAsk(key string, p provisions.Provision, at time.Time, id string) oc
|
||||
"is handed to the resource's owner and its credential goes; the resource and its data stay.", p.Consumer.Node,
|
||||
p.Provision, p.Provider.Module, what),
|
||||
Who: occ.Operator, Expires: occ.FormatTime(at.Add(askLasts)), OnExpiry: "nothing is removed; it is asked again when it changes",
|
||||
About: "provision." + key, Details: "The provision " + key + " of mesh-provisioner's state provisions."}
|
||||
About: "provision." + key, Details: "The provision " + key + " of mesh-provisioner's state meshprovisionerclient."}
|
||||
for _, o := range []struct{ id, label, does string }{
|
||||
{OptionApprove, "Remove", "the provider removes the credential; the resource and its data stay"},
|
||||
{OptionDecline, "Keep", "nothing is removed, and it is not asked again while nothing changes"},
|
||||
@@ -1041,7 +1041,7 @@ const askWaits = "its ask waits until fewer than 3 of mesh-provisioner's asks st
|
||||
var errAskWaits = errors.New(askWaits)
|
||||
|
||||
// raise asks a retiring key and says so in out: asked, waiting for an open ask to end, or a problem.
|
||||
func (h *Holder) raise(p *provisions.Provision, key string, out *Composed) {
|
||||
func (h *Holder) raise(p *meshprovisionerclient.Provision, key string, out *Composed) {
|
||||
switch err := h.ask(p, key); {
|
||||
case err == nil:
|
||||
out.Asked = append(out.Asked, key)
|
||||
@@ -1053,7 +1053,7 @@ func (h *Holder) raise(p *provisions.Provision, key string, out *Composed) {
|
||||
|
||||
// ask raises the retiring ask of a key, under mesh-provisioner's own name; one already raised stands. While
|
||||
// askOpenAtMost asks stand open it raises none and the key's why says so.
|
||||
func (h *Holder) ask(p *provisions.Provision, key string) error {
|
||||
func (h *Holder) ask(p *meshprovisionerclient.Provision, key string) error {
|
||||
if p.Ask != "" {
|
||||
return nil
|
||||
}
|
||||
@@ -1086,7 +1086,7 @@ func (h *Holder) openAsks() int {
|
||||
keys, _ := h.Store.Keys(ProvisionsState)
|
||||
for _, k := range keys {
|
||||
if e, err := h.Store.Get(ProvisionsState, k); err == nil && e != nil {
|
||||
if p, err := provisions.Decode(e.Value); err == nil && p.State == provisions.Retiring && p.Ask != "" &&
|
||||
if p, err := meshprovisionerclient.Decode(e.Value); err == nil && p.State == meshprovisionerclient.Retiring && p.Ask != "" &&
|
||||
p.Warrant == nil {
|
||||
n++
|
||||
}
|
||||
@@ -1097,7 +1097,7 @@ func (h *Holder) openAsks() int {
|
||||
|
||||
// askHeld says the router holds a key's ask, or may not have taken it yet: false only when it has no record of it
|
||||
// askHeldWithin after it was raised (issue 940: an ask the router refused was counted asked for good).
|
||||
func (h *Holder) askHeld(p provisions.Provision) bool {
|
||||
func (h *Holder) askHeld(p meshprovisionerclient.Provision) bool {
|
||||
if h.Asks == nil || p.Ask == "" {
|
||||
return true
|
||||
}
|
||||
@@ -1125,8 +1125,8 @@ func (h *Holder) Decide() []string {
|
||||
if err != nil || e == nil {
|
||||
continue
|
||||
}
|
||||
p, err := provisions.Decode(e.Value)
|
||||
if err != nil || p.State != provisions.Retiring || p.Ask == "" || p.Warrant != nil {
|
||||
p, err := meshprovisionerclient.Decode(e.Value)
|
||||
if err != nil || p.State != meshprovisionerclient.Retiring || p.Ask == "" || p.Warrant != nil {
|
||||
continue
|
||||
}
|
||||
raw, found, err := h.Asks.Record(p.Ask)
|
||||
@@ -1180,7 +1180,7 @@ func (h *Holder) Decide() []string {
|
||||
said = append(said, k+": the answer is refused: its time is not RFC 3339 ("+err.Error()+")")
|
||||
continue
|
||||
}
|
||||
p.Warrant = &provisions.Warrant{Ask: p.Ask, Option: option, By: by, At: answeredAt.UTC()}
|
||||
p.Warrant = &meshprovisionerclient.Warrant{Ask: p.Ask, Option: option, By: by, At: answeredAt.UTC()}
|
||||
h.put(k, p)
|
||||
said = append(said, k+": "+option+" by "+by)
|
||||
}
|
||||
@@ -1200,8 +1200,8 @@ func (h *Holder) raiseWaiting() []string {
|
||||
if err != nil || e == nil {
|
||||
continue
|
||||
}
|
||||
p, err := provisions.Decode(e.Value)
|
||||
if err != nil || p.State != provisions.Retiring || p.Ask != "" || p.Warrant != nil || !strings.Contains(p.Why, askWaits) {
|
||||
p, err := meshprovisionerclient.Decode(e.Value)
|
||||
if err != nil || p.State != meshprovisionerclient.Retiring || p.Ask != "" || p.Warrant != nil || !strings.Contains(p.Why, askWaits) {
|
||||
continue
|
||||
}
|
||||
switch err := h.ask(&p, k); {
|
||||
@@ -1297,9 +1297,9 @@ func (h *Holder) LastingHolds() map[string]string {
|
||||
return out
|
||||
}
|
||||
|
||||
func (h *Holder) put(key string, p provisions.Provision) {
|
||||
func (h *Holder) put(key string, p meshprovisionerclient.Provision) {
|
||||
p.At = h.Now().UTC()
|
||||
if raw, err := provisions.Encode(p); err == nil {
|
||||
if raw, err := meshprovisionerclient.Encode(p); err == nil {
|
||||
if err := h.Store.Put(ProvisionsState, key, json.RawMessage(raw)); err != nil {
|
||||
h.Logf("[%s] %s is not written: %v", Module, key, err)
|
||||
}
|
||||
@@ -1334,7 +1334,7 @@ func (h *Holder) NodesOfModule(module string) []string {
|
||||
if keys, err := h.Store.Keys(ProvisionsState); err == nil {
|
||||
for _, k := range keys {
|
||||
if e, err := h.Store.Get(ProvisionsState, k); err == nil && e != nil {
|
||||
if p, err := provisions.Decode(e.Value); err == nil && p.Provider.Module == module {
|
||||
if p, err := meshprovisionerclient.Decode(e.Value); err == nil && p.Provider.Module == module {
|
||||
nodes[p.Consumer.Node] = true
|
||||
}
|
||||
}
|
||||
@@ -1368,7 +1368,7 @@ func (h *Holder) placedOn(module string) []string {
|
||||
|
||||
// NodeOfEntry is the consumer's node a provider's entry key names.
|
||||
func NodeOfEntry(key string) []string {
|
||||
if n, _, _, _, err := provisions.ParseKey(key); err == nil {
|
||||
if n, _, _, _, err := meshprovisionerclient.ParseKey(key); err == nil {
|
||||
return []string{n}
|
||||
}
|
||||
return nil
|
||||
@@ -1403,11 +1403,11 @@ func (h *Holder) Rejudge(key string) (string, error) {
|
||||
if err != nil || e == nil {
|
||||
return "", err
|
||||
}
|
||||
have, err := provisions.Decode(e.Value)
|
||||
have, err := meshprovisionerclient.Decode(e.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if have.State == provisions.Retiring {
|
||||
if have.State == meshprovisionerclient.Retiring {
|
||||
if gone, why := h.retired(key, have); gone {
|
||||
return key + " retired: " + why, h.Store.Delete(ProvisionsState, key)
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||
)
|
||||
|
||||
@@ -132,9 +132,9 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
published(t, mem, c)
|
||||
vaultManifest := `{"module":"mesh-vault","version":"1","state":["bound"]}` // the vault keeps its outcomes as entries
|
||||
vaultDigest := delivery.ManifestDigest([]byte(vaultManifest))
|
||||
vaultValue, _ := delivery.EncodeManifest(delivery.Manifest{Module: provisions.VaultModule, JSON: vaultManifest})
|
||||
vaultValue, _ := delivery.EncodeManifest(delivery.Manifest{Module: meshprovisionerclient.VaultModule, JSON: vaultManifest})
|
||||
mem.write(t, ManifestsRead, mustKey(delivery.ManifestsKey(vaultDigest)), string(vaultValue))
|
||||
mem.write(t, ModulesRead, provisions.VaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`)
|
||||
mem.write(t, ModulesRead, meshprovisionerclient.VaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`)
|
||||
for state, keys := range mem.states {
|
||||
for key, raw := range keys {
|
||||
if err := s.Put(state, key, json.RawMessage(raw)); err != nil {
|
||||
@@ -195,19 +195,19 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
a := &asked{}
|
||||
h := &Holder{Store: Watching(s, ws), Asks: a, Now: time.Now, Logf: t.Logf}
|
||||
h.Compose = FromCompositions(h.Store)
|
||||
state := func(key string) provisions.Provision {
|
||||
state := func(key string) meshprovisionerclient.Provision {
|
||||
e, _ := s.Get(ProvisionsState, key)
|
||||
if e == nil {
|
||||
return provisions.Provision{}
|
||||
return meshprovisionerclient.Provision{}
|
||||
}
|
||||
p, _ := provisions.Decode(e.Value)
|
||||
p, _ := meshprovisionerclient.Decode(e.Value)
|
||||
return p
|
||||
}
|
||||
key := node + ".restic.postgres-database"
|
||||
if _, err := h.ComposeNode(ctx, node); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p := state(key); p.State != provisions.Asked || p.Provider.Module != "postgres" {
|
||||
if p := state(key); p.State != meshprovisionerclient.Asked || p.Provider.Module != "postgres" {
|
||||
t.Fatalf("the placement and the want composed %+v", p)
|
||||
}
|
||||
if e, _ := s.Get(PlansState, node+".restic"); e == nil {
|
||||
@@ -215,7 +215,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
}
|
||||
// The providers' outcomes: the vault provides the credential where restic has one, postgres runs healthy.
|
||||
for _, dep := range state(key).DependsOn {
|
||||
if err := s.Put(boundRead(provisions.VaultModule), dep, map[string]any{"state": "provided"}); err != nil {
|
||||
if err := s.Put(boundRead(meshprovisionerclient.VaultModule), dep, map[string]any{"state": "provided"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -248,7 +248,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if p := state(key); p.State != provisions.Asked {
|
||||
if p := state(key); p.State != meshprovisionerclient.Asked {
|
||||
t.Fatalf("a build not offering the provision turned it %s", p.State)
|
||||
}
|
||||
offered(`["postgres-database"]`)
|
||||
@@ -257,7 +257,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if p := state(key); p.State != provisions.Provided {
|
||||
if p := state(key); p.State != meshprovisionerclient.Provided {
|
||||
t.Fatalf("the outcomes did not turn it provided: %+v", p)
|
||||
}
|
||||
// restic leaves the node and its want goes: retiring, one ask. Its compositions stay until its engine deletes them.
|
||||
@@ -273,7 +273,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
||||
if _, err := h.ComposeNode(ctx, node); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r := state(key); r.State != provisions.Retiring || r.Ask == "" || len(a.ids) == 0 {
|
||||
if r := state(key); r.State != meshprovisionerclient.Retiring || r.Ask == "" || len(a.ids) == 0 {
|
||||
t.Fatalf("removing the want did not ask: %+v, asked %v", r, a.ids)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ import (
|
||||
|
||||
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
|
||||
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
)
|
||||
|
||||
// router is the operator's channel as the holder asks on it: it holds an ask open, or, like the router past its open
|
||||
@@ -114,13 +114,13 @@ var (
|
||||
split = catalogue.PlanProvision{Provision: "split-dns", Provider: "g14", ProviderModule: "systemd-resolved"}
|
||||
)
|
||||
|
||||
func (m *mesh) state(t *testing.T, key string) provisions.Provision {
|
||||
func (m *mesh) state(t *testing.T, key string) meshprovisionerclient.Provision {
|
||||
t.Helper()
|
||||
e, _ := m.s.Get(ProvisionsState, key)
|
||||
if e == nil {
|
||||
return provisions.Provision{}
|
||||
return meshprovisionerclient.Provision{}
|
||||
}
|
||||
p, err := provisions.Decode(e.Value)
|
||||
p, err := meshprovisionerclient.Decode(e.Value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -150,11 +150,11 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
||||
state string
|
||||
why string
|
||||
}{
|
||||
{"composed: asked", "", provisions.Asked, ""},
|
||||
{"the provider waits for a person", `{"waits":"the database's owner to accept"}`, provisions.Waits, "owner"},
|
||||
{"the provider refused", `{"refused":"no room on ace"}`, provisions.Failed, "no room"},
|
||||
{"the provider says failed in its state", `{"state":"failed","why":"the disk is full"}`, provisions.Failed, "disk"},
|
||||
{"the provider made it", `{"state":"provided","vault-name":"pair/gitea-postgres","generation":3}`, provisions.Provided, ""},
|
||||
{"composed: asked", "", meshprovisionerclient.Asked, ""},
|
||||
{"the provider waits for a person", `{"waits":"the database's owner to accept"}`, meshprovisionerclient.Waits, "owner"},
|
||||
{"the provider refused", `{"refused":"no room on ace"}`, meshprovisionerclient.Failed, "no room"},
|
||||
{"the provider says failed in its state", `{"state":"failed","why":"the disk is full"}`, meshprovisionerclient.Failed, "disk"},
|
||||
{"the provider made it", `{"state":"provided","vault-name":"pair/gitea-postgres","generation":3}`, meshprovisionerclient.Provided, ""},
|
||||
}
|
||||
for _, s := range steps {
|
||||
if s.entry != "" {
|
||||
@@ -173,11 +173,11 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
||||
// holder alone.
|
||||
split := "g14.gitea.split-dns"
|
||||
for _, row := range []struct{ have, want, state, why string }{
|
||||
{"", "", provisions.Asked, "says nothing yet"},
|
||||
{`{"v":1,"fp":"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","phase":"applying","health":"unknown"}`, "", provisions.Asked, "applying"},
|
||||
{`{"v":1,"fp":"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","phase":"running","health":"ok"}`, `{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","self":true}`, provisions.Asked, "build its want"},
|
||||
{`{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","phase":"failed","health":"failed","detail":"the unit did not start"}`, "", provisions.Failed, "did not start"},
|
||||
{`{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","phase":"running","health":"ok"}`, `{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","self":true}`, provisions.Provided, ""},
|
||||
{"", "", meshprovisionerclient.Asked, "says nothing yet"},
|
||||
{`{"v":1,"fp":"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","phase":"applying","health":"unknown"}`, "", meshprovisionerclient.Asked, "applying"},
|
||||
{`{"v":1,"fp":"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","phase":"running","health":"ok"}`, `{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","self":true}`, meshprovisionerclient.Asked, "build its want"},
|
||||
{`{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","phase":"failed","health":"failed","detail":"the unit did not start"}`, "", meshprovisionerclient.Failed, "did not start"},
|
||||
{`{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","phase":"running","health":"ok"}`, `{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","self":true}`, meshprovisionerclient.Provided, ""},
|
||||
} {
|
||||
if row.have != "" {
|
||||
m.s.write(t, HaveRead, "g14.systemd-resolved", row.have)
|
||||
@@ -195,8 +195,8 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
||||
bfp := "src2:" + strings.Repeat("bb", 32)
|
||||
buildKey := mustKey(delivery.BuilderBuildKey("systemd-resolved", bfp))
|
||||
for _, row := range []struct{ name, provides, state, why string }{
|
||||
{"a build whose manifest does not offer it", `["other"]`, provisions.Asked, "offers split-dns"},
|
||||
{"a build whose manifest offers it", `[{"name":"split-dns"}]`, provisions.Provided, ""},
|
||||
{"a build whose manifest does not offer it", `["other"]`, meshprovisionerclient.Asked, "offers split-dns"},
|
||||
{"a build whose manifest offers it", `[{"name":"split-dns"}]`, meshprovisionerclient.Provided, ""},
|
||||
} {
|
||||
raw := `{"module":"systemd-resolved","version":"1","provides":` + row.provides + `}`
|
||||
digest := delivery.ManifestDigest([]byte(raw))
|
||||
@@ -238,7 +238,7 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
||||
c.atNewest["g14"] = c.atWant["g14"]
|
||||
c.compose(t, "g14")
|
||||
cred := c.state(t, "g14.gitea.secret-postgres-database")
|
||||
if cred.Provider.Module != "mesh-vault" || cred.Provider.Node != "novox" || cred.State != provisions.Asked {
|
||||
if cred.Provider.Module != "mesh-vault" || cred.Provider.Node != "novox" || cred.State != meshprovisionerclient.Asked {
|
||||
t.Errorf("the credential's provision is %+v", cred)
|
||||
}
|
||||
if d := c.state(t, key).DependsOn; !slices.Equal(d, []string{"g14.gitea.secret-postgres-database"}) {
|
||||
@@ -254,9 +254,9 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
||||
for _, row := range []struct {
|
||||
name, entry, cred, state string
|
||||
}{
|
||||
{"its credential still asked", "", provisions.Asked, provisions.Asked},
|
||||
{"its credential still asked", "", meshprovisionerclient.Asked, meshprovisionerclient.Asked},
|
||||
{"its credential provided by the vault", `{"state":"provided","vault-name":"pair/g14/s3-bucket/g14/gitea","generation":1}`,
|
||||
provisions.Provided, provisions.Provided},
|
||||
meshprovisionerclient.Provided, meshprovisionerclient.Provided},
|
||||
} {
|
||||
if row.entry != "" {
|
||||
c.s.write(t, "mesh-vault.bound", s3cred, row.entry)
|
||||
@@ -270,7 +270,7 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
||||
// An entry's change judges its key alone, no composition.
|
||||
before := len(m.composed)
|
||||
m.s.write(t, "postgres.bound", key, `{"refused":"the login is locked"}`)
|
||||
if said, err := m.h.Rejudge(key); err != nil || !strings.Contains(said, provisions.Failed) || len(m.composed) != before {
|
||||
if said, err := m.h.Rejudge(key); err != nil || !strings.Contains(said, meshprovisionerclient.Failed) || len(m.composed) != before {
|
||||
t.Errorf("rejudged %q, %v, composed %d more", said, err, len(m.composed)-before)
|
||||
}
|
||||
}
|
||||
@@ -293,17 +293,17 @@ func TestRetiringIsAskedAndActsOnlyOnTheWarrant(t *testing.T) {
|
||||
removed bool
|
||||
}{
|
||||
{"a key composed ahead of its want is asked, not retiring", func() {}, nil,
|
||||
map[string]string{pg: provisions.Provided, kc: provisions.Asked}, false},
|
||||
map[string]string{pg: meshprovisionerclient.Provided, kc: meshprovisionerclient.Asked}, false},
|
||||
{"one requirement removed and another added: only the removed retires", func() {
|
||||
m.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc)}
|
||||
m.atNewest["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc)}
|
||||
}, []string{pg}, map[string]string{pg: provisions.Retiring, kc: provisions.Asked}, false},
|
||||
{"composed again: no second ask", func() {}, nil, map[string]string{pg: provisions.Retiring}, false},
|
||||
}, []string{pg}, map[string]string{pg: meshprovisionerclient.Retiring, kc: meshprovisionerclient.Asked}, false},
|
||||
{"composed again: no second ask", func() {}, nil, map[string]string{pg: meshprovisionerclient.Retiring}, false},
|
||||
{"needed again before the warrant: back to its outcome", func() {
|
||||
m.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc, pgDB)}
|
||||
}, nil, map[string]string{pg: provisions.Provided}, false},
|
||||
}, nil, map[string]string{pg: meshprovisionerclient.Provided}, false},
|
||||
{"gone again: asked again", func() { m.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc)} },
|
||||
[]string{pg}, map[string]string{pg: provisions.Retiring}, false},
|
||||
[]string{pg}, map[string]string{pg: meshprovisionerclient.Retiring}, false},
|
||||
}
|
||||
for _, s := range steps {
|
||||
s.change()
|
||||
@@ -336,7 +336,7 @@ func TestRetiringIsAskedAndActsOnlyOnTheWarrant(t *testing.T) {
|
||||
if w := m.state(t, pg).Warrant; w.Option != "yes" {
|
||||
t.Fatalf("the warrant says %q, and a provider lets go only on \"yes\"", w.Option)
|
||||
}
|
||||
if said, _ := m.h.Rejudge(pg); said != "" || m.state(t, pg).State != provisions.Retiring {
|
||||
if said, _ := m.h.Rejudge(pg); said != "" || m.state(t, pg).State != meshprovisionerclient.Retiring {
|
||||
t.Errorf("retired while the provider's entry stands: %q", said)
|
||||
}
|
||||
_ = m.s.Delete("postgres.bound", pg)
|
||||
@@ -397,7 +397,7 @@ func TestOneBadModuleDoesNotDropItsNode(t *testing.T) {
|
||||
if e, _ := m.s.Get(PlansState, "g14.nats"); e == nil {
|
||||
t.Error("the held module's last plan was removed")
|
||||
}
|
||||
if p := m.state(t, "g14.nats.oidc-client"); p.State != provisions.Asked {
|
||||
if p := m.state(t, "g14.nats.oidc-client"); p.State != meshprovisionerclient.Asked {
|
||||
t.Errorf("the held module's provision is %s (%s), not kept", p.State, p.Why)
|
||||
}
|
||||
got, err := m.h.read(RefusedState, "", "g14", "")
|
||||
@@ -539,21 +539,21 @@ func TestAProvidersOutcomeIsReadPerOffer(t *testing.T) {
|
||||
}{
|
||||
{"gitea-read: the helper's refusal is not its outcome; gitea runs, so provided", "ace",
|
||||
map[string]catalogue.Plan{"claude-code": planOf(catalogue.PlanProvision{Provision: "gitea-read", Provider: "novox", ProviderModule: "gitea"})},
|
||||
"ace.claude-code.gitea-read", "gitea.bound", refused, "novox.gitea", provisions.Provided, ""},
|
||||
"ace.claude-code.gitea-read", "gitea.bound", refused, "novox.gitea", meshprovisionerclient.Provided, ""},
|
||||
{"forge-account: still the helper's entry", "ace",
|
||||
map[string]catalogue.Plan{"mesh-build-go": planOf(catalogue.PlanProvision{Provision: "forge-account", Provider: "novox", ProviderModule: "gitea"})},
|
||||
"ace.mesh-build-go.forge-account", "gitea.bound", `{"state":"provided"}`, "", provisions.Provided, ""},
|
||||
"ace.mesh-build-go.forge-account", "gitea.bound", `{"state":"provided"}`, "", meshprovisionerclient.Provided, ""},
|
||||
{"route: route-proxy's entry is read", "novox",
|
||||
map[string]catalogue.Plan{"gitea": planOf(catalogue.PlanProvision{Provision: "route", Provider: "novox", ProviderModule: "route-proxy"})},
|
||||
"novox.gitea.route", "route-proxy.bound", `{"state":"provided"}`, "", provisions.Provided, ""},
|
||||
"novox.gitea.route", "route-proxy.bound", `{"state":"provided"}`, "", meshprovisionerclient.Provided, ""},
|
||||
{"mcp-endpoint: node-tools' entry is read", "ace",
|
||||
map[string]catalogue.Plan{"claude-code": planOf(catalogue.PlanProvision{Provision: "mcp-endpoint", Provider: "ace", ProviderModule: "node-tools"})},
|
||||
"ace.claude-code.mcp-endpoint", "node-tools.bound", `{"state":"provided"}`, "", provisions.Provided, ""},
|
||||
"ace.claude-code.mcp-endpoint", "node-tools.bound", `{"state":"provided"}`, "", meshprovisionerclient.Provided, ""},
|
||||
// Issue 774, 2026-10-31: route-proxy's helper read the controller's assignments, never the seat's plans, so 38
|
||||
// routes the seat keys stood asked with no why while the assigner withheld their consumers' builds.
|
||||
{"route: no entry from a provider that reads no plans of the seat is said so", "ace",
|
||||
map[string]catalogue.Plan{"grafana": planOf(catalogue.PlanProvision{Provision: "route", Provider: "ace", ProviderModule: "route-proxy"})},
|
||||
"ace.grafana.route", "", "", "", provisions.Asked, "reads no plans of the seat"},
|
||||
"ace.grafana.route", "", "", "", meshprovisionerclient.Asked, "reads no plans of the seat"},
|
||||
} {
|
||||
m := newMesh()
|
||||
m.manifestOf("gitea", gitea)
|
||||
@@ -588,7 +588,7 @@ func TestAKeyAheadOfItsWantCarriesItsPlanEntryAndSaysWhy(t *testing.T) {
|
||||
if kc.Plan == nil || kc.Plan.Provision != "oidc-client" || kc.Plan.ProviderModule != "keycloak" || kc.Plan.As != "gitea-g14" {
|
||||
t.Errorf("the key ahead of its want carries %+v", kc.Plan)
|
||||
}
|
||||
if kc.State != provisions.Asked || !strings.Contains(kc.Why, "no plan names it") {
|
||||
if kc.State != meshprovisionerclient.Asked || !strings.Contains(kc.Why, "no plan names it") {
|
||||
t.Errorf("the key ahead of its want: %s %q", kc.State, kc.Why)
|
||||
}
|
||||
// A key a plan names carries no entry: a reader on an older mesh-sdk, which refused a field it did not know, reads it
|
||||
@@ -712,7 +712,7 @@ func TestARetirementIsJudgedOnlyFromAWholePicture(t *testing.T) {
|
||||
}
|
||||
retiring := 0
|
||||
for i := range row.consumer {
|
||||
if m.state(t, "novox."+apps[i]+".route.1").State == provisions.Retiring {
|
||||
if m.state(t, "novox."+apps[i]+".route.1").State == meshprovisionerclient.Retiring {
|
||||
retiring++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
)
|
||||
|
||||
// The holder's tools.
|
||||
@@ -133,7 +133,7 @@ func (h *Holder) read(state, key, node, want string) (any, error) {
|
||||
}
|
||||
row := map[string]any{"key": k}
|
||||
if state == ProvisionsState {
|
||||
p, err := provisions.Decode(e.Value)
|
||||
p, err := meshprovisionerclient.Decode(e.Value)
|
||||
if err != nil {
|
||||
row["unreadable"] = err.Error()
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@ module mesh-provisioner
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient v0.0.0-00010101000000-000000000000
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -78,14 +78,14 @@ const ProvisionerPrefix = "provisioner."
|
||||
// seat mesh-provisioner's state `plans`, `<node>.<module>` (novox/hq ADR 0570 clause 2), no longer the controller's
|
||||
// assignments. A plan is read as the assignment's value carried it (asAssignment), so the rules that read a plan are
|
||||
// unchanged.
|
||||
const AssignmentsRead = provisions.PlansRead
|
||||
const AssignmentsRead = meshprovisionerclient.PlansRead
|
||||
|
||||
// asAssignment is a plan's key in the form the vault's rules read: `{node, module, provisioning: plan}`, nil for none.
|
||||
func asAssignment(key string, raw []byte, err error) ([]byte, error) {
|
||||
if err != nil || raw == nil {
|
||||
return raw, err
|
||||
}
|
||||
p, err := provisions.DecodePlan(raw)
|
||||
p, err := meshprovisionerclient.DecodePlan(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the plan %s: %w", key, err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
@@ -79,7 +80,7 @@ func build() *Env {
|
||||
Delete: func(key string) error { return stdio.State(BoundState).Delete(key) },
|
||||
Keys: func() ([]string, error) { return stdio.State(BoundState).Keys() },
|
||||
Provision: func(key string) ([]byte, error) {
|
||||
e, err := stdio.State(provisions.ProvisionsRead).Get(key)
|
||||
e, err := stdio.State(meshprovisionerclient.ProvisionsRead).Get(key)
|
||||
if err != nil || e == nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -122,11 +123,8 @@ func watchLeavers(env *Env) bool {
|
||||
if node, ok := leaverKey(key); ok {
|
||||
env.Vault.NodeLeft(node)
|
||||
}
|
||||
}, env.Vault.PlacementChanged, func(key string, v json.RawMessage) error {
|
||||
_, err := stdio.State(FindingsState).Put(key, v)
|
||||
return err
|
||||
},
|
||||
func(key string) error { return stdio.State(FindingsState).Delete(key) })
|
||||
}, env.Vault.PlacementChanged,
|
||||
meshconditionsclient.FindingWriter{State: stdio.State(FindingsState)})
|
||||
beginPlacementView(placementView)
|
||||
}
|
||||
return true
|
||||
@@ -222,7 +220,7 @@ func watchCredentialAsks(env *Env, kick chan<- struct{}) bool {
|
||||
if env.Vault == nil {
|
||||
return true
|
||||
}
|
||||
err := stdio.State(provisions.ProvisionsRead).Watch("", func(c stdio.StateChange) error {
|
||||
err := stdio.State(meshprovisionerclient.ProvisionsRead).Watch("", func(c stdio.StateChange) error {
|
||||
if !c.Current && credentialAsked(c.Value) {
|
||||
select {
|
||||
case kick <- struct{}{}:
|
||||
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
// The vault's outcome for each credential it provides (novox/hq ADR 0570 clauses 3, 6, 7): an entry in its own state
|
||||
// `bound`, one per credential provision `<node>.<consumer>.secret-<provision>[.<local>]`, which the holder of
|
||||
// mesh-provisioner reads to set that key's state. The entry names the value by the vault's own name for it
|
||||
// (provisions.VaultName, as its verb `own` names it) and its generation, never the value. A value the vault holds is
|
||||
// (meshprovisionerclient.VaultName, as its verb `own` names it) and its generation, never the value. A value the vault holds is
|
||||
// provided; one only the provider's software issues, or only a person gives, waits for them; one the vault has not
|
||||
// made yet has no entry, and the key stays asked. An offer whose form is none has no credential and no entry. An entry
|
||||
// is let go only once its key is retiring with the operator's warrant, or gone.
|
||||
@@ -14,13 +14,13 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
)
|
||||
|
||||
// BoundState is the vault's state of outcome entries.
|
||||
const BoundState = "bound"
|
||||
|
||||
// Bound is the vault's own state `bound`, and what it reads of the seat's provisions.
|
||||
// Bound is the vault's own state `bound`, and what it reads of the seat's meshprovisionerclient.
|
||||
type Bound struct {
|
||||
Get func(key string) ([]byte, error)
|
||||
Put func(key string, value []byte) error
|
||||
@@ -96,8 +96,8 @@ func (v *Vault) letGo(key string) bool {
|
||||
if raw == nil {
|
||||
return true
|
||||
}
|
||||
p, err := provisions.Decode(raw)
|
||||
return err == nil && p.State == provisions.Retiring && p.Warrant != nil && p.Warrant.Option == "approve"
|
||||
p, err := meshprovisionerclient.Decode(raw)
|
||||
return err == nil && p.State == meshprovisionerclient.Retiring && p.Warrant != nil && p.Warrant.Option == "approve"
|
||||
}
|
||||
|
||||
// outcomes is every credential's outcome the plans call for, by its key.
|
||||
@@ -119,42 +119,42 @@ func (v *Vault) outcomes() (map[string]BoundOutcome, error) {
|
||||
if err := json.Unmarshal(raw, &a); err != nil || a.Provisioning == nil { // binds, not handing
|
||||
continue
|
||||
}
|
||||
consumer := provisions.End{Module: a.Module, Node: a.Node}
|
||||
consumer := meshprovisionerclient.End{Module: a.Module, Node: a.Node}
|
||||
for _, p := range a.Provisioning.Provisions {
|
||||
provider := provisions.End{Module: p.ProviderModule, Node: p.Provider}
|
||||
if p.ProviderModule == provisions.VaultModule && p.Provision == "secret" {
|
||||
provider := meshprovisionerclient.End{Module: p.ProviderModule, Node: p.Provider}
|
||||
if p.ProviderModule == meshprovisionerclient.VaultModule && p.Provision == "secret" {
|
||||
// The vault's own offer: a value it hands the consumer's client on request (`get`), which it serves as
|
||||
// soon as it runs.
|
||||
key, err := provisions.Key(a.Node, a.Module, p.Provision, p.Local)
|
||||
key, err := meshprovisionerclient.Key(a.Node, a.Module, p.Provision, p.Local)
|
||||
if err == nil {
|
||||
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: provisions.VaultModule, Provision: p.Provision,
|
||||
Consumer: a.Node, Module: a.Module, Local: p.Local, State: provisions.Provided,
|
||||
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: p.Provision,
|
||||
Consumer: a.Node, Module: a.Module, Local: p.Local, State: meshprovisionerclient.Provided,
|
||||
Why: "the vault hands it to the consumer's client on request"}
|
||||
}
|
||||
continue
|
||||
}
|
||||
ck, ok := provisions.CredentialKey(p.CredentialForm, consumer, provider, p.Provision, p.Local)
|
||||
ck, ok := meshprovisionerclient.CredentialKey(p.CredentialForm, consumer, provider, p.Provision, p.Local)
|
||||
if !ok {
|
||||
continue // form none: no credential, nothing minted
|
||||
}
|
||||
parent := provisions.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider}
|
||||
name := provisions.VaultName(p.CredentialForm, parent)
|
||||
cn, cm, cp, cl, err := provisions.ParseKey(ck)
|
||||
parent := meshprovisionerclient.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider}
|
||||
name := meshprovisionerclient.VaultName(p.CredentialForm, parent)
|
||||
cn, cm, cp, cl, err := meshprovisionerclient.ParseKey(ck)
|
||||
if err != nil || name == "" {
|
||||
continue
|
||||
}
|
||||
o := BoundOutcome{Provider: p.Provider, ProviderModule: provisions.VaultModule, Provision: cp, Consumer: cn,
|
||||
o := BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: cp, Consumer: cn,
|
||||
Module: cm, Local: cl, VaultName: name}
|
||||
rec, held, err := v.Store.Read(name)
|
||||
switch {
|
||||
case err != nil:
|
||||
o.State, o.Why = provisions.Failed, "the vault cannot read "+name+": "+err.Error()
|
||||
o.State, o.Why = meshprovisionerclient.Failed, "the vault cannot read "+name+": "+err.Error()
|
||||
case held:
|
||||
o.State, o.Generation = provisions.Provided, rec.Generation
|
||||
o.State, o.Generation = meshprovisionerclient.Provided, rec.Generation
|
||||
case p.CredentialForm == FormIssued:
|
||||
o.State, o.Why = provisions.Waits, "the provider's software issues it (ADR 0305): `issued` brings it in"
|
||||
o.State, o.Why = meshprovisionerclient.Waits, "the provider's software issues it (ADR 0305): `issued` brings it in"
|
||||
case p.CredentialForm == FormGiven:
|
||||
o.State, o.Why = provisions.Waits, "a person gives it (ADR 0337): `accept` brings it in"
|
||||
o.State, o.Why = meshprovisionerclient.Waits, "a person gives it (ADR 0337): `accept` brings it in"
|
||||
default:
|
||||
continue // not made yet: the key stays asked until the pair pass makes it
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
||||
)
|
||||
|
||||
@@ -865,7 +865,7 @@ func (e *Env) credentialsState() []PairOutcome {
|
||||
// credentialAsked is whether a key of the seat mesh-provisioner's provisions asks the vault for a credential (ADR 0570
|
||||
// §1, §6): a provision `secret-<provision>` whose provider is the vault, in state asked.
|
||||
func credentialAsked(raw []byte) bool {
|
||||
p, err := provisions.Decode(raw)
|
||||
return err == nil && p.State == provisions.Asked && p.Provider.Module == provisions.VaultModule &&
|
||||
p, err := meshprovisionerclient.Decode(raw)
|
||||
return err == nil && p.State == meshprovisionerclient.Asked && p.Provider.Module == meshprovisionerclient.VaultModule &&
|
||||
strings.HasPrefix(p.Provision, catalogue.CredentialProvisionPrefix)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ var placementView *views.Follower
|
||||
// newPlacementView makes the follower: ask calls the tool, gone is told of a deleted key, changed of every change after
|
||||
// it is held (a first view's keys too).
|
||||
func newPlacementView(ask func(key string, body any) (json.RawMessage, error), gone func(key string), changed func(),
|
||||
put func(key string, value json.RawMessage) error, drop func(key string) error) *views.Follower {
|
||||
findings views.FindingWriter) *views.Follower {
|
||||
return &views.Follower{Owner: delivery.AssignerModule, Tool: PlacementTool, State: "placement", Logf: say,
|
||||
Ask: func(_ context.Context, after string) (views.Answer, error) {
|
||||
body := map[string]any{"values": true}
|
||||
@@ -55,16 +55,13 @@ func newPlacementView(ask func(key string, body any) (json.RawMessage, error), g
|
||||
Raise: func(s views.Status) {
|
||||
say("the vault has no first view of the assigner's placement: %s has not answered since %s (%s); it keeps asking", s.Tool,
|
||||
s.Asked.Format(time.RFC3339), s.Error)
|
||||
if k, v, err := views.UnansweredFinding(Seat, s, time.Now()); err == nil {
|
||||
if err := put(k, v); err != nil {
|
||||
say("the finding %s is not written: %v", k, err)
|
||||
}
|
||||
f := views.UnansweredFinding(Seat, s, time.Now())
|
||||
if err := findings.Put(f); err != nil {
|
||||
say("the finding %s is not written: %v", f.ID, err)
|
||||
}
|
||||
},
|
||||
Clear: func(s views.Status) {
|
||||
if k, _, err := views.UnansweredFinding(Seat, s, time.Now()); err == nil {
|
||||
_ = drop(k)
|
||||
}
|
||||
_ = findings.Delete(views.UnansweredFinding(Seat, s, time.Now()))
|
||||
},
|
||||
Changed: func(key string, _ json.RawMessage, deleted bool, _ time.Time) {
|
||||
if deleted {
|
||||
|
||||
@@ -28,9 +28,7 @@ func TestAnUnansweredFirstViewOfThePlacementIsAFindingTheAnswerClears(t *testing
|
||||
Values: map[string]json.RawMessage{"g14.node": json.RawMessage(`{}`)}})
|
||||
return raw, nil
|
||||
}
|
||||
put := func(k string, v json.RawMessage) error { mu.Lock(); held[k] = v; mu.Unlock(); return nil }
|
||||
drop := func(k string) error { mu.Lock(); delete(held, k); mu.Unlock(); return nil }
|
||||
f := newPlacementView(ask, func(string) {}, nil, put, drop)
|
||||
f := newPlacementView(ask, func(string) {}, nil, heldFindings{&mu, held})
|
||||
f.Bound, f.Wait = 30*time.Millisecond, 10*time.Millisecond
|
||||
reader := viewPlacement(func() *views.Follower { return f })
|
||||
if _, err := reader.Keys(); err == nil {
|
||||
@@ -57,3 +55,22 @@ func TestAnUnansweredFirstViewOfThePlacementIsAFindingTheAnswerClears(t *testing
|
||||
t.Errorf("keys %v, %v", keys, err)
|
||||
}
|
||||
}
|
||||
|
||||
// heldFindings keeps the findings written, by the finding's kind and subject.
|
||||
type heldFindings struct {
|
||||
mu *sync.Mutex
|
||||
held map[string]json.RawMessage
|
||||
}
|
||||
|
||||
func (h heldFindings) Put(f views.Finding) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.held[f.ID+"."+f.Kind] = json.RawMessage(f.Summary)
|
||||
return nil
|
||||
}
|
||||
func (h heldFindings) Delete(f views.Finding) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
delete(h.held, f.ID+"."+f.Kind)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -196,16 +196,15 @@ func TestNoBucketHoldsASecret(t *testing.T) {
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(WantedRead).Watch(", "", 1))
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(WantedRead).Get(", "", 1))
|
||||
// And its findings (ADR 0615 §6): the finding of a first view unanswered, written and deleted, never a secret.
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(FindingsState).Put(", "", 1))
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(FindingsState).Delete(", "", 1))
|
||||
code = []byte(strings.Replace(string(code), "meshconditionsclient.FindingWriter{State: stdio.State(FindingsState)}", "", 1))
|
||||
// And its outcome entries (novox/hq ADR 0570 clauses 3, 6): a value's name and generation, never a value; and
|
||||
// one read of the seat's provisions, which hold none.
|
||||
for _, call := range []string{"Get(", "Put(", "Delete(", "Keys("} {
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(BoundState)."+call, "", 1))
|
||||
}
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(provisions.ProvisionsRead).Get(", "", 1))
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(meshprovisionerclient.ProvisionsRead).Get(", "", 1))
|
||||
// And one watch of them, for a credential asked of the vault (issue 1126).
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(provisions.ProvisionsRead).Watch(", "", 1))
|
||||
code = []byte(strings.Replace(string(code), "stdio.State(meshprovisionerclient.ProvisionsRead).Watch(", "", 1))
|
||||
}
|
||||
if m := bucket.Find(code); m != nil {
|
||||
t.Errorf("%s opens a bucket (%s): the vault keeps every secret in its own store only", f, m)
|
||||
|
||||
@@ -3,6 +3,7 @@ module mesh-vault
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/go/seats/meshvault v0.0.0-20261104174344-ae5266e77639
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -12,7 +12,7 @@ package main
|
||||
// COMMIT (novox/hq ADR 0230).
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ package main
|
||||
// that cannot be read is not nobody asking (issue 241): nothing is retired until it is read again.
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
)
|
||||
|
||||
// ProvisionName is what postgres provides, as its manifest says.
|
||||
|
||||
@@ -14,7 +14,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -4,7 +4,7 @@ package main
|
||||
// of it — the login refused, the data kept, a deletion dropping only its own — is live_test.go's.
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -3,6 +3,7 @@ module postgres
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||
github.com/jackc/pgx/v5 v5.11.0
|
||||
)
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
)
|
||||
|
||||
func say(format string, args ...any) {
|
||||
|
||||
@@ -11,7 +11,7 @@ package main
|
||||
// mesh still gives it.
|
||||
|
||||
import (
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
)
|
||||
|
||||
// ProvisionName is what route-adapter provides, as its manifest says.
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||
)
|
||||
|
||||
// bucketBus is the helper's bus over in-memory buckets.
|
||||
@@ -51,17 +51,17 @@ func TestAConsumerOfARouteIsToldItsNamesFromItsPlanEntry(t *testing.T) {
|
||||
"provider-module": ModuleName, "at": "10.7.0.1", "from": "novox", "as": "mesh_g14_gitea",
|
||||
"names": map[string]any{"name": "git.example.org", "internal-name": "git.novox.internal",
|
||||
"name-web": "git.example.org"}}}}})
|
||||
if err := bus.Put(context.Background(), provisions.PlansBucket, "g14.gitea", plan); err != nil {
|
||||
if err := bus.Put(context.Background(), meshprovisionerclient.PlansBucket, "g14.gitea", plan); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The key the seat composed for the entry, asked of route-adapter (novox/hq ADR 0570 clauses 1, 3).
|
||||
key, _ := provisions.Key("g14", "gitea", ProvisionName, "")
|
||||
asked, err := provisions.Encode(provisions.Provision{Provision: ProvisionName, State: provisions.Asked,
|
||||
Consumer: provisions.End{Node: "g14", Module: "gitea"}, Provider: provisions.End{Node: "novox", Module: ModuleName}})
|
||||
key, _ := meshprovisionerclient.Key("g14", "gitea", ProvisionName, "")
|
||||
asked, err := meshprovisionerclient.Encode(meshprovisionerclient.Provision{Provision: ProvisionName, State: meshprovisionerclient.Asked,
|
||||
Consumer: meshprovisionerclient.End{Node: "g14", Module: "gitea"}, Provider: meshprovisionerclient.End{Node: "novox", Module: ModuleName}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := bus.Put(context.Background(), provisions.ProvisionsBucket, key, asked); err != nil {
|
||||
if err := bus.Put(context.Background(), meshprovisionerclient.ProvisionsBucket, key, asked); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p := newProvider("novox", bus, t.Logf)
|
||||
|
||||
@@ -5,6 +5,7 @@ go 1.26.0
|
||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104174344-ae5266e77639 // indirect
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||
|
||||
Reference in New Issue
Block a user