Read stored manifests leniently and mark the defaults layer (hq ADR 0262 review)
A strict read of the stored catalogue fails every plan and send once a manifest uses a field an older controller lacks; registration stays strict. A node named default lost its layer to the name check. Judge the operator's keys by whole words, and scan a default under any key.
This commit is contained in:
@@ -37,6 +37,7 @@ type SettingDeclaration struct {
|
||||
const KindPreference = "preference"
|
||||
|
||||
// DefaultLayer is what the layer of a module's own defaults is called where a value's source is said.
|
||||
// Only said: the layer is recognised by Layer.Default, never by this name, which a node may also have.
|
||||
const DefaultLayer = "default"
|
||||
|
||||
// meshWords are the settings keys the mesh reads itself; a module declares none of them.
|
||||
@@ -45,11 +46,33 @@ var meshWords = map[string]bool{
|
||||
PlacesSetting: true, AccessesSetting: true, NetworksSetting: true,
|
||||
}
|
||||
|
||||
// operatorsOwn are words that, in a key's name, say its value is the operator's and never a
|
||||
// preference: a default for one would be the literal ADR 0112 removed from definitions.
|
||||
var operatorsOwn = []string{
|
||||
"domain", "host", "issuer", "url", "email", "mail", "address", "identity", "login", "user",
|
||||
"account", "password", "secret", "token", "credential",
|
||||
// operatorsOwn are words that, as a whole word of a key's name, say its value is the operator's and
|
||||
// never a preference: a default for one would be the literal ADR 0112 removed from definitions. Whole
|
||||
// words, split at dashes, underscores and dots, so `max-tokens`, `show-hostname` and `mailbox-size` are
|
||||
// preferences and `api-token`, `host` and `mail-domain` are not.
|
||||
var operatorsOwn = map[string]bool{
|
||||
"domain": true, "host": true, "fqdn": true, "zone": true, "realm": true, "tenant": true,
|
||||
"issuer": true, "url": true, "uri": true, "webhook": true, "origin": true, "dsn": true, "ip": true,
|
||||
"email": true, "mail": true, "phone": true, "address": true,
|
||||
"identity": true, "login": true, "user": true, "username": true, "account": true, "owner": true,
|
||||
"uid": true, "gid": true, "puid": true, "pgid": true,
|
||||
"password": true, "pass": true, "passwd": true, "passphrase": true, "secret": true, "token": true,
|
||||
"key": true, "credential": true,
|
||||
}
|
||||
|
||||
// operatorsWord is the word of a key's name that says its value is the operator's, or "".
|
||||
func operatorsWord(key string) string {
|
||||
words := strings.FieldsFunc(key, func(r rune) bool { return r == '-' || r == '_' || r == '.' })
|
||||
for i, w := range words {
|
||||
if operatorsOwn[w] {
|
||||
return w
|
||||
}
|
||||
// An identifier a client is known by: `client-id`, `oauth-client-id`.
|
||||
if w == "client" && i+1 < len(words) && words[i+1] == "id" {
|
||||
return "client-id"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SettingProblems is every way a definition's setting defaults are wrong, in its own words.
|
||||
@@ -75,12 +98,9 @@ func SettingProblems(m Manifest) []string {
|
||||
if d.Kind != KindPreference {
|
||||
say("kind is %q, and only a %q has a default (novox/hq ADR 0262)", d.Kind, KindPreference)
|
||||
}
|
||||
for _, word := range operatorsOwn {
|
||||
if strings.Contains(key, word) {
|
||||
say("a key naming %q is the operator's value, and has no default — it is the "+
|
||||
"assignment's, never the definition's (novox/hq ADR 0112, ADR 0262)", word)
|
||||
break
|
||||
}
|
||||
if word := operatorsWord(key); word != "" {
|
||||
say("a key naming %q is the operator's value, and has no default — it is the "+
|
||||
"assignment's, never the definition's (novox/hq ADR 0112, ADR 0262)", word)
|
||||
}
|
||||
switch v := d.Default.(type) {
|
||||
case string:
|
||||
@@ -114,7 +134,7 @@ func Defaults(m Manifest) (Layer, bool) {
|
||||
values[key] = d.Default
|
||||
}
|
||||
}
|
||||
return Layer{From: DefaultLayer, Values: values}, len(values) > 0
|
||||
return Layer{From: DefaultLayer, Values: values, Default: true}, len(values) > 0
|
||||
}
|
||||
|
||||
// WithDefaults is a module's layers with its defaults under them, for filling `${setting:<key>}`.
|
||||
@@ -132,6 +152,8 @@ type SettingSource struct {
|
||||
Value any
|
||||
// From is DefaultLayer, MeshWideLayer or the node's name.
|
||||
From string
|
||||
// FromDefault is whether the value is the module's default, whatever From reads.
|
||||
FromDefault bool
|
||||
// Default is the module's default, when it gives one.
|
||||
Default any
|
||||
HasDefault bool
|
||||
@@ -142,7 +164,8 @@ type SettingSource struct {
|
||||
func Effective(m Manifest, layers []Layer) []SettingSource {
|
||||
byKey := map[string]*SettingSource{}
|
||||
for key, d := range m.Settings {
|
||||
byKey[key] = &SettingSource{Key: key, Value: d.Default, From: DefaultLayer, Default: d.Default, HasDefault: true}
|
||||
byKey[key] = &SettingSource{Key: key, Value: d.Default, From: DefaultLayer, FromDefault: true,
|
||||
Default: d.Default, HasDefault: true}
|
||||
}
|
||||
for _, layer := range layers {
|
||||
for key, v := range layer.Values {
|
||||
@@ -151,7 +174,7 @@ func Effective(m Manifest, layers []Layer) []SettingSource {
|
||||
s = &SettingSource{Key: key}
|
||||
byKey[key] = s
|
||||
}
|
||||
s.Value, s.From = v, layer.From
|
||||
s.Value, s.From, s.FromDefault = v, layer.From, layer.Default
|
||||
}
|
||||
}
|
||||
out := make([]SettingSource, 0, len(byKey))
|
||||
|
||||
Reference in New Issue
Block a user