Skip to content
This repository has been archived by the owner on Apr 27, 2022. It is now read-only.

Add Environment Variable Source to Staert #42

Closed
wants to merge 3 commits into from

Conversation

serbrech
Copy link
Contributor

@serbrech serbrech commented Mar 2, 2018

Description

This is a first shot at providing an environment variable source to staert (and traefik). I revived #30, extended it, and finished it. I'll add documentation (it's pretty much similar to the initial PR one, so see :
I tested it briefly with traefik, I'll send a PR there too as I get closer to a releasable state.)
I have integrated it in traefik to test, and it manages to parse the entire config tree from traefik. that covers many cases. I'm not certain it manages to SET all values though. so more tests will be needed there.

Quite some work left to do though before calling it done :

  • Make sure the priority order is ok when running envsource + toml + cli
  • Handle defaultPointerConfig
  • Handle some edge cases
  • Log correctly (info/debug..)
  • Document it. (see 9b6531c)
  • Cleanup
  • Refactor (not pretty code to my standards, but it's a good start)
  • Make the env variables discoverable (list all env variable name for a given config struct, maybe separate PR)
  • Bonus: Rewrite it, it's not efficient, there are better ways to do it 😄

DefaultPointerConfig

When it comes to defaultPointerConfig, some notes that made me understand it 🥇

  • it's used for optional defaults.
  • if you dont set any of the values under that pointer, then you take the value from the defaultConfig
  • if you set just --pointerName, it touches the value, and so, you take the default pointer value
  • if you set any of the parameter under that pointer, you use the defaultPointerConfig value as default

Related to traefik/traefik#7

@coveralls
Copy link

coveralls commented Mar 2, 2018

Coverage Status

Coverage increased (+2.3%) to 82.504% when pulling 6d1fcfb on serbrech:envsource-PR into e4d686c on containous:master.

Copy link
Contributor

@ldez ldez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a first quick review.

I did not repeat a message for the repetitive problems.
I do not take a look on algorithmic.

"testing"

"github.com/containous/flaeg/parse"

Copy link
Contributor

@ldez ldez Mar 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please group imports.

import (
	"reflect"
	"testing"

	"github.com/containous/flaeg/parse"
	"github.com/stretchr/testify/assert"
)

OtherStringValue string
}{},
[]*envValue{
&envValue{"FOO", path{"StringValue"}},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&envValue declaration is not needed, same everywhere

envsource.go Outdated

values, err := e.analyzeStruct(configVal.Type(), []string{})

if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

	values, err := e.analyzeStruct(configVal.Type(), []string{})
	if err != nil {
		return err
	}

envsource.go Outdated
// Recursively scan the given config structure type information
// and look for defined environment variables.
func (e *envSource) analyzeStruct(configType reflect.Type, currentPath path) ([]*envValue, error) {
res := []*envValue{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var res []*envValue

envsource.go Outdated

if err != nil {
return []*envValue{}, err
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

			values, err := e.analyzeValue(field.Type, currentPath)
			if err != nil {
				return nil, err
			}

t.Fail()
}

assert.Exactly(t, testCase.Expectation, testCase.Value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mix stblib test and testify.

Either you use stblib everywhere or testify, but not the both.

}
}

func TestFilterEnvVarWithPrefix(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could move the tests on the top of the file (after type declaration)

Expectation interface{}
}{
{
"BasicStruct",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you explicitly name all fields.

Config []basicAppConfig
}{},
[]*envValue{
&envValue{"Test", path{"Config", "0", "StringValue"}},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&envValue declaration is not needed, same everywhere

expectedPtrPtr := getPtrPtrConfig()

testCases := []struct {
Label string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add the test cases description for each test.
use a field desc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what the Label field should do. "BasicStructPointerPointer", "WithWrongPath". What format do you use for test names/descriptions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use desc to avoid ambiguities of names like name, label, ...

The description must be a sentence or partial sentence.
ex: with a wrong path

I love the should ... when ... format.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the label to provide better context and be more readable. if a test fails it looks like this :
TestAssignValues/should_assign_values_to_config_with_a_map_that_uses_a_parser_for_values (0.00s)
changed field name to desc

@serbrech
Copy link
Contributor Author

serbrech commented Mar 7, 2018

Commits 9840c06 and onwards are fixes following the review

@ldez
Copy link
Contributor

ldez commented Mar 11, 2018

There is a problem, it's complex to explain.

The problem is related to the DefaultPointersConfig management.

To explain I will give you some example from the TomlSource.

Case 1

DurationField = 28

[PtrStruct1]
S1Int = 28
config := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    1,
		S1String: "S1StringInitConfig",
	},
	DurationField: parse.Duration(time.Second),
}
defaultPointersConfig := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    11,
		S1String: "S1StringDefaultPointersConfig",
		S1Bool:   true,
		S1PtrStruct3: &Struct3{
			S3Float64: 11.11,
		},
	},
}

Result:

{
  "PtrStruct1": {
      "S1Int": 28,
      "S1String": "S1StringDefaultPointersConfig",
      "S1Bool": true,
      "S1PtrStruct3": null
    },
    "DurationField":"28s"
}

Case 2

DurationField = 28

[PtrStruct1]
S1Int = 28
S1String =  "S1StringToml"
config := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    1,
		S1String: "S1StringInitConfig",
	},
	DurationField: parse.Duration(time.Second),
}
defaultPointersConfig := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    11,
		S1String: "S1StringDefaultPointersConfig",
		S1Bool:   true,
		S1PtrStruct3: &Struct3{
			S3Float64: 11.11,
		},
	},
}

Result:

{
  "PtrStruct1": {
      "S1Int": 28,
      "S1String": "S1StringToml",
      "S1Bool": true,
      "S1PtrStruct3": null
    },
    "DurationField":"28s"
}

Case 3

DurationField = 28

[PtrStruct1]
S1Int = 28
S1Bool = false
config := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    1,
		S1String: "S1StringInitConfig",
	},
	DurationField: parse.Duration(time.Second),
}
defaultPointersConfig := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    11,
		S1String: "S1StringDefaultPointersConfig",
		S1Bool:   true,
		S1PtrStruct3: &Struct3{
			S3Float64: 11.11,
		},
	},
}

Result:

{
  "PtrStruct1": {
      "S1Int": 28,
      "S1String": "S1StringDefaultPointersConfig",
      "S1Bool": false,
      "S1PtrStruct3": null
    },
    "DurationField":"28s"
}

Case 4

DurationField = 28

[PtrStruct1]
S1Int = 28
S1Bool = false
config := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    1,
		S1String: "S1StringInitConfig",
	},
	DurationField: parse.Duration(time.Second),
}
defaultPointersConfig := &StructPtr{
	PtrStruct1: &Struct1{
		S1Int:    11,
		S1String: "S1StringDefaultPointersConfig",
		S1Bool:   true,
		S1PtrStruct3: &Struct3{
			S3Float64: 11.11,
		},
	},
}

Result:

{
  "PtrStruct1": {
      "S1Int": 28,
      "S1String": "S1StringDefaultPointersConfig",
      "S1Bool": false,
      "S1PtrStruct3": null
    },
    "DurationField":"28s"
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants