Хорошо, так что шаблон, который я не видел, был довольно очевиден: «самый глубокий лист» конфигурации перезаписывает все ниже, либо с заданными данными, либо с значениями по умолчанию go для пустых значений:
Это означает, чточто структура, подобная этой ...
[key_string]:
input:
listener: [string]
static: [string]
output:
listener: [string]
details:
filter: [string]
retention: [string]
... по умолчанию с данными ...
defaults:
input:
listener: 127.0.0.1:8910
static: default
output:
listener: 127.0.0.1:8989
details:
filter: '*foo*'
retention: 3h
... подается с yaml этой формы ..
empty:
both:
input:
output:
input: &input
input:
input-modified-with-anchor:
<<: *input
input:
static: NOTDEFAULT
input-modified-without-anchor:
input:
static: NOTDEFAULT
output: &output
output:
output-modified-with-anchor:
<<: *output
output:
details:
filter: NOTDEFAULT
output-modified-without-anchor:
output:
details:
filter: NOTDEFAULT
... получается как ...
both:
input:
listener: ""
static: ""
output:
listener: ""
details:
filter: ""
retention: ""
empty:
input:
listener: ""
static: ""
output:
listener: ""
details:
filter: ""
retention: ""
input:
input:
listener: ""
static: ""
output:
listener: 127.0.0.1:8989
details:
filter: '*foo*'
retention: 3h
input-modified-with-anchor:
input:
listener: 127.0.0.1:8910
static: NOTDEFAULT
output:
listener: 127.0.0.1:8989
details:
filter: '*foo*'
retention: 3h
input-modified-without-anchor:
input:
listener: 127.0.0.1:8910
static: NOTDEFAULT
output:
listener: 127.0.0.1:8989
details:
filter: '*foo*'
retention: 3h
Для моего случая использования это слишком сложное поведение, поэтому я пытаюсь использовать другой подход: я, если требуется, ввожу значение по умолчаниюconfig в yaml и поместите ссылку на его якорь в каждом разделе.Я чувствую, что это более прозрачно и воспроизводимо для конечного пользователя.Вот ужасный чертёж функции:
func injectYAML(data []byte) ([]byte, error) {
// render a default section an add an anchor
key := "injected_defaults"
defaultData := Config{key: Defaults()}
var defaultSection []byte
defaultSection, _ = yaml.Marshal(defaultData)
defaultSection = bytes.Replace(defaultSection, []byte(key+":"), []byte(key+": &"+key), 1)
// get list of sections in input data
c := Config{}
err := yaml.Unmarshal(data, &c)
if err != nil {
return data, fmt.Errorf("Error while reading sections from yaml: %s", err.Error())
}
// remove "---" at beginning when present
data = bytes.TrimLeft(data, "---")
// add reference to default section to each section
lines := bytes.Split(data, []byte("\n"))
var updatedLines [][]byte
for _, line := range lines {
updatedLines = append(updatedLines, line)
for section := range c {
if bytes.HasPrefix(line, []byte(section+":")) {
updatedLines = append(updatedLines, []byte(" <<: *"+key))
}
}
}
updatedData := bytes.Join(updatedLines, []byte("\n"))
// compose injected yaml
out := []byte("---\n")
out = append(out, defaultSection...)
out = append(out, updatedData...)
return out, nil
}
Полный пример по адресу: https://github.com/unprofession-al/configuration/tree/7c2eb7da58b51f52b50f2a0fbac193c799c9eb08