Skip to content

Commented out property in YAML yields inconsistencies between Get and Unmarshal when overriding using environment variables #725

@yinonavraham

Description

@yinonavraham

Unmarshal does not resolve the correct value when using environment variable and a yaml config file with commented out property.
To reproduce, see the following unit test:

func Test(t *testing.T) {
	type FooConfig struct {
		Bar string
	}
	type Config struct {
		Foo FooConfig
	}

	yamlWithValueCommented := `---
foo:
#  bar: "value from file"`

	// Overriding with OS Environment Variable
	os.Setenv("FOO_BAR", "value from env")

	v := viper.New()
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	v.AutomaticEnv()
	v.SetConfigType("yaml")
	v.SetDefault("foo.bar", "default value")
	v.ReadConfig(strings.NewReader(yamlWithValueCommented))
	c := Config{}
	v.Unmarshal(&c)
	fmt.Println(v.Get("foo.bar")) // As expected: "value from env"
	fmt.Println(c.Foo.Bar) // Expected: "value from env", Actual: "default value"
}

Output:

=== RUN   Test
value from env
default value
--- PASS: Test (0.00s)

The full test, which includes some sanity tests:

func Test(t *testing.T) {
	type FooConfig struct {
		Bar string
	}
	type Config struct {
		Foo FooConfig
	}

	yamlWithValue := `---
foo:
  bar: "value from file"`

	yamlWithValueCommented := `---
foo:
#  bar: "value from file"`

	v := viper.New()
	v.SetConfigType("yaml")
	v.SetDefault("foo.bar", "default value")
	v.ReadConfig(strings.NewReader(yamlWithValue))
	c := Config{}
	v.Unmarshal(&c)
	fmt.Println(v.Get("foo.bar")) // As expected: "value from file"
	fmt.Println(c.Foo.Bar) // As expected: "value from file"

	v = viper.New()
	v.SetConfigType("yaml")
	v.SetDefault("foo.bar", "default value")
	v.ReadConfig(strings.NewReader(yamlWithValueCommented))
	c = Config{}
	v.Unmarshal(&c)
	fmt.Println(v.Get("foo.bar")) // As expected: "default value"
	fmt.Println(c.Foo.Bar) // As expected: "default value"

	// Now overriding with OS Environment Variable
	os.Setenv("FOO_BAR", "value from env")

	v = viper.New()
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	v.AutomaticEnv()
	v.SetConfigType("yaml")
	v.SetDefault("foo.bar", "default value")
	v.ReadConfig(strings.NewReader(yamlWithValue))
	c = Config{}
	v.Unmarshal(&c)
	fmt.Println(v.Get("foo.bar")) // As expected: "value from env"
	fmt.Println(c.Foo.Bar) // As expected: "value from env"

	v = viper.New()
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	v.AutomaticEnv()
	v.SetConfigType("yaml")
	v.SetDefault("foo.bar", "default value")
	v.ReadConfig(strings.NewReader(yamlWithValueCommented))
	c = Config{}
	v.Unmarshal(&c)
	fmt.Println(v.Get("foo.bar")) // As expected: "value from env"
	fmt.Println(c.Foo.Bar) // Expected: "value from env", Actual: "default value"
}

Output:

=== RUN   Test
value from file
value from file
default value
default value
value from env
value from env
value from env
default value
--- PASS: Test (0.00s)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions