Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a74896114 |
No files matched your search
+1205
-3
File diff suppressed because it is too large.
Load diff
@@ -46,6 +46,7 @@ var (
|
||||
errNeedNumericIndex = errors.New("key is a list; use a numeric index")
|
||||
errIndexOutOfRange = errors.New("index out of range")
|
||||
errNotMapOrList = errors.New("key is not a map or list")
|
||||
errNoSuchSetting = errors.New("no setting has this key")
|
||||
errWouldNotLoad = errors.New(
|
||||
"vaultik would not load the config after this change, " +
|
||||
"so the file was not changed")
|
||||
@@ -483,9 +484,9 @@ func writeConfigSet(out *ui.Writer, path, key, value string) error {
|
||||
|
||||
keys := strings.Split(key, ".")
|
||||
|
||||
_, err = config.SettingType(keys)
|
||||
if err != nil {
|
||||
return err
|
||||
_, ok := config.SettingType(keys)
|
||||
if !ok {
|
||||
return fmt.Errorf("%w: %s", errNoSuchSetting, key)
|
||||
}
|
||||
|
||||
err = yamlPathSet(root, keys, value)
|
||||
@@ -745,9 +746,9 @@ func yamlPathSet(root *yaml.Node, keys []string, value string) error {
|
||||
// configKeyIsString reports whether the dotted key names a string in
|
||||
// config.Config, as s3.access_key_id and snapshots.home.exclude.0 do.
|
||||
func configKeyIsString(keys []string) bool {
|
||||
typ, err := config.SettingType(keys)
|
||||
typ, ok := config.SettingType(keys)
|
||||
|
||||
return err == nil && typ.Kind() == reflect.String
|
||||
return ok && typ.Kind() == reflect.String
|
||||
}
|
||||
|
||||
// yamlSetInMapping resolves (creating if needed) the value node for the
|
||||
|
||||
@@ -240,11 +240,11 @@ func TestConfigSetKeepsFileThatWouldNotLoad(t *testing.T) {
|
||||
// Keys that no setting has, on a config that loads and on one
|
||||
// that does not.
|
||||
{configWithoutRecipients, "nosuchkey", "1",
|
||||
`unknown config key "nosuchkey"`},
|
||||
"no setting has this key: nosuchkey"},
|
||||
{configWithoutRecipients, "snapshots.home.exlude", "/tmp",
|
||||
`unknown config key "snapshots.home.exlude"`},
|
||||
"no setting has this key: snapshots.home.exlude"},
|
||||
{defaultConfigText(macOS), "compression_lvl", "9",
|
||||
`unknown config key "compression_lvl"`},
|
||||
"no setting has this key: compression_lvl"},
|
||||
// Index 1 of the empty list the missing key gives.
|
||||
{configWithoutRecipients, "age_recipients.1", testRecipient,
|
||||
"index out of range"},
|
||||
|
||||
@@ -300,7 +300,7 @@ func Parse(data []byte) (*Config, error) {
|
||||
return nil, fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
|
||||
err = checkKeys(&root, reflect.TypeFor[Config](), "", map[*yaml.Node]bool{})
|
||||
err = checkKeys(&root, reflect.TypeFor[Config](), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+16
-33
@@ -19,16 +19,13 @@ const yamlMergeTag = "!!merge"
|
||||
// so its keys are not checked.
|
||||
const envSection = "env"
|
||||
|
||||
var (
|
||||
errUnknownKey = errors.New("unknown config key")
|
||||
errAnchorContainsItself = errors.New("anchor contains itself")
|
||||
)
|
||||
var errUnknownKey = errors.New("unknown config key")
|
||||
|
||||
// SettingType returns the type of the setting that the dotted key names in
|
||||
// Config, following the fields' yaml tags, as for s3.bucket or
|
||||
// snapshots.home.exclude.0. A snapshot name or a list index may be any
|
||||
// word. It returns an unknown key error when no setting has that key.
|
||||
func SettingType(keys []string) (reflect.Type, error) {
|
||||
// word. It returns false when no setting has that key.
|
||||
func SettingType(keys []string) (reflect.Type, bool) {
|
||||
typ := reflect.TypeFor[Config]()
|
||||
|
||||
for _, key := range keys {
|
||||
@@ -38,16 +35,16 @@ func SettingType(keys []string) (reflect.Type, error) {
|
||||
case typ.Kind() == reflect.Struct:
|
||||
field, ok := yamlField(typ, key)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w %q", errUnknownKey, strings.Join(keys, "."))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
typ = field.Type
|
||||
default:
|
||||
return nil, fmt.Errorf("%w %q", errUnknownKey, strings.Join(keys, "."))
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return typ, nil
|
||||
return typ, true
|
||||
}
|
||||
|
||||
// yamlField returns the field of struct type typ whose yaml tag names key.
|
||||
@@ -65,43 +62,31 @@ func yamlField(typ reflect.Type, key string) (reflect.StructField, bool) {
|
||||
// checkKeys returns an error naming the first key in node, a parsed config
|
||||
// or part of one at the dotted path, that no setting in typ has. The error
|
||||
// gives the key's line in the file, when it has one. A value of the wrong
|
||||
// type is left for the YAML decoder to report. following holds the aliases
|
||||
// the walk is inside. Meeting one of them again means its anchor contains
|
||||
// itself, which is an error.
|
||||
func checkKeys(node *yaml.Node, typ reflect.Type, path string,
|
||||
following map[*yaml.Node]bool,
|
||||
) error {
|
||||
// type is left for the YAML decoder to report.
|
||||
func checkKeys(node *yaml.Node, typ reflect.Type, path string) error {
|
||||
switch node.Kind {
|
||||
case yaml.DocumentNode:
|
||||
for _, child := range node.Content {
|
||||
err := checkKeys(child, typ, path, following)
|
||||
err := checkKeys(child, typ, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case yaml.AliasNode:
|
||||
if following[node] {
|
||||
return fmt.Errorf("%w: %q on line %d",
|
||||
errAnchorContainsItself, node.Value, node.Line)
|
||||
}
|
||||
|
||||
following[node] = true
|
||||
defer delete(following, node)
|
||||
|
||||
return checkKeys(node.Alias, typ, path, following)
|
||||
return checkKeys(node.Alias, typ, path)
|
||||
case yaml.SequenceNode:
|
||||
if typ.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, child := range node.Content {
|
||||
err := checkKeys(child, typ.Elem(), joinKey(path, strconv.Itoa(i)), following)
|
||||
err := checkKeys(child, typ.Elem(), joinKey(path, strconv.Itoa(i)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case yaml.MappingNode:
|
||||
return checkMappingKeys(node, typ, path, following)
|
||||
return checkMappingKeys(node, typ, path)
|
||||
case yaml.ScalarNode:
|
||||
// A scalar has no keys.
|
||||
}
|
||||
@@ -120,13 +105,11 @@ func checkFilledKeys(data map[string]any) error {
|
||||
return fmt.Errorf("failed to marshal config data: %w", err)
|
||||
}
|
||||
|
||||
return checkKeys(&filled, reflect.TypeFor[Config](), "", map[*yaml.Node]bool{})
|
||||
return checkKeys(&filled, reflect.TypeFor[Config](), "")
|
||||
}
|
||||
|
||||
// checkMappingKeys is checkKeys for a map.
|
||||
func checkMappingKeys(node *yaml.Node, typ reflect.Type, path string,
|
||||
following map[*yaml.Node]bool,
|
||||
) error {
|
||||
func checkMappingKeys(node *yaml.Node, typ reflect.Type, path string) error {
|
||||
for i := 0; i+1 < len(node.Content); i += 2 {
|
||||
key, value := node.Content[i], node.Content[i+1]
|
||||
|
||||
@@ -138,7 +121,7 @@ func checkMappingKeys(node *yaml.Node, typ reflect.Type, path string,
|
||||
}
|
||||
|
||||
for _, m := range merged {
|
||||
err := checkKeys(m, typ, path, following)
|
||||
err := checkKeys(m, typ, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -174,7 +157,7 @@ func checkMappingKeys(node *yaml.Node, typ reflect.Type, path string,
|
||||
return nil
|
||||
}
|
||||
|
||||
err := checkKeys(value, valueType, keyPath, following)
|
||||
err := checkKeys(value, valueType, keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -65,25 +65,6 @@ func TestParseRefusesUnknownKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseRefusesAnchorThatContainsItself checks that a YAML merge key (<<)
|
||||
// naming the anchor of the map it sits in fails the parse with an error.
|
||||
func TestParseRefusesAnchorThatContainsItself(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
data := strings.Replace(keysTestConfig, " test:", " test: &test", 1) +
|
||||
" <<: *test\n"
|
||||
|
||||
_, err := Parse([]byte(data))
|
||||
if !errors.Is(err, errAnchorContainsItself) {
|
||||
t.Fatalf("Parse() error = %v, want errAnchorContainsItself", err)
|
||||
}
|
||||
|
||||
want := `anchor contains itself: "test" on line 7`
|
||||
if err.Error() != want {
|
||||
t.Errorf("Parse() error = %q, want %q", err.Error(), want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseAcceptsKnownKeys checks that a config with a ${...} reference
|
||||
// and a YAML merge key (<<) still parses.
|
||||
func TestParseAcceptsKnownKeys(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user