From a163694dc653204681e067da4e66bfd1f1b59080 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 17:38:52 +0200 Subject: [PATCH 1/2] Config parsing: fail on unknown fields and print them Useful against config validation or runtime failures caused by wrong field spelling or YAML indentation. --- cmd/icingadb-migrate/main.go | 2 +- pkg/config/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/icingadb-migrate/main.go b/cmd/icingadb-migrate/main.go index 9618ec2f8..eefa14242 100644 --- a/cmd/icingadb-migrate/main.go +++ b/cmd/icingadb-migrate/main.go @@ -125,7 +125,7 @@ func parseConfig(f *Flags) (_ *Config, exit int) { return nil, 2 } - if err := yaml.NewDecoder(cf).Decode(c); err != nil { + if err := yaml.NewDecoder(cf, yaml.DisallowUnknownField()).Decode(c); err != nil { _, _ = fmt.Fprintf(os.Stderr, "can't parse config file: %s\n", err.Error()) return nil, 2 } diff --git a/pkg/config/config.go b/pkg/config/config.go index 1388ba930..db6662c3e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -54,7 +54,7 @@ func FromYAMLFile(name string) (*Config, error) { defer f.Close() c := &Config{} - d := yaml.NewDecoder(f) + d := yaml.NewDecoder(f, yaml.DisallowUnknownField()) if err := defaults.Set(c); err != nil { return nil, errors.Wrap(err, "can't set config defaults") From af868b1762911be5b43a70b2dd31c0dce501a4d9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Jul 2023 09:57:54 +0200 Subject: [PATCH 2/2] Config parsing: unit test failure due to unknown fields --- pkg/config/config_test.go | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 pkg/config/config_test.go diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go new file mode 100644 index 000000000..94e377355 --- /dev/null +++ b/pkg/config/config_test.go @@ -0,0 +1,69 @@ +package config + +import ( + "github.com/creasty/defaults" + "github.com/icinga/icingadb/pkg/logging" + "github.com/stretchr/testify/require" + "os" + "testing" +) + +func TestFromYAMLFile(t *testing.T) { + const miniConf = ` +database: + host: 192.0.2.1 + database: icingadb + user: icingadb + password: icingadb + +redis: + host: 2001:db8::1 +` + + subtests := []struct { + name string + input string + output *Config + }{ + { + name: "mini", + input: miniConf, + output: func() *Config { + c := &Config{} + _ = defaults.Set(c) + + c.Database.Host = "192.0.2.1" + c.Database.Database = "icingadb" + c.Database.User = "icingadb" + c.Database.Password = "icingadb" + + c.Redis.Host = "2001:db8::1" + c.Logging.Output = logging.CONSOLE + + return c + }(), + }, + { + name: "mini-with-unknown", + input: miniConf + "\nunknown: 42", + output: nil, + }, + } + + for _, st := range subtests { + t.Run(st.name, func(t *testing.T) { + tempFile, err := os.CreateTemp("", "") + require.NoError(t, err) + defer func() { _ = os.Remove(tempFile.Name()) }() + + require.NoError(t, os.WriteFile(tempFile.Name(), []byte(st.input), 0o600)) + + if actual, err := FromYAMLFile(tempFile.Name()); st.output == nil { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, st.output, actual) + } + }) + } +}