mesh/delivery-group group feat/sdk1 rejected: composed together: the checker's verdict at the members' heads (428d048dd50a): unit: git.novox.be/novox/mesh-sdk/…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: unit: git.novox.be/novox/mesh-sdk/go/process.TestStatePutWritesAKeyWhole: a Put through a short blackout: 0, mesh-mail's state send…
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
114 lines
4.1 KiB
Go
114 lines
4.1 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// OnlyWhenBound is a resource's field naming the optional requirement it is delivered only while
|
|
// bound (ADR 0330 §5). Said in the catalogue and dropped before the declaration is sent: the
|
|
// node-engine parses strictly and knows no such field.
|
|
const OnlyWhenBound = "only-when-bound"
|
|
|
|
// requirementForm is one entry of `requires` in its long form.
|
|
type requirementForm struct {
|
|
Name string `json:"name"`
|
|
Optional bool `json:"optional,omitempty"`
|
|
// Shares is who else may use the consumer's bucket (BucketShare, novox/hq ADR 0468 §8), on s3-bucket only.
|
|
Shares []BucketShare `json:"shares,omitempty"`
|
|
}
|
|
|
|
// readRequires reads `requires` in both of its forms — a bare name, or {name, optional} — into the
|
|
// names and which of them are optional.
|
|
func readRequires(raw json.RawMessage) ([]string, map[string]bool, map[string][]BucketShare, error) {
|
|
var entries []json.RawMessage
|
|
if err := json.Unmarshal(raw, &entries); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("requires: a list of names, or of {name, optional}: %w", err)
|
|
}
|
|
var names []string
|
|
optional := map[string]bool{}
|
|
var shares map[string][]BucketShare
|
|
for i, e := range entries {
|
|
var plain string
|
|
if err := json.Unmarshal(e, &plain); err == nil {
|
|
names = append(names, plain)
|
|
continue
|
|
}
|
|
var full requirementForm
|
|
dec := json.NewDecoder(bytes.NewReader(e))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&full); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("requires[%d]: a name, or {name, optional} (novox/hq ADR 0330): %w", i, err)
|
|
}
|
|
if strings.TrimSpace(full.Name) == "" {
|
|
return nil, nil, nil, fmt.Errorf("requires[%d] names no requirement: {name, optional}", i)
|
|
}
|
|
names = append(names, full.Name)
|
|
if err := checkBucketShares(full.Name, full.Shares); err != nil {
|
|
return nil, nil, nil, fmt.Errorf("requires[%d]: %w", i, err)
|
|
}
|
|
if len(full.Shares) > 0 {
|
|
if shares == nil {
|
|
shares = map[string][]BucketShare{}
|
|
}
|
|
shares[full.Name] = full.Shares
|
|
}
|
|
if full.Optional {
|
|
optional[full.Name] = true
|
|
}
|
|
}
|
|
if len(optional) == 0 {
|
|
optional = nil
|
|
}
|
|
return names, optional, shares, nil
|
|
}
|
|
|
|
// OptionalRequiresKey is where a stored manifest keeps which of its requirements are optional: beside
|
|
// `requires`, which stays a plain list of names (mesh-controller #281's review). A controller older than
|
|
// optional requirements reads `requires` as names; the long form inside it failed that read, and with it
|
|
// the whole catalogue, so a rollback below this controller would have composed no machine. An older
|
|
// controller sets this unknown key aside instead and leaves only the module that carries it out. A
|
|
// manifest may be written either way; it is stored this way.
|
|
const OptionalRequiresKey = "optional-requires"
|
|
|
|
// readOptionalRequires reads the stored form's list of optional requirements.
|
|
func readOptionalRequires(raw json.RawMessage) (map[string]bool, error) {
|
|
var names []string
|
|
if err := json.Unmarshal(raw, &names); err != nil {
|
|
return nil, fmt.Errorf("%s: a list of the names in `requires` that are optional: %w", OptionalRequiresKey, err)
|
|
}
|
|
out := map[string]bool{}
|
|
for _, n := range names {
|
|
out[n] = true
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// optionalRequiresList is the stored form's list: the optional requirements, sorted.
|
|
func optionalRequiresList(m Manifest) []string {
|
|
var out []string
|
|
for name, optional := range m.OptionalRequires {
|
|
if optional {
|
|
out = append(out, name)
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// IsOptional is whether this module requires the name optionally (ADR 0330 §1). A name it contributes
|
|
// to without requiring, or requires as a bare name, is hard.
|
|
func (m Manifest) IsOptional(requirement string) bool { return m.OptionalRequires[requirement] }
|
|
|
|
// Absent is one optional requirement of one consumer that nothing assigned answers (ADR 0330 §3):
|
|
// left unbound rather than refused, and listed by `plan` and `node` (§7).
|
|
type Absent struct {
|
|
// Consumer is the module that requires it optionally.
|
|
Consumer string `json:"consumer"`
|
|
// Requirement is what it requires.
|
|
Requirement string `json:"requirement"`
|
|
}
|