List every module's preferences in the settings verb (hq ADR 0262)
One verb is the interface to every preference, so no module builds a settings tool of its own: each key, its default and why, and every assigned machine's value with its source.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/broker"
|
||||
@@ -396,7 +397,8 @@ func assignCommand(ctx context.Context, verb string, args []string) error {
|
||||
func settingsCommand(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("settings show <module> [--node <node>] [--history], settings set <module> <file> " +
|
||||
"[--node <node>] [--replace], or settings clear <module> [--node <node>]")
|
||||
"[--node <node>] [--replace], settings clear <module> [--node <node>], or settings preferences " +
|
||||
"[<module>] [--node <node>]")
|
||||
}
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
@@ -536,6 +538,46 @@ func settingsCommand(ctx context.Context, args []string) error {
|
||||
fmt.Print(describeEffective(positionals[0], where, catalogue.Effective(m, layers)))
|
||||
return nil
|
||||
|
||||
case "preferences":
|
||||
// Every module's preferences, and each machine's value with its source (novox/hq ADR 0262).
|
||||
if len(positionals) > 1 {
|
||||
return errors.New("settings preferences [<module>] [--node <node>]")
|
||||
}
|
||||
only := ""
|
||||
if len(positionals) == 1 {
|
||||
only = positionals[0]
|
||||
}
|
||||
entries, err := inv.Catalogued(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var listed []preferencesOf
|
||||
for _, e := range entries {
|
||||
m := e.Manifest
|
||||
if len(m.Settings) == 0 || (only != "" && m.Module != only) {
|
||||
continue
|
||||
}
|
||||
p := preferencesOf{Manifest: m, On: map[string][]catalogue.SettingSource{}}
|
||||
for _, n := range e.On {
|
||||
if *node != "" && n != *node {
|
||||
continue
|
||||
}
|
||||
p.Nodes = append(p.Nodes, n)
|
||||
layers, err := inv.SettingsFor(ctx, n, m.Module)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.On[n] = catalogue.Effective(m, layers)
|
||||
}
|
||||
listed = append(listed, p)
|
||||
}
|
||||
if only != "" && len(listed) == 0 {
|
||||
fmt.Printf("%s declares no preferences\n", only)
|
||||
return nil
|
||||
}
|
||||
fmt.Print(describePreferences(listed))
|
||||
return nil
|
||||
|
||||
case "clear":
|
||||
if len(positionals) != 1 {
|
||||
return errors.New("settings clear <module> [--node <node>]")
|
||||
@@ -547,7 +589,7 @@ func settingsCommand(ctx context.Context, args []string) error {
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("settings has no %q; it has show, set and clear", args[0])
|
||||
return fmt.Errorf("settings has no %q; it has show, set, clear and preferences", args[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,6 +610,58 @@ func describeEffective(module, where string, values []catalogue.SettingSource) s
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// preferencesOf is one module's preferences and its value on each machine it is assigned to.
|
||||
type preferencesOf struct {
|
||||
Manifest catalogue.Manifest
|
||||
Nodes []string
|
||||
On map[string][]catalogue.SettingSource
|
||||
}
|
||||
|
||||
// describePreferences lists each module's preferences — key, default and why — and, per machine it is
|
||||
// assigned to, the value and where it comes from (novox/hq ADR 0262).
|
||||
func describePreferences(modules []preferencesOf) string {
|
||||
if len(modules) == 0 {
|
||||
return "no module declares a preference\n"
|
||||
}
|
||||
var b strings.Builder
|
||||
for i, p := range modules {
|
||||
if i > 0 {
|
||||
b.WriteString("\n")
|
||||
}
|
||||
on := "assigned nowhere"
|
||||
if len(p.Nodes) > 0 {
|
||||
on = "on " + strings.Join(p.Nodes, ", ")
|
||||
}
|
||||
fmt.Fprintf(&b, "%s (%s)\n", p.Manifest.Module, on)
|
||||
keys := make([]string, 0, len(p.Manifest.Settings))
|
||||
for k := range p.Manifest.Settings {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
d := p.Manifest.Settings[k]
|
||||
def, _ := json.Marshal(d.Default)
|
||||
fmt.Fprintf(&b, " %s, default %s: %s\n", k, def, d.Why)
|
||||
for _, n := range p.Nodes {
|
||||
for _, s := range p.On[n] {
|
||||
if s.Key != k {
|
||||
continue
|
||||
}
|
||||
v, _ := json.Marshal(s.Value)
|
||||
from := s.From
|
||||
if s.FromDefault {
|
||||
from = catalogue.DefaultLayer
|
||||
} else if s.From != catalogue.MeshWideLayer {
|
||||
from = "the node"
|
||||
}
|
||||
fmt.Fprintf(&b, " %s: %s (%s)\n", n, v, from)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// nodeFlag is ` --node <node>` for a machine's layer, nothing for the whole mesh's.
|
||||
func nodeFlag(node string) string {
|
||||
if node == "" {
|
||||
|
||||
Reference in New Issue
Block a user