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"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
|
|
||||||
"gitea/internal/forgehttp"
|
"gitea/internal/forgehttp"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import (
|
|||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"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"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bundleVar = "MESH_FORGE_ACCOUNT_TEST_BUNDLE"
|
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",
|
"plan": map[string]any{"provisions": []map[string]any{{"provision": "forge-account", "provider": "novox",
|
||||||
"provider-module": "gitea", "as": "mesh_g14_ssh_client"}}}})
|
"provider-module": "gitea", "as": "mesh_g14_ssh_client"}}}})
|
||||||
rt.put("mesh-provisioner.provisions", "g14.ssh-client.forge-account",
|
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",
|
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"})
|
"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",
|
rt.start([]string{"MESH_NODE=novox", "MESH_MODULE=gitea", "MESH_SERVED_MODULE=gitea",
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
|
||||||
|
|
||||||
"gitea/internal/forgeaccount"
|
"gitea/internal/forgeaccount"
|
||||||
"gitea/internal/forgehttp"
|
"gitea/internal/forgehttp"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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/forgeaccount"
|
||||||
"gitea/internal/forgehttp"
|
"gitea/internal/forgehttp"
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
|
|
||||||
"gitea/internal/forgehttp"
|
"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
|
// 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).
|
// ready to make waits and keeps serving).
|
||||||
func (b *fakeBus) serves(key string, state ...string) bool {
|
func (b *fakeBus) serves(key string, state ...string) bool {
|
||||||
want := provisions.Provided
|
want := meshprovisionerclient.Provided
|
||||||
if len(state) > 0 {
|
if len(state) > 0 {
|
||||||
want = 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
|
// 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).
|
// provision key asked of gitea on novox (novox/hq ADR 0570 clauses 1 to 3).
|
||||||
func (b *fakeBus) bindPlan(node, module, as string) {
|
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}]}}`,
|
`{"v":1,"node":%q,"module":%q,"plan":{"provisions":[{"provision":"forge-account","provider":"novox","provider-module":"gitea","as":%q}]}}`,
|
||||||
node, module, as)))
|
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
|
// 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.
|
// with the warrant (ADR 0570 clause 5), the only way the provider lets an entry go.
|
||||||
func (b *fakeBus) retire(node, module string) {
|
func (b *fakeBus) retire(node, module string) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
delete(b.buckets[provisions.PlansBucket], node+"."+module)
|
delete(b.buckets[meshprovisionerclient.PlansBucket], node+"."+module)
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
b.set(provisions.ProvisionsBucket, node+"."+module+".forge-account", provisionKey(node, module, provisions.Retiring,
|
b.set(meshprovisionerclient.ProvisionsBucket, node+"."+module+".forge-account", provisionKey(node, module, meshprovisionerclient.Retiring,
|
||||||
&provisions.Warrant{Ask: "ask-1", Option: "yes", By: "operator", At: time.Date(2026, 10, 29, 12, 0, 0, 0, time.UTC)}))
|
&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 {
|
func provisionKey(node, module, state string, w *meshprovisionerclient.Warrant) []byte {
|
||||||
pr := provisions.Provision{Provision: "forge-account", State: state, Warrant: w,
|
pr := meshprovisionerclient.Provision{Provision: "forge-account", State: state, Warrant: w,
|
||||||
Consumer: provisions.End{Node: node, Module: module}, Provider: provisions.End{Node: "novox", Module: "gitea"}}
|
Consumer: meshprovisionerclient.End{Node: node, Module: module}, Provider: meshprovisionerclient.End{Node: "novox", Module: "gitea"}}
|
||||||
if state == provisions.Retiring {
|
if state == meshprovisionerclient.Retiring {
|
||||||
pr.Why = "no want needs it"
|
pr.Why = "no want needs it"
|
||||||
}
|
}
|
||||||
raw, err := provisions.Encode(pr)
|
raw, err := meshprovisionerclient.Encode(pr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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" {
|
if got := strings.Join(r.forge.titles("nox-agent"), ","); got != "mesh:g14.nox,my own" {
|
||||||
t.Fatalf("keys = %s", got)
|
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")
|
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") {
|
if len(out) != 1 || !strings.Contains(out[0].Why, "not read yet") {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module gitea
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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-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/gopackageregistry v0.0.0-00010101000000-000000000000
|
||||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry 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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
"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-sdk/go/provisions"
|
"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
|
// 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).
|
// 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) {
|
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})
|
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)
|
list, _ := plan["provisions"].([]any)
|
||||||
for _, e := range list {
|
for _, e := range list {
|
||||||
entry := e.(map[string]any)
|
entry := e.(map[string]any)
|
||||||
name, _ := entry["provision"].(string)
|
name, _ := entry["provision"].(string)
|
||||||
local, _ := entry["local"].(string)
|
local, _ := entry["local"].(string)
|
||||||
key, err := provisions.Key(node, module, name, local)
|
key, err := meshprovisionerclient.Key(node, module, name, local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
asked, err := provisions.Encode(provisions.Provision{Provision: name, Local: local, State: provisions.Asked,
|
asked, err := meshprovisionerclient.Encode(meshprovisionerclient.Provision{Provision: name, Local: local, State: meshprovisionerclient.Asked,
|
||||||
Consumer: provisions.End{Node: node, Module: module}, Provider: provisions.End{Node: "novox", Module: "keycloak"}})
|
Consumer: meshprovisionerclient.End{Node: node, Module: module}, Provider: meshprovisionerclient.End{Node: "novox", Module: "keycloak"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_ = bus.Put(ctx, provisions.ProvisionsBucket, key, asked)
|
_ = bus.Put(ctx, meshprovisionerclient.ProvisionsBucket, key, asked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"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.
|
// retirement to the harness's guards (the SDK's harness retirement): the client is disabled and marked to delete, never removed.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProvisionName is what keycloak provides, as its manifest says.
|
// ProvisionName is what keycloak provides, as its manifest says.
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testIssuer = "https://id.example.org/realms/Novox"
|
const testIssuer = "https://id.example.org/realms/Novox"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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.
|
// Mark is the attribute marking a client as the mesh's own work.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"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-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||||
|
|
||||||
require (
|
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
|
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104174344-ae5266e77639 // indirect
|
||||||
github.com/klauspost/compress v1.20.1 // indirect
|
github.com/klauspost/compress v1.20.1 // indirect
|
||||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module mesh-config
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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/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/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-contracts/operator-and-conversation/go/seats/operatorchannel v0.0.0-00010101000000-000000000000
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module mesh-delivery-gate
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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/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-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
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"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).
|
// 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 (
|
const (
|
||||||
// PlansBucket is where each module's provisioning plan on each node is read: the seat mesh-provisioner's
|
// 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).
|
// `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
|
// 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
|
// 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
|
// 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:
|
// asAssignment is a plan of the seat mesh-provisioner as the client's rules read an assignment's value:
|
||||||
// `{node, module, provisioning: plan}`.
|
// `{node, module, provisioning: plan}`.
|
||||||
func asAssignment(raw []byte) ([]byte, error) {
|
func asAssignment(raw []byte) ([]byte, error) {
|
||||||
if _, err := provisions.DecodePlan(raw); err != nil {
|
if _, err := meshprovisionerclient.DecodePlan(raw); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// The plan as it is kept, its bytes unchanged: the client reads it strictly (planOf).
|
// The plan as it is kept, its bytes unchanged: the client reads it strictly (planOf).
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"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).
|
// as the seat mesh-provisioner keeps it (ADR 0570 clause 2).
|
||||||
func routeEntryFor(t *testing.T, module, planEntry string) string {
|
func routeEntryFor(t *testing.T, module, planEntry string) string {
|
||||||
t.Helper()
|
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":[` +
|
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).
|
// The key the seat composed for that entry, provided-for: asked of route-proxy (ADR 0570 clause 3).
|
||||||
var b struct {
|
var b struct {
|
||||||
Provision string `json:"provision"`
|
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 {
|
if err := json.Unmarshal([]byte(planEntry), &b); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
pr := provisions.Provision{Provision: b.Provision, Local: b.Local, State: provisions.Asked,
|
pr := meshprovisionerclient.Provision{Provision: b.Provision, Local: b.Local, State: meshprovisionerclient.Asked,
|
||||||
Consumer: provisions.End{Node: node, Module: module}, Provider: provisions.End{Node: "novox", Module: "route-proxy"}}
|
Consumer: meshprovisionerclient.End{Node: node, Module: module}, Provider: meshprovisionerclient.End{Node: "novox", Module: "route-proxy"}}
|
||||||
key, err := pr.Key()
|
key, err := pr.Key()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
raw, err := provisions.Encode(pr)
|
raw, err := meshprovisionerclient.Encode(pr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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,
|
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{}}}})
|
Offers: map[string]provider.Offer{"route": {Credential: provider.NoCredential, Serves: map[string]any{}}}})
|
||||||
out, err := p.Pass(context.Background())
|
out, err := p.Pass(context.Background())
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module mesh-provisioner-client
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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
|
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-server/v2 v2.11.17
|
||||||
github.com/nats-io/nats.go v1.54.0
|
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"
|
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"
|
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"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"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
|
// 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.
|
// placed module has no plan composed (issue 774): written while it is held, deleted once it composes again.
|
||||||
const (
|
const (
|
||||||
ProvisionsState = provisions.ProvisionsState
|
ProvisionsState = meshprovisionerclient.ProvisionsState
|
||||||
PlansState = provisions.PlansState
|
PlansState = meshprovisionerclient.PlansState
|
||||||
RefusedState = "refused"
|
RefusedState = "refused"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ func TookSaid(took []Took) string {
|
|||||||
return strings.Join(said, ", ")
|
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) {
|
func (h *Holder) ComposeNode(ctx context.Context, node string) (Composed, error) {
|
||||||
return h.ComposeNodeWith(ctx, node, false)
|
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())
|
out.Problems = append(out.Problems, err.Error())
|
||||||
}
|
}
|
||||||
lap("plans written")
|
lap("plans written")
|
||||||
needed := map[string]provisions.Provision{}
|
needed := map[string]meshprovisionerclient.Provision{}
|
||||||
elsewhere := map[string]provisions.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
|
// 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).
|
// them not yet, and the provider binds them from the entry they carry (issue 1126).
|
||||||
ahead := map[string]string{}
|
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 i, plans := range []map[string]catalogue.Plan{atWants, newest} {
|
||||||
for module, plan := range plans {
|
for module, plan := range plans {
|
||||||
for _, p := range plan.Provisions {
|
for _, p := range plan.Provisions {
|
||||||
consumer := provisions.End{Module: module, Node: node}
|
consumer := meshprovisionerclient.End{Module: module, Node: node}
|
||||||
provider := provisions.End{Module: p.ProviderModule, Node: p.Provider}
|
provider := meshprovisionerclient.End{Module: p.ProviderModule, Node: p.Provider}
|
||||||
pv := provisions.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider,
|
pv := meshprovisionerclient.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider,
|
||||||
DependsOn: provisions.DependsOn(consumer, p, h.providerPlan(p, node, atWants))}
|
DependsOn: meshprovisionerclient.DependsOn(consumer, p, h.providerPlan(p, node, atWants))}
|
||||||
key, err := pv.Key()
|
key, err := pv.Key()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out.Problems = append(out.Problems, err.Error())
|
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
|
// 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.
|
// 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 {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cn, cm, cp, cl, err := provisions.ParseKey(ck)
|
cn, cm, cp, cl, err := meshprovisionerclient.ParseKey(ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
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")
|
out.Problems = append(out.Problems, "no node runs mesh-vault, so the credential "+ck+" has no provider")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cred := provisions.Provision{Provision: cp, Local: cl, Consumer: provisions.End{Module: cm, Node: cn},
|
cred := meshprovisionerclient.Provision{Provision: cp, Local: cl, Consumer: meshprovisionerclient.End{Module: cm, Node: cn},
|
||||||
Provider: provisions.End{Module: provisions.VaultModule, Node: vaultNode}}
|
Provider: meshprovisionerclient.End{Module: meshprovisionerclient.VaultModule, Node: vaultNode}}
|
||||||
if cn == node {
|
if cn == node {
|
||||||
if _, seen := needed[ck]; !seen {
|
if _, seen := needed[ck]; !seen {
|
||||||
needed[ck] = cred
|
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 {
|
func (h *Holder) writeRefused(node string, held map[string]string) error {
|
||||||
var problems []error
|
var problems []error
|
||||||
for _, module := range sortedKeys(held) {
|
for _, module := range sortedKeys(held) {
|
||||||
key, err := provisions.PlanKey(node, module)
|
key, err := meshprovisionerclient.PlanKey(node, module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
problems = append(problems, err)
|
problems = append(problems, err)
|
||||||
continue
|
continue
|
||||||
@@ -382,7 +382,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
key, err := provisions.PlanKey(p.Provider, p.ProviderModule)
|
key, err := meshprovisionerclient.PlanKey(p.Provider, p.ProviderModule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -390,7 +390,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
|
|||||||
if err != nil || e == nil {
|
if err != nil || e == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
plan, err := provisions.DecodePlan(e.Value)
|
plan, err := meshprovisionerclient.DecodePlan(e.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 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.
|
// vaultNode is the node mesh-vault is placed on: the provider of every credential.
|
||||||
func (h *Holder) vaultNode() string {
|
func (h *Holder) vaultNode() string {
|
||||||
nodes := h.placedOn(provisions.VaultModule)
|
nodes := h.placedOn(meshprovisionerclient.VaultModule)
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -421,7 +421,7 @@ func (h *Holder) dependedOn(node string) []string {
|
|||||||
if err != nil || e == nil {
|
if err != nil || e == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if p, err := provisions.Decode(e.Value); err == nil {
|
if p, err := meshprovisionerclient.Decode(e.Value); err == nil {
|
||||||
for _, d := range p.DependsOn {
|
for _, d := range p.DependsOn {
|
||||||
if strings.HasPrefix(d, node+".") {
|
if strings.HasPrefix(d, node+".") {
|
||||||
out = append(out, d)
|
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;
|
// 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.
|
// 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) {
|
for _, key := range sortedKeys(keys) {
|
||||||
next := keys[key]
|
next := keys[key]
|
||||||
var have provisions.Provision
|
var have meshprovisionerclient.Provision
|
||||||
e, err := h.Store.Get(ProvisionsState, key)
|
e, err := h.Store.Get(ProvisionsState, key)
|
||||||
if err == nil && e != nil {
|
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
|
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 {
|
func (h *Holder) writePlans(node string, plans map[string]catalogue.Plan, held map[string]string, out *Composed) error {
|
||||||
var problems []error
|
var problems []error
|
||||||
for _, module := range sortedKeys(plans) {
|
for _, module := range sortedKeys(plans) {
|
||||||
key, err := provisions.PlanKey(node, module)
|
key, err := meshprovisionerclient.PlanKey(node, module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
problems = append(problems, err)
|
problems = append(problems, err)
|
||||||
continue
|
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 {
|
if err != nil {
|
||||||
problems = append(problems, err)
|
problems = append(problems, err)
|
||||||
continue
|
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
|
// 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
|
// 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.
|
// 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) {
|
untouched map[string]string, retireMany bool, out *Composed) {
|
||||||
held := h.held(node)
|
held := h.held(node)
|
||||||
consumers := map[string]bool{}
|
consumers := map[string]bool{}
|
||||||
@@ -577,7 +577,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
|||||||
var newly []string
|
var newly []string
|
||||||
leaving := map[string]bool{}
|
leaving := map[string]bool{}
|
||||||
for key, have := range held {
|
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)
|
newly = append(newly, key)
|
||||||
leaving[have.Consumer.Module] = true
|
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) {
|
for _, key := range unionKeys(needed, held) {
|
||||||
want, isNeeded := needed[key]
|
want, isNeeded := needed[key]
|
||||||
have, isHeld := held[key]
|
have, isHeld := held[key]
|
||||||
var next provisions.Provision
|
var next meshprovisionerclient.Provision
|
||||||
switch {
|
switch {
|
||||||
case isNeeded:
|
case isNeeded:
|
||||||
next = want
|
next = want
|
||||||
@@ -615,7 +615,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
|||||||
next.Reference = have.Reference
|
next.Reference = have.Reference
|
||||||
}
|
}
|
||||||
h.outcome(key, &next)
|
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.
|
// 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 " +
|
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"
|
"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
|
next.Why = why
|
||||||
}
|
}
|
||||||
case have.State == provisions.Retiring:
|
case have.State == meshprovisionerclient.Retiring:
|
||||||
next = have
|
next = have
|
||||||
if next.Ask != "" && next.Warrant == nil && !h.askHeld(next) {
|
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")
|
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:
|
default:
|
||||||
next = have
|
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)
|
"any more", node, have.Provision, have.Provider.Module, have.Provider.Node)
|
||||||
next.Warrant = nil
|
next.Warrant = nil
|
||||||
h.raise(&next, key, out)
|
h.raise(&next, key, out)
|
||||||
@@ -652,7 +652,7 @@ func (h *Holder) judgeNode(node string, needed map[string]provisions.Provision,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
next.At = h.Now().UTC()
|
next.At = h.Now().UTC()
|
||||||
raw, err := provisions.Encode(next)
|
raw, err := meshprovisionerclient.Encode(next)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out.Problems = append(out.Problems, err.Error())
|
out.Problems = append(out.Problems, err.Error())
|
||||||
continue
|
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.
|
// held is the node's provision keys as kept.
|
||||||
func (h *Holder) held(node string) map[string]provisions.Provision {
|
func (h *Holder) held(node string) map[string]meshprovisionerclient.Provision {
|
||||||
out := map[string]provisions.Provision{}
|
out := map[string]meshprovisionerclient.Provision{}
|
||||||
keys, err := h.Store.Keys(ProvisionsState)
|
keys, err := h.Store.Keys(ProvisionsState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return out
|
return out
|
||||||
@@ -680,7 +680,7 @@ func (h *Holder) held(node string) map[string]provisions.Provision {
|
|||||||
if err != nil || e == nil {
|
if err != nil || e == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if p, err := provisions.Decode(e.Value); err == nil {
|
if p, err := meshprovisionerclient.Decode(e.Value); err == nil {
|
||||||
out[k] = p
|
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.
|
// 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
|
a.At, b.At, a.V, b.V = time.Time{}, time.Time{}, 0, 0
|
||||||
ra, _ := json.Marshal(a)
|
ra, _ := json.Marshal(a)
|
||||||
rb, _ := json.Marshal(b)
|
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
|
// 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
|
// none. The holder never sets provided by itself (clause 3): a provider whose entries it does not read keeps the key
|
||||||
// asked, said so.
|
// 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
|
p.Ask, p.Warrant = "", nil
|
||||||
keeps, err := h.keepsEntries(p.Provider.Module, p.Provision)
|
keeps, err := h.keepsEntries(p.Provider.Module, p.Provision)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if !keeps {
|
if !keeps {
|
||||||
@@ -722,40 +722,40 @@ func (h *Holder) outcome(key string, p *provisions.Provision) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !slices.Contains(BoundReads, p.Provider.Module) {
|
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"
|
"manifest does not read yet"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e, err := h.Store.Get(boundRead(p.Provider.Module), key)
|
e, err := h.Store.Get(boundRead(p.Provider.Module), key)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if e == nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
var en entry
|
var en entry
|
||||||
if err := json.Unmarshal(e.Value, &en); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case en.State == provisions.Failed:
|
case en.State == meshprovisionerclient.Failed:
|
||||||
p.State, p.Why = provisions.Failed, orSaid(en.Why, en.Refused, "the provider said it failed")
|
p.State, p.Why = meshprovisionerclient.Failed, orSaid(en.Why, en.Refused, "the provider said it failed")
|
||||||
case en.State == provisions.Waits:
|
case en.State == meshprovisionerclient.Waits:
|
||||||
p.State, p.Why = provisions.Waits, orSaid(en.Why, en.Waits, "the provider waits for a person")
|
p.State, p.Why = meshprovisionerclient.Waits, orSaid(en.Why, en.Waits, "the provider waits for a person")
|
||||||
case en.State == "" && en.Refused != "":
|
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 != "":
|
case en.State == "" && en.Waits != "":
|
||||||
p.State, p.Why = provisions.Waits, en.Waits
|
p.State, p.Why = meshprovisionerclient.Waits, en.Waits
|
||||||
case en.State != "" && en.State != provisions.Provided:
|
case en.State != "" && en.State != meshprovisionerclient.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.Asked, "the provider's entry says "+en.State+", which no state of a provision is"
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
p.State, p.Why = provisions.Provided, ""
|
p.State, p.Why = meshprovisionerclient.Provided, ""
|
||||||
if en.VaultName != "" || en.Generation > 0 {
|
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 {
|
var decl struct {
|
||||||
Reads []string `json:"reads"`
|
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 ""
|
||||||
}
|
}
|
||||||
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"
|
"), 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
|
// 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
|
// 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.
|
// 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
|
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).
|
// It makes what the consumer needs only once all it depends on is provided (its credential first).
|
||||||
for _, dep := range p.DependsOn {
|
for _, dep := range p.DependsOn {
|
||||||
state := "not composed yet"
|
state := "not composed yet"
|
||||||
if e, err := h.Store.Get(ProvisionsState, dep); err == nil && e != nil {
|
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
|
state = d.State
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if state != provisions.Provided {
|
if state != meshprovisionerclient.Provided {
|
||||||
p.State, p.Why = provisions.Asked, "waits for "+dep+" ("+state+") before "+where+" can provide it"
|
p.State, p.Why = meshprovisionerclient.Asked, "waits for "+dep+" ("+state+") before "+where+" can provide it"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := p.Provider.Node + "." + p.Provider.Module
|
key := p.Provider.Node + "." + p.Provider.Module
|
||||||
e, err := h.Store.Get(HaveRead, key)
|
e, err := h.Store.Get(HaveRead, key)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if e == nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
have, err := delivery.DecodeHave(e.Value)
|
have, err := delivery.DecodeHave(e.Value)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
wantFP := ""
|
wantFP := ""
|
||||||
@@ -821,19 +821,19 @@ func (h *Holder) haveOutcome(p *provisions.Provision) {
|
|||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case have.Phase == delivery.PhaseFailed || have.Health == delivery.HealthFailed:
|
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):
|
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).
|
// 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
|
// 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.
|
// 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 {
|
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"
|
have.FP+") does not"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.State, p.Why = provisions.Provided, ""
|
p.State, p.Why = meshprovisionerclient.Provided, ""
|
||||||
default:
|
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)
|
"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.
|
// 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 {
|
if p.Warrant == nil || p.Warrant.Option != OptionApprove {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
@@ -1003,7 +1003,7 @@ const (
|
|||||||
const askLasts = occ.ApproveLasts - 10*time.Minute
|
const askLasts = occ.ApproveLasts - 10*time.Minute
|
||||||
|
|
||||||
// retiringAsk is the ask of one retiring provision, composed from the key alone.
|
// 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,
|
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)
|
p.Consumer.Module, p.Consumer.Node)
|
||||||
a := oc.Ask{ID: id, Headline: "Remove " + p.Consumer.Module + "'s " + p.Provision + " access?",
|
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,
|
"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),
|
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",
|
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 }{
|
for _, o := range []struct{ id, label, does string }{
|
||||||
{OptionApprove, "Remove", "the provider removes the credential; the resource and its data stay"},
|
{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"},
|
{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)
|
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.
|
// 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); {
|
switch err := h.ask(p, key); {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
out.Asked = append(out.Asked, key)
|
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
|
// 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.
|
// 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 != "" {
|
if p.Ask != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1086,7 +1086,7 @@ func (h *Holder) openAsks() int {
|
|||||||
keys, _ := h.Store.Keys(ProvisionsState)
|
keys, _ := h.Store.Keys(ProvisionsState)
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if e, err := h.Store.Get(ProvisionsState, k); err == nil && e != nil {
|
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 {
|
p.Warrant == nil {
|
||||||
n++
|
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
|
// 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).
|
// 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 == "" {
|
if h.Asks == nil || p.Ask == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -1125,8 +1125,8 @@ func (h *Holder) Decide() []string {
|
|||||||
if err != nil || e == nil {
|
if err != nil || e == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p, err := provisions.Decode(e.Value)
|
p, err := meshprovisionerclient.Decode(e.Value)
|
||||||
if err != nil || p.State != provisions.Retiring || p.Ask == "" || p.Warrant != nil {
|
if err != nil || p.State != meshprovisionerclient.Retiring || p.Ask == "" || p.Warrant != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
raw, found, err := h.Asks.Record(p.Ask)
|
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()+")")
|
said = append(said, k+": the answer is refused: its time is not RFC 3339 ("+err.Error()+")")
|
||||||
continue
|
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)
|
h.put(k, p)
|
||||||
said = append(said, k+": "+option+" by "+by)
|
said = append(said, k+": "+option+" by "+by)
|
||||||
}
|
}
|
||||||
@@ -1200,8 +1200,8 @@ func (h *Holder) raiseWaiting() []string {
|
|||||||
if err != nil || e == nil {
|
if err != nil || e == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p, err := provisions.Decode(e.Value)
|
p, err := meshprovisionerclient.Decode(e.Value)
|
||||||
if err != nil || p.State != provisions.Retiring || p.Ask != "" || p.Warrant != nil || !strings.Contains(p.Why, askWaits) {
|
if err != nil || p.State != meshprovisionerclient.Retiring || p.Ask != "" || p.Warrant != nil || !strings.Contains(p.Why, askWaits) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch err := h.ask(&p, k); {
|
switch err := h.ask(&p, k); {
|
||||||
@@ -1297,9 +1297,9 @@ func (h *Holder) LastingHolds() map[string]string {
|
|||||||
return out
|
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()
|
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 {
|
if err := h.Store.Put(ProvisionsState, key, json.RawMessage(raw)); err != nil {
|
||||||
h.Logf("[%s] %s is not written: %v", Module, key, err)
|
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 {
|
if keys, err := h.Store.Keys(ProvisionsState); err == nil {
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if e, err := h.Store.Get(ProvisionsState, k); err == nil && e != nil {
|
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
|
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.
|
// NodeOfEntry is the consumer's node a provider's entry key names.
|
||||||
func NodeOfEntry(key string) []string {
|
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 []string{n}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -1403,11 +1403,11 @@ func (h *Holder) Rejudge(key string) (string, error) {
|
|||||||
if err != nil || e == nil {
|
if err != nil || e == nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
have, err := provisions.Decode(e.Value)
|
have, err := meshprovisionerclient.Decode(e.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if have.State == provisions.Retiring {
|
if have.State == meshprovisionerclient.Retiring {
|
||||||
if gone, why := h.retired(key, have); gone {
|
if gone, why := h.retired(key, have); gone {
|
||||||
return key + " retired: " + why, h.Store.Delete(ProvisionsState, key)
|
return key + " retired: " + why, h.Store.Delete(ProvisionsState, key)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ import (
|
|||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
|
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"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,9 +132,9 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
|||||||
published(t, mem, c)
|
published(t, mem, c)
|
||||||
vaultManifest := `{"module":"mesh-vault","version":"1","state":["bound"]}` // the vault keeps its outcomes as entries
|
vaultManifest := `{"module":"mesh-vault","version":"1","state":["bound"]}` // the vault keeps its outcomes as entries
|
||||||
vaultDigest := delivery.ManifestDigest([]byte(vaultManifest))
|
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, 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 state, keys := range mem.states {
|
||||||
for key, raw := range keys {
|
for key, raw := range keys {
|
||||||
if err := s.Put(state, key, json.RawMessage(raw)); err != nil {
|
if err := s.Put(state, key, json.RawMessage(raw)); err != nil {
|
||||||
@@ -195,19 +195,19 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
|||||||
a := &asked{}
|
a := &asked{}
|
||||||
h := &Holder{Store: Watching(s, ws), Asks: a, Now: time.Now, Logf: t.Logf}
|
h := &Holder{Store: Watching(s, ws), Asks: a, Now: time.Now, Logf: t.Logf}
|
||||||
h.Compose = FromCompositions(h.Store)
|
h.Compose = FromCompositions(h.Store)
|
||||||
state := func(key string) provisions.Provision {
|
state := func(key string) meshprovisionerclient.Provision {
|
||||||
e, _ := s.Get(ProvisionsState, key)
|
e, _ := s.Get(ProvisionsState, key)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return provisions.Provision{}
|
return meshprovisionerclient.Provision{}
|
||||||
}
|
}
|
||||||
p, _ := provisions.Decode(e.Value)
|
p, _ := meshprovisionerclient.Decode(e.Value)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
key := node + ".restic.postgres-database"
|
key := node + ".restic.postgres-database"
|
||||||
if _, err := h.ComposeNode(ctx, node); err != nil {
|
if _, err := h.ComposeNode(ctx, node); err != nil {
|
||||||
t.Fatal(err)
|
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)
|
t.Fatalf("the placement and the want composed %+v", p)
|
||||||
}
|
}
|
||||||
if e, _ := s.Get(PlansState, node+".restic"); e == nil {
|
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.
|
// The providers' outcomes: the vault provides the credential where restic has one, postgres runs healthy.
|
||||||
for _, dep := range state(key).DependsOn {
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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)
|
t.Fatalf("a build not offering the provision turned it %s", p.State)
|
||||||
}
|
}
|
||||||
offered(`["postgres-database"]`)
|
offered(`["postgres-database"]`)
|
||||||
@@ -257,7 +257,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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)
|
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.
|
// 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 {
|
if _, err := h.ComposeNode(ctx, node); err != nil {
|
||||||
t.Fatal(err)
|
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)
|
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"
|
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"
|
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"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"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
|
// 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"}
|
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()
|
t.Helper()
|
||||||
e, _ := m.s.Get(ProvisionsState, key)
|
e, _ := m.s.Get(ProvisionsState, key)
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return provisions.Provision{}
|
return meshprovisionerclient.Provision{}
|
||||||
}
|
}
|
||||||
p, err := provisions.Decode(e.Value)
|
p, err := meshprovisionerclient.Decode(e.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -150,11 +150,11 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
|||||||
state string
|
state string
|
||||||
why string
|
why string
|
||||||
}{
|
}{
|
||||||
{"composed: asked", "", provisions.Asked, ""},
|
{"composed: asked", "", meshprovisionerclient.Asked, ""},
|
||||||
{"the provider waits for a person", `{"waits":"the database's owner to accept"}`, provisions.Waits, "owner"},
|
{"the provider waits for a person", `{"waits":"the database's owner to accept"}`, meshprovisionerclient.Waits, "owner"},
|
||||||
{"the provider refused", `{"refused":"no room on ace"}`, provisions.Failed, "no room"},
|
{"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"}`, provisions.Failed, "disk"},
|
{"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}`, provisions.Provided, ""},
|
{"the provider made it", `{"state":"provided","vault-name":"pair/gitea-postgres","generation":3}`, meshprovisionerclient.Provided, ""},
|
||||||
}
|
}
|
||||||
for _, s := range steps {
|
for _, s := range steps {
|
||||||
if s.entry != "" {
|
if s.entry != "" {
|
||||||
@@ -173,11 +173,11 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
|||||||
// holder alone.
|
// holder alone.
|
||||||
split := "g14.gitea.split-dns"
|
split := "g14.gitea.split-dns"
|
||||||
for _, row := range []struct{ have, want, state, why string }{
|
for _, row := range []struct{ have, want, state, why string }{
|
||||||
{"", "", provisions.Asked, "says nothing yet"},
|
{"", "", meshprovisionerclient.Asked, "says nothing yet"},
|
||||||
{`{"v":1,"fp":"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","phase":"applying","health":"unknown"}`, "", provisions.Asked, "applying"},
|
{`{"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}`, provisions.Asked, "build its want"},
|
{`{"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"}`, "", provisions.Failed, "did not start"},
|
{`{"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}`, provisions.Provided, ""},
|
{`{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","phase":"running","health":"ok"}`, `{"v":1,"fp":"src2:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","self":true}`, meshprovisionerclient.Provided, ""},
|
||||||
} {
|
} {
|
||||||
if row.have != "" {
|
if row.have != "" {
|
||||||
m.s.write(t, HaveRead, "g14.systemd-resolved", 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)
|
bfp := "src2:" + strings.Repeat("bb", 32)
|
||||||
buildKey := mustKey(delivery.BuilderBuildKey("systemd-resolved", bfp))
|
buildKey := mustKey(delivery.BuilderBuildKey("systemd-resolved", bfp))
|
||||||
for _, row := range []struct{ name, provides, state, why string }{
|
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 does not offer it", `["other"]`, meshprovisionerclient.Asked, "offers split-dns"},
|
||||||
{"a build whose manifest offers it", `[{"name":"split-dns"}]`, provisions.Provided, ""},
|
{"a build whose manifest offers it", `[{"name":"split-dns"}]`, meshprovisionerclient.Provided, ""},
|
||||||
} {
|
} {
|
||||||
raw := `{"module":"systemd-resolved","version":"1","provides":` + row.provides + `}`
|
raw := `{"module":"systemd-resolved","version":"1","provides":` + row.provides + `}`
|
||||||
digest := delivery.ManifestDigest([]byte(raw))
|
digest := delivery.ManifestDigest([]byte(raw))
|
||||||
@@ -238,7 +238,7 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) {
|
|||||||
c.atNewest["g14"] = c.atWant["g14"]
|
c.atNewest["g14"] = c.atWant["g14"]
|
||||||
c.compose(t, "g14")
|
c.compose(t, "g14")
|
||||||
cred := c.state(t, "g14.gitea.secret-postgres-database")
|
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)
|
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"}) {
|
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 {
|
for _, row := range []struct {
|
||||||
name, entry, cred, state string
|
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}`,
|
{"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 != "" {
|
if row.entry != "" {
|
||||||
c.s.write(t, "mesh-vault.bound", s3cred, 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.
|
// An entry's change judges its key alone, no composition.
|
||||||
before := len(m.composed)
|
before := len(m.composed)
|
||||||
m.s.write(t, "postgres.bound", key, `{"refused":"the login is locked"}`)
|
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)
|
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
|
removed bool
|
||||||
}{
|
}{
|
||||||
{"a key composed ahead of its want is asked, not retiring", func() {}, nil,
|
{"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() {
|
{"one requirement removed and another added: only the removed retires", func() {
|
||||||
m.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc)}
|
m.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc)}
|
||||||
m.atNewest["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},
|
}, []string{pg}, map[string]string{pg: meshprovisionerclient.Retiring, kc: meshprovisionerclient.Asked}, false},
|
||||||
{"composed again: no second ask", func() {}, nil, map[string]string{pg: provisions.Retiring}, 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() {
|
{"needed again before the warrant: back to its outcome", func() {
|
||||||
m.atWant["g14"] = map[string]catalogue.Plan{"gitea": planOf(oidc, pgDB)}
|
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)} },
|
{"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 {
|
for _, s := range steps {
|
||||||
s.change()
|
s.change()
|
||||||
@@ -336,7 +336,7 @@ func TestRetiringIsAskedAndActsOnlyOnTheWarrant(t *testing.T) {
|
|||||||
if w := m.state(t, pg).Warrant; w.Option != "yes" {
|
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)
|
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)
|
t.Errorf("retired while the provider's entry stands: %q", said)
|
||||||
}
|
}
|
||||||
_ = m.s.Delete("postgres.bound", pg)
|
_ = 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 {
|
if e, _ := m.s.Get(PlansState, "g14.nats"); e == nil {
|
||||||
t.Error("the held module's last plan was removed")
|
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)
|
t.Errorf("the held module's provision is %s (%s), not kept", p.State, p.Why)
|
||||||
}
|
}
|
||||||
got, err := m.h.read(RefusedState, "", "g14", "")
|
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",
|
{"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"})},
|
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",
|
{"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"})},
|
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",
|
{"route: route-proxy's entry is read", "novox",
|
||||||
map[string]catalogue.Plan{"gitea": planOf(catalogue.PlanProvision{Provision: "route", Provider: "novox", ProviderModule: "route-proxy"})},
|
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",
|
{"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"})},
|
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
|
// 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.
|
// 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",
|
{"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"})},
|
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 := newMesh()
|
||||||
m.manifestOf("gitea", gitea)
|
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" {
|
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)
|
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)
|
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
|
// 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
|
retiring := 0
|
||||||
for i := range row.consumer {
|
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++
|
retiring++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/health"
|
"git.novox.be/novox/mesh-sdk/go/health"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The holder's tools.
|
// 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}
|
row := map[string]any{"key": k}
|
||||||
if state == ProvisionsState {
|
if state == ProvisionsState {
|
||||||
p, err := provisions.Decode(e.Value)
|
p, err := meshprovisionerclient.Decode(e.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
row["unreadable"] = err.Error()
|
row["unreadable"] = err.Error()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module mesh-provisioner
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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/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/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/clients/operatorchannelclient v0.0.0-00010101000000-000000000000
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"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
|
// 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
|
// assignments. A plan is read as the assignment's value carried it (asAssignment), so the rules that read a plan are
|
||||||
// unchanged.
|
// 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.
|
// 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) {
|
func asAssignment(key string, raw []byte, err error) ([]byte, error) {
|
||||||
if err != nil || raw == nil {
|
if err != nil || raw == nil {
|
||||||
return raw, err
|
return raw, err
|
||||||
}
|
}
|
||||||
p, err := provisions.DecodePlan(raw)
|
p, err := meshprovisionerclient.DecodePlan(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("the plan %s: %w", key, err)
|
return nil, fmt.Errorf("the plan %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
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"
|
"encoding/json"
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ func build() *Env {
|
|||||||
Delete: func(key string) error { return stdio.State(BoundState).Delete(key) },
|
Delete: func(key string) error { return stdio.State(BoundState).Delete(key) },
|
||||||
Keys: func() ([]string, error) { return stdio.State(BoundState).Keys() },
|
Keys: func() ([]string, error) { return stdio.State(BoundState).Keys() },
|
||||||
Provision: func(key string) ([]byte, error) {
|
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 {
|
if err != nil || e == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -122,11 +123,8 @@ func watchLeavers(env *Env) bool {
|
|||||||
if node, ok := leaverKey(key); ok {
|
if node, ok := leaverKey(key); ok {
|
||||||
env.Vault.NodeLeft(node)
|
env.Vault.NodeLeft(node)
|
||||||
}
|
}
|
||||||
}, env.Vault.PlacementChanged, func(key string, v json.RawMessage) error {
|
}, env.Vault.PlacementChanged,
|
||||||
_, err := stdio.State(FindingsState).Put(key, v)
|
meshconditionsclient.FindingWriter{State: stdio.State(FindingsState)})
|
||||||
return err
|
|
||||||
},
|
|
||||||
func(key string) error { return stdio.State(FindingsState).Delete(key) })
|
|
||||||
beginPlacementView(placementView)
|
beginPlacementView(placementView)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -222,7 +220,7 @@ func watchCredentialAsks(env *Env, kick chan<- struct{}) bool {
|
|||||||
if env.Vault == nil {
|
if env.Vault == nil {
|
||||||
return true
|
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) {
|
if !c.Current && credentialAsked(c.Value) {
|
||||||
select {
|
select {
|
||||||
case kick <- struct{}{}:
|
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
|
// 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
|
// `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
|
// 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
|
// 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
|
// 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.
|
// is let go only once its key is retiring with the operator's warrant, or gone.
|
||||||
@@ -14,13 +14,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"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.
|
// BoundState is the vault's state of outcome entries.
|
||||||
const BoundState = "bound"
|
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 {
|
type Bound struct {
|
||||||
Get func(key string) ([]byte, error)
|
Get func(key string) ([]byte, error)
|
||||||
Put func(key string, value []byte) error
|
Put func(key string, value []byte) error
|
||||||
@@ -96,8 +96,8 @@ func (v *Vault) letGo(key string) bool {
|
|||||||
if raw == nil {
|
if raw == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
p, err := provisions.Decode(raw)
|
p, err := meshprovisionerclient.Decode(raw)
|
||||||
return err == nil && p.State == provisions.Retiring && p.Warrant != nil && p.Warrant.Option == "approve"
|
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.
|
// 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
|
if err := json.Unmarshal(raw, &a); err != nil || a.Provisioning == nil { // binds, not handing
|
||||||
continue
|
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 {
|
for _, p := range a.Provisioning.Provisions {
|
||||||
provider := provisions.End{Module: p.ProviderModule, Node: p.Provider}
|
provider := meshprovisionerclient.End{Module: p.ProviderModule, Node: p.Provider}
|
||||||
if p.ProviderModule == provisions.VaultModule && p.Provision == "secret" {
|
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
|
// The vault's own offer: a value it hands the consumer's client on request (`get`), which it serves as
|
||||||
// soon as it runs.
|
// 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 {
|
if err == nil {
|
||||||
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: provisions.VaultModule, Provision: p.Provision,
|
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: p.Provision,
|
||||||
Consumer: a.Node, Module: a.Module, Local: p.Local, State: provisions.Provided,
|
Consumer: a.Node, Module: a.Module, Local: p.Local, State: meshprovisionerclient.Provided,
|
||||||
Why: "the vault hands it to the consumer's client on request"}
|
Why: "the vault hands it to the consumer's client on request"}
|
||||||
}
|
}
|
||||||
continue
|
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 {
|
if !ok {
|
||||||
continue // form none: no credential, nothing minted
|
continue // form none: no credential, nothing minted
|
||||||
}
|
}
|
||||||
parent := provisions.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider}
|
parent := meshprovisionerclient.Provision{Provision: p.Provision, Local: p.Local, Consumer: consumer, Provider: provider}
|
||||||
name := provisions.VaultName(p.CredentialForm, parent)
|
name := meshprovisionerclient.VaultName(p.CredentialForm, parent)
|
||||||
cn, cm, cp, cl, err := provisions.ParseKey(ck)
|
cn, cm, cp, cl, err := meshprovisionerclient.ParseKey(ck)
|
||||||
if err != nil || name == "" {
|
if err != nil || name == "" {
|
||||||
continue
|
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}
|
Module: cm, Local: cl, VaultName: name}
|
||||||
rec, held, err := v.Store.Read(name)
|
rec, held, err := v.Store.Read(name)
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
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:
|
case held:
|
||||||
o.State, o.Generation = provisions.Provided, rec.Generation
|
o.State, o.Generation = meshprovisionerclient.Provided, rec.Generation
|
||||||
case p.CredentialForm == FormIssued:
|
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:
|
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:
|
default:
|
||||||
continue // not made yet: the key stays asked until the pair pass makes it
|
continue // not made yet: the key stays asked until the pair pass makes it
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/catalogue"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
"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
|
// 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.
|
// §1, §6): a provision `secret-<provision>` whose provider is the vault, in state asked.
|
||||||
func credentialAsked(raw []byte) bool {
|
func credentialAsked(raw []byte) bool {
|
||||||
p, err := provisions.Decode(raw)
|
p, err := meshprovisionerclient.Decode(raw)
|
||||||
return err == nil && p.State == provisions.Asked && p.Provider.Module == provisions.VaultModule &&
|
return err == nil && p.State == meshprovisionerclient.Asked && p.Provider.Module == meshprovisionerclient.VaultModule &&
|
||||||
strings.HasPrefix(p.Provision, catalogue.CredentialProvisionPrefix)
|
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
|
// 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).
|
// 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(),
|
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,
|
return &views.Follower{Owner: delivery.AssignerModule, Tool: PlacementTool, State: "placement", Logf: say,
|
||||||
Ask: func(_ context.Context, after string) (views.Answer, error) {
|
Ask: func(_ context.Context, after string) (views.Answer, error) {
|
||||||
body := map[string]any{"values": true}
|
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) {
|
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,
|
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)
|
s.Asked.Format(time.RFC3339), s.Error)
|
||||||
if k, v, err := views.UnansweredFinding(Seat, s, time.Now()); err == nil {
|
f := views.UnansweredFinding(Seat, s, time.Now())
|
||||||
if err := put(k, v); err != nil {
|
if err := findings.Put(f); err != nil {
|
||||||
say("the finding %s is not written: %v", k, err)
|
say("the finding %s is not written: %v", f.ID, err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Clear: func(s views.Status) {
|
Clear: func(s views.Status) {
|
||||||
if k, _, err := views.UnansweredFinding(Seat, s, time.Now()); err == nil {
|
_ = findings.Delete(views.UnansweredFinding(Seat, s, time.Now()))
|
||||||
_ = drop(k)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Changed: func(key string, _ json.RawMessage, deleted bool, _ time.Time) {
|
Changed: func(key string, _ json.RawMessage, deleted bool, _ time.Time) {
|
||||||
if deleted {
|
if deleted {
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ func TestAnUnansweredFirstViewOfThePlacementIsAFindingTheAnswerClears(t *testing
|
|||||||
Values: map[string]json.RawMessage{"g14.node": json.RawMessage(`{}`)}})
|
Values: map[string]json.RawMessage{"g14.node": json.RawMessage(`{}`)}})
|
||||||
return raw, nil
|
return raw, nil
|
||||||
}
|
}
|
||||||
put := func(k string, v json.RawMessage) error { mu.Lock(); held[k] = v; mu.Unlock(); return nil }
|
f := newPlacementView(ask, func(string) {}, nil, heldFindings{&mu, held})
|
||||||
drop := func(k string) error { mu.Lock(); delete(held, k); mu.Unlock(); return nil }
|
|
||||||
f := newPlacementView(ask, func(string) {}, nil, put, drop)
|
|
||||||
f.Bound, f.Wait = 30*time.Millisecond, 10*time.Millisecond
|
f.Bound, f.Wait = 30*time.Millisecond, 10*time.Millisecond
|
||||||
reader := viewPlacement(func() *views.Follower { return f })
|
reader := viewPlacement(func() *views.Follower { return f })
|
||||||
if _, err := reader.Keys(); err == nil {
|
if _, err := reader.Keys(); err == nil {
|
||||||
@@ -57,3 +55,22 @@ func TestAnUnansweredFirstViewOfThePlacementIsAFindingTheAnswerClears(t *testing
|
|||||||
t.Errorf("keys %v, %v", keys, err)
|
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).Watch(", "", 1))
|
||||||
code = []byte(strings.Replace(string(code), "stdio.State(WantedRead).Get(", "", 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.
|
// 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), "meshconditionsclient.FindingWriter{State: stdio.State(FindingsState)}", "", 1))
|
||||||
code = []byte(strings.Replace(string(code), "stdio.State(FindingsState).Delete(", "", 1))
|
|
||||||
// And its outcome entries (novox/hq ADR 0570 clauses 3, 6): a value's name and generation, never a value; and
|
// 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.
|
// one read of the seat's provisions, which hold none.
|
||||||
for _, call := range []string{"Get(", "Put(", "Delete(", "Keys("} {
|
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(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).
|
// 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 {
|
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)
|
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
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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-contracts/go/seats/meshvault v0.0.0-20261104174344-ae5266e77639
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
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-server/v2 v2.11.17
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ package main
|
|||||||
// COMMIT (novox/hq ADR 0230).
|
// COMMIT (novox/hq ADR 0230).
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"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.
|
// that cannot be read is not nobody asking (issue 241): nothing is retired until it is read again.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProvisionName is what postgres provides, as its manifest says.
|
// ProvisionName is what postgres provides, as its manifest says.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"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.
|
// of it — the login refused, the data kept, a deletion dropping only its own — is live_test.go's.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider/harness"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module postgres
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
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
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||||
github.com/jackc/pgx/v5 v5.11.0
|
github.com/jackc/pgx/v5 v5.11.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func say(format string, args ...any) {
|
func say(format string, args ...any) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ package main
|
|||||||
// mesh still gives it.
|
// mesh still gives it.
|
||||||
|
|
||||||
import (
|
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.
|
// ProvisionName is what route-adapter provides, as its manifest says.
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
|
||||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
// bucketBus is the helper's bus over in-memory buckets.
|
// 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",
|
"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",
|
"names": map[string]any{"name": "git.example.org", "internal-name": "git.novox.internal",
|
||||||
"name-web": "git.example.org"}}}}})
|
"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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// The key the seat composed for the entry, asked of route-adapter (novox/hq ADR 0570 clauses 1, 3).
|
// 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, "")
|
key, _ := meshprovisionerclient.Key("g14", "gitea", ProvisionName, "")
|
||||||
asked, err := provisions.Encode(provisions.Provision{Provision: ProvisionName, State: provisions.Asked,
|
asked, err := meshprovisionerclient.Encode(meshprovisionerclient.Provision{Provision: ProvisionName, State: meshprovisionerclient.Asked,
|
||||||
Consumer: provisions.End{Node: "g14", Module: "gitea"}, Provider: provisions.End{Node: "novox", Module: ModuleName}})
|
Consumer: meshprovisionerclient.End{Node: "g14", Module: "gitea"}, Provider: meshprovisionerclient.End{Node: "novox", Module: ModuleName}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
p := newProvider("novox", bus, t.Logf)
|
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-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||||
|
|
||||||
require (
|
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
|
git.novox.be/novox/mesh-contracts/go v0.0.0-20261104174344-ae5266e77639 // indirect
|
||||||
github.com/klauspost/compress v1.20.1 // indirect
|
github.com/klauspost/compress v1.20.1 // indirect
|
||||||
github.com/nats-io/nats.go v1.54.0 // indirect
|
github.com/nats-io/nats.go v1.54.0 // indirect
|
||||||
|
|||||||
Reference in New Issue
Block a user