mesh-provisioner: the provision's plan entry typed in the definition, and fixtures for its two states (ADR 0634 §6; issue 1966)

This commit is contained in:
2026-11-05 14:15:36 +01:00
parent 935a0c7753
commit 97b7da6128
5 changed files with 328 additions and 10 deletions
@@ -184,6 +184,25 @@ type ProvisionsByNodeConsumerProvisionValueConsumer struct {
Node string `json:"node,omitempty"`
}
// ProvisionsByNodeConsumerProvisionValuePlan is the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126).
type ProvisionsByNodeConsumerProvisionValuePlan struct {
As string `json:"as,omitempty"`
At string `json:"at,omitempty"`
// the provider's own contract's bytes, carried unread
Contributed map[string]any `json:"contributed,omitempty"`
Contributes map[string]any `json:"contributes,omitempty"`
CredentialForm any `json:"credential-form,omitempty"`
CredentialTool string `json:"credential-tool,omitempty"`
From string `json:"from,omitempty"`
Local string `json:"local,omitempty"`
Name string `json:"name,omitempty"`
Names map[string]string `json:"names,omitempty"`
Path string `json:"path,omitempty"`
Provider string `json:"provider"`
ProviderModule string `json:"provider-module"`
Provision string `json:"provision"`
}
// ProvisionsByNodeConsumerProvisionValueProvider is a type of the contract.
type ProvisionsByNodeConsumerProvisionValueProvider struct {
Module string `json:"module,omitempty"`
@@ -211,8 +230,8 @@ type ProvisionsByNodeConsumerProvisionValue struct {
Consumer ProvisionsByNodeConsumerProvisionValueConsumer `json:"consumer"`
DependsOn []string `json:"depends-on,omitempty"`
Local string `json:"local,omitempty"`
// the plan's provision
Plan map[string]any `json:"plan,omitempty"`
// the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126)
Plan *ProvisionsByNodeConsumerProvisionValuePlan `json:"plan,omitempty"`
Provider ProvisionsByNodeConsumerProvisionValueProvider `json:"provider"`
Provision string `json:"provision"`
Reference *ProvisionsByNodeConsumerProvisionValueReference `json:"reference,omitempty"`
@@ -237,6 +256,25 @@ type ProvisionsByNodeConsumerProvisionLocalValueConsumer struct {
Node string `json:"node,omitempty"`
}
// ProvisionsByNodeConsumerProvisionLocalValuePlan is the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126).
type ProvisionsByNodeConsumerProvisionLocalValuePlan struct {
As string `json:"as,omitempty"`
At string `json:"at,omitempty"`
// the provider's own contract's bytes, carried unread
Contributed map[string]any `json:"contributed,omitempty"`
Contributes map[string]any `json:"contributes,omitempty"`
CredentialForm any `json:"credential-form,omitempty"`
CredentialTool string `json:"credential-tool,omitempty"`
From string `json:"from,omitempty"`
Local string `json:"local,omitempty"`
Name string `json:"name,omitempty"`
Names map[string]string `json:"names,omitempty"`
Path string `json:"path,omitempty"`
Provider string `json:"provider"`
ProviderModule string `json:"provider-module"`
Provision string `json:"provision"`
}
// ProvisionsByNodeConsumerProvisionLocalValueProvider is a type of the contract.
type ProvisionsByNodeConsumerProvisionLocalValueProvider struct {
Module string `json:"module,omitempty"`
@@ -264,8 +302,8 @@ type ProvisionsByNodeConsumerProvisionLocalValue struct {
Consumer ProvisionsByNodeConsumerProvisionLocalValueConsumer `json:"consumer"`
DependsOn []string `json:"depends-on,omitempty"`
Local string `json:"local,omitempty"`
// the plan's provision
Plan map[string]any `json:"plan,omitempty"`
// the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126)
Plan *ProvisionsByNodeConsumerProvisionLocalValuePlan `json:"plan,omitempty"`
Provider ProvisionsByNodeConsumerProvisionLocalValueProvider `json:"provider"`
Provision string `json:"provision"`
Reference *ProvisionsByNodeConsumerProvisionLocalValueReference `json:"reference,omitempty"`
@@ -0,0 +1,66 @@
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
package meshprovisioner
import (
"encoding/json"
"reflect"
"regexp"
"testing"
)
// TestFixtures reads every fixture of seats/mesh-provisioner/fixtures: each decodes into its type, a field the type does not
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
func TestFixtures(t *testing.T) {
cases := []struct {
name, subject, given, wire string
decode func([]byte) (any, error)
subjectIs *regexp.Regexp
}{
{name: "a database provision provided to a consumer under a local name", subject: "", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"consumer\":{\"module\":\"keycloak\",\"node\":\"ace\"},\"depends-on\":[\"novox.postgres.secret-postgres-database\"],\"local\":\"main\",\"plan\":{\"credential-form\":\"pair\",\"local\":\"main\",\"names\":{\"name-main\":\"keycloak.example.internal\"},\"provider\":\"novox\",\"provider-module\":\"postgres\",\"provision\":\"postgres-database\"},\"provider\":{\"module\":\"postgres\",\"node\":\"novox\"},\"provision\":\"postgres-database\",\"reference\":{\"generation\":2,\"id\":\"pair/novox/postgres-database/ace/keycloak/main\"},\"state\":\"provided\",\"v\":1}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"consumer\":{\"module\":\"keycloak\",\"node\":\"ace\"},\"depends-on\":[\"novox.postgres.secret-postgres-database\"],\"local\":\"main\",\"plan\":{\"credential-form\":\"pair\",\"local\":\"main\",\"names\":{\"name-main\":\"keycloak.example.internal\"},\"provider\":\"novox\",\"provider-module\":\"postgres\",\"provision\":\"postgres-database\"},\"provider\":{\"module\":\"postgres\",\"node\":\"novox\"},\"provision\":\"postgres-database\",\"reference\":{\"generation\":2,\"id\":\"pair/novox/postgres-database/ace/keycloak/main\"},\"state\":\"provided\",\"v\":1}",
decode: func(b []byte) (any, error) {
var v ProvisionsByNodeConsumerProvisionLocalValue
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: nil},
{name: "a provision that waits for the operator", subject: "", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"consumer\":{\"module\":\"bazarr\",\"node\":\"ace\"},\"provider\":{\"module\":\"mesh-vault\",\"node\":\"novox\"},\"provision\":\"api-token\",\"state\":\"waits\",\"v\":1,\"why\":\"the token is the operator's to give\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"consumer\":{\"module\":\"bazarr\",\"node\":\"ace\"},\"provider\":{\"module\":\"mesh-vault\",\"node\":\"novox\"},\"provision\":\"api-token\",\"state\":\"waits\",\"v\":1,\"why\":\"the token is the operator's to give\"}",
decode: func(b []byte) (any, error) {
var v ProvisionsByNodeConsumerProvisionValue
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: nil},
{name: "a retiring provision with the operator's warrant", subject: "", given: "{\"ask\":\"a7\",\"at\":\"2026-11-04T12:00:00Z\",\"consumer\":{\"module\":\"old-app\",\"node\":\"ace\"},\"provider\":{\"module\":\"postgres\",\"node\":\"novox\"},\"provision\":\"postgres-database\",\"state\":\"retiring\",\"v\":1,\"warrant\":{\"ask\":\"a7\",\"at\":\"2026-11-04T13:00:00Z\",\"by\":\"operator\",\"option\":\"retire\"},\"why\":\"no want needs it\"}", wire: "{\"ask\":\"a7\",\"at\":\"2026-11-04T12:00:00Z\",\"consumer\":{\"module\":\"old-app\",\"node\":\"ace\"},\"provider\":{\"module\":\"postgres\",\"node\":\"novox\"},\"provision\":\"postgres-database\",\"state\":\"retiring\",\"v\":1,\"warrant\":{\"ask\":\"a7\",\"at\":\"2026-11-04T13:00:00Z\",\"by\":\"operator\",\"option\":\"retire\"},\"why\":\"no want needs it\"}",
decode: func(b []byte) (any, error) {
var v ProvisionsByNodeConsumerProvisionValue
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: nil},
{name: "a module's plan on a node", subject: "", given: "{\"module\":\"keycloak\",\"node\":\"ace\",\"plan\":{\"provisions\":[{\"credential-form\":\"pair\",\"provider\":\"novox\",\"provider-module\":\"postgres\",\"provision\":\"postgres-database\"}]},\"v\":1}", wire: "{\"module\":\"keycloak\",\"node\":\"ace\",\"plan\":{\"provisions\":[{\"credential-form\":\"pair\",\"provider\":\"novox\",\"provider-module\":\"postgres\",\"provision\":\"postgres-database\"}]},\"v\":1}",
decode: func(b []byte) (any, error) { var v PlansByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
}
for _, c := range cases {
v, err := c.decode([]byte(c.given))
if err != nil {
t.Errorf("%s: does not decode: %v", c.name, err)
continue
}
back, err := json.Marshal(v)
if err != nil {
t.Errorf("%s: does not encode: %v", c.name, err)
continue
}
var got, want any
if err := json.Unmarshal(back, &got); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, want) {
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
}
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
}
}
}
@@ -0,0 +1,56 @@
[
{
"name": "a database provision provided to a consumer under a local name",
"of": "state:provisions/<node>.<consumer>.<provision>.<local>",
"given": {
"v": 1,
"provision": "postgres-database",
"local": "main",
"consumer": {"module": "keycloak", "node": "ace"},
"provider": {"module": "postgres", "node": "novox"},
"depends-on": ["novox.postgres.secret-postgres-database"],
"state": "provided",
"reference": {"id": "pair/novox/postgres-database/ace/keycloak/main", "generation": 2},
"plan": {"provision": "postgres-database", "local": "main", "provider": "novox", "provider-module": "postgres", "credential-form": "pair", "names": {"name-main": "keycloak.example.internal"}},
"at": "2026-11-04T12:00:00Z"
}
},
{
"name": "a provision that waits for the operator",
"of": "state:provisions/<node>.<consumer>.<provision>",
"given": {
"v": 1,
"provision": "api-token",
"consumer": {"module": "bazarr", "node": "ace"},
"provider": {"module": "mesh-vault", "node": "novox"},
"state": "waits",
"why": "the token is the operator's to give",
"at": "2026-11-04T12:00:00Z"
}
},
{
"name": "a retiring provision with the operator's warrant",
"of": "state:provisions/<node>.<consumer>.<provision>",
"given": {
"v": 1,
"provision": "postgres-database",
"consumer": {"module": "old-app", "node": "ace"},
"provider": {"module": "postgres", "node": "novox"},
"state": "retiring",
"why": "no want needs it",
"ask": "a7",
"warrant": {"ask": "a7", "option": "retire", "by": "operator", "at": "2026-11-04T13:00:00Z"},
"at": "2026-11-04T12:00:00Z"
}
},
{
"name": "a module's plan on a node",
"of": "state:plans/<node>.<module>",
"given": {
"v": 1,
"node": "ace",
"module": "keycloak",
"plan": {"provisions": [{"provision": "postgres-database", "provider": "novox", "provider-module": "postgres", "credential-form": "pair"}]}
}
}
]
+122 -2
View File
@@ -168,7 +168,67 @@
},
"plan": {
"type": "object",
"description": "the plan's provision"
"description": "the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126)",
"properties": {
"name": {
"type": "string"
},
"provision": {
"type": "string"
},
"local": {
"type": "string"
},
"provider": {
"type": "string"
},
"provider-module": {
"type": "string"
},
"credential-form": {
"enum": [
"pair",
"shared",
"issued",
"given",
"none"
]
},
"credential-tool": {
"type": "string"
},
"path": {
"type": "string"
},
"contributes": {
"type": "object"
},
"at": {
"type": "string"
},
"from": {
"type": "string"
},
"as": {
"type": "string"
},
"names": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"contributed": {
"type": "object",
"description": "the provider's own contract's bytes, carried unread"
}
},
"required": [
"provision",
"provider",
"provider-module"
],
"additionalProperties": false
},
"at": {
"type": "string",
@@ -275,7 +335,67 @@
},
"plan": {
"type": "object",
"description": "the plan's provision"
"description": "the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126)",
"properties": {
"name": {
"type": "string"
},
"provision": {
"type": "string"
},
"local": {
"type": "string"
},
"provider": {
"type": "string"
},
"provider-module": {
"type": "string"
},
"credential-form": {
"enum": [
"pair",
"shared",
"issued",
"given",
"none"
]
},
"credential-tool": {
"type": "string"
},
"path": {
"type": "string"
},
"contributes": {
"type": "object"
},
"at": {
"type": "string"
},
"from": {
"type": "string"
},
"as": {
"type": "string"
},
"names": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"contributed": {
"type": "object",
"description": "the provider's own contract's bytes, carried unread"
}
},
"required": [
"provision",
"provider",
"provider-module"
],
"additionalProperties": false
},
"at": {
"type": "string",
@@ -111,6 +111,25 @@ export interface ProvisionsByNodeConsumerProvisionValueConsumer {
"node"?: string;
}
/** the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126) */
export interface ProvisionsByNodeConsumerProvisionValuePlan {
"as"?: string;
"at"?: string;
/** the provider's own contract's bytes, carried unread */
"contributed"?: Record<string, unknown>;
"contributes"?: Record<string, unknown>;
"credential-form"?: unknown;
"credential-tool"?: string;
"from"?: string;
"local"?: string;
"name"?: string;
"names"?: Record<string, string>;
"path"?: string;
"provider": string;
"provider-module": string;
"provision": string;
}
export interface ProvisionsByNodeConsumerProvisionValueProvider {
"module"?: string;
"node"?: string;
@@ -135,8 +154,8 @@ export interface ProvisionsByNodeConsumerProvisionValue {
"consumer": ProvisionsByNodeConsumerProvisionValueConsumer;
"depends-on"?: string[];
"local"?: string;
/** the plan's provision */
"plan"?: Record<string, unknown>;
/** the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126) */
"plan"?: ProvisionsByNodeConsumerProvisionValuePlan;
"provider": ProvisionsByNodeConsumerProvisionValueProvider;
"provision": string;
"reference"?: ProvisionsByNodeConsumerProvisionValueReference;
@@ -151,6 +170,25 @@ export interface ProvisionsByNodeConsumerProvisionLocalValueConsumer {
"node"?: string;
}
/** the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126) */
export interface ProvisionsByNodeConsumerProvisionLocalValuePlan {
"as"?: string;
"at"?: string;
/** the provider's own contract's bytes, carried unread */
"contributed"?: Record<string, unknown>;
"contributes"?: Record<string, unknown>;
"credential-form"?: unknown;
"credential-tool"?: string;
"from"?: string;
"local"?: string;
"name"?: string;
"names"?: Record<string, string>;
"path"?: string;
"provider": string;
"provider-module": string;
"provision": string;
}
export interface ProvisionsByNodeConsumerProvisionLocalValueProvider {
"module"?: string;
"node"?: string;
@@ -175,8 +213,8 @@ export interface ProvisionsByNodeConsumerProvisionLocalValue {
"consumer": ProvisionsByNodeConsumerProvisionLocalValueConsumer;
"depends-on"?: string[];
"local"?: string;
/** the plan's provision */
"plan"?: Record<string, unknown>;
/** the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126) */
"plan"?: ProvisionsByNodeConsumerProvisionLocalValuePlan;
"provider": ProvisionsByNodeConsumerProvisionLocalValueProvider;
"provision": string;
"reference"?: ProvisionsByNodeConsumerProvisionLocalValueReference;