Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba343c30e2 |
No files matched your search
@@ -311,12 +311,15 @@ existing file. Created with mode `0600` since it will contain credentials.
|
||||
in the file are preserved. Missing keys along the path are created: as a list
|
||||
when the next path element is a number (so
|
||||
`vaultik config set age_recipients.0 age1...` works on a file without
|
||||
`age_recipients`), otherwise as a map. If the config loads, a change after which
|
||||
it would not, such as `vaultik config set compression_level 99`, is refused and
|
||||
the file is left unchanged. A config that does not load is checked one setting
|
||||
at a time. Examples are the file `config init` writes before `storage_url` is
|
||||
set, and one with a `${ENV:...}` reference to a variable not set in this shell.
|
||||
In such a config, an unknown key or a value of the wrong type is refused.
|
||||
`age_recipients`), otherwise as a map. A key that no setting has, such as a
|
||||
misspelled one, is refused. If the config loads, a change after which it would
|
||||
not, such as `vaultik config set compression_level 99`, is refused and the file
|
||||
is left unchanged. A config that does not load, such as the one `config init`
|
||||
writes before `storage_url` is set or one with a `${ENV:...}` reference to a
|
||||
variable not set in this shell, is checked one setting at a time: a change after
|
||||
which the changed setting cannot be read, such as one giving it the wrong type
|
||||
(a single value where a list belongs or a word where a number belongs), is
|
||||
refused.
|
||||
|
||||
**`snapshot create`**: Perform incremental backup of configured snapshots.
|
||||
|
||||
|
||||
@@ -440,14 +440,15 @@ func newConfigSetCommand() *cobra.Command {
|
||||
Long: `Sets a scalar config value addressed by dotted YAML path and writes
|
||||
the file back, preserving comments and formatting. Missing keys along
|
||||
the path are created: as a list when the next path element is a
|
||||
number, otherwise as a map.
|
||||
number, otherwise as a map. A key that no setting has is refused.
|
||||
|
||||
If the config loads, a change after which it would not, such as
|
||||
compression_level 99, is refused and the file is left unchanged. A
|
||||
config that does not load is checked one setting at a time. Examples
|
||||
are the file config init writes before storage_url is set, and one
|
||||
with a ${ENV:...} reference to a variable not set in this shell. In
|
||||
such a config, an unknown key or a value of the wrong type is refused.
|
||||
config that does not load, such as the one config init writes before
|
||||
storage_url is set or one with a ${ENV:...} reference to a variable
|
||||
not set here, is checked one setting at a time: a change after which
|
||||
the changed setting cannot be read, such as one giving it the wrong
|
||||
type, is refused.
|
||||
|
||||
Examples:
|
||||
vaultik config set storage_url "file:///mnt/backups"
|
||||
|
||||
@@ -225,7 +225,7 @@ func TestConfigSetIndexUnderMissingKey(t *testing.T) {
|
||||
// change after which the config would not load, with an error giving the
|
||||
// reason, and leaves the file as it was. On the file `config init` writes,
|
||||
// which does not load until storage_url is set, and on one that cannot be
|
||||
// read here, only an unknown key or a value of the wrong type is refused.
|
||||
// read here, only a value of the wrong type is refused.
|
||||
func TestConfigSetKeepsFileThatWouldNotLoad(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -311,12 +311,6 @@ func Parse(data []byte) (*Config, error) {
|
||||
return nil, fmt.Errorf("failed to load config file: %w", err)
|
||||
}
|
||||
|
||||
// A ${...} reference can bring in keys that are not in the file.
|
||||
err = checkFilledKeys(sc.Data())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
// Set defaults
|
||||
BlobSizeLimit: defaultBlobSizeLimit,
|
||||
|
||||
+4
-31
@@ -14,11 +14,6 @@ import (
|
||||
// of the map it names to the map it sits in.
|
||||
const yamlMergeTag = "!!merge"
|
||||
|
||||
// envSection is the top-level key of a map of environment variables that
|
||||
// smartconfig sets while it loads the config. It is not a vaultik setting,
|
||||
// so its keys are not checked.
|
||||
const envSection = "env"
|
||||
|
||||
var errUnknownKey = errors.New("unknown config key")
|
||||
|
||||
// SettingType returns the type of the setting that the dotted key names in
|
||||
@@ -60,9 +55,9 @@ 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.
|
||||
// file or part of one at the dotted path, that no setting in typ has. The
|
||||
// error gives the key's line in the file. A value of the wrong 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:
|
||||
@@ -94,21 +89,7 @@ func checkKeys(node *yaml.Node, typ reflect.Type, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkFilledKeys is checkKeys for data, the config after smartconfig has
|
||||
// filled in its ${...} references. A key that a reference brings in is not
|
||||
// in the file, and the copy that Encode makes has no line numbers.
|
||||
func checkFilledKeys(data map[string]any) error {
|
||||
var filled yaml.Node
|
||||
|
||||
err := filled.Encode(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config data: %w", err)
|
||||
}
|
||||
|
||||
return checkKeys(&filled, reflect.TypeFor[Config](), "")
|
||||
}
|
||||
|
||||
// checkMappingKeys is checkKeys for a map.
|
||||
// checkMappingKeys is checkKeys for a map in the file.
|
||||
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]
|
||||
@@ -131,9 +112,6 @@ func checkMappingKeys(node *yaml.Node, typ reflect.Type, path string) error {
|
||||
}
|
||||
|
||||
keyPath := joinKey(path, key.Value)
|
||||
if keyPath == envSection {
|
||||
continue
|
||||
}
|
||||
|
||||
var valueType reflect.Type
|
||||
|
||||
@@ -142,11 +120,6 @@ func checkMappingKeys(node *yaml.Node, typ reflect.Type, path string) error {
|
||||
valueType = typ.Elem()
|
||||
case typ.Kind() == reflect.Struct:
|
||||
field, ok := yamlField(typ, key.Value)
|
||||
if !ok && key.Line == 0 {
|
||||
// A key that a ${...} reference brought in has no line.
|
||||
return fmt.Errorf("%w %q", errUnknownKey, keyPath)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("%w %q on line %d",
|
||||
errUnknownKey, keyPath, key.Line)
|
||||
|
||||
@@ -17,21 +17,10 @@ const keysTestConfig = "storage_url: file:///tmp/vaultik-test-storage\n" +
|
||||
" paths: [/tmp/vaultik-test-source]\n"
|
||||
|
||||
// TestParseRefusesUnknownKey checks that a key no setting has, at any
|
||||
// depth, fails the parse with an error naming the key and its line. A key
|
||||
// that a ${...} reference brings in is not in the file, so its error gives
|
||||
// no line.
|
||||
// depth, fails the parse with an error naming the key and its line.
|
||||
func TestParseRefusesUnknownKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
snapshotPath := filepath.Join(t.TempDir(), "snapshot.yml")
|
||||
|
||||
err := os.WriteFile(snapshotPath, []byte("other:\n"+
|
||||
" paths: [/tmp/vaultik-test-source]\n"+
|
||||
" exlude: [/tmp/cache]\n"), 0o600)
|
||||
if err != nil {
|
||||
t.Fatalf("write snapshot: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
yaml string
|
||||
@@ -44,9 +33,6 @@ func TestParseRefusesUnknownKey(t *testing.T) {
|
||||
{"in s3", strings.Replace(keysTestConfig, " region:",
|
||||
" bukcet: b\n region:", 1),
|
||||
`unknown config key "s3.bukcet" on line 3`},
|
||||
{"from a reference",
|
||||
keysTestConfig + " other: ${YAML:" + snapshotPath + ":other}\n",
|
||||
`unknown config key "snapshots.other.exlude"`},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -98,18 +84,3 @@ func TestParseAcceptsKnownKeys(t *testing.T) {
|
||||
other)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseAcceptsEnvSection checks that a config with a top-level env
|
||||
// section still parses. smartconfig sets the environment variables it
|
||||
// lists, so its keys are not settings.
|
||||
func TestParseAcceptsEnvSection(t *testing.T) {
|
||||
const name = "VAULTIK_TEST_ENV_SECTION"
|
||||
|
||||
// Parse sets the variable; t.Setenv restores it after the test.
|
||||
t.Setenv(name, "")
|
||||
|
||||
_, err := Parse([]byte(keysTestConfig + "env:\n " + name + ": set\n"))
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user