diff --git a/context/context.go b/context/context.go index c812af8..66a4539 100644 --- a/context/context.go +++ b/context/context.go @@ -9,6 +9,7 @@ import ( type ResourceSet struct { Name string `json:"name"` + Path string `json:"path"` Values map[string]interface{} `json:"values"` // Fields for resource set collections @@ -41,7 +42,7 @@ func LoadContextFromFile(filename string) (*Context, error) { ) } - c.ResourceSets = flattenResourceSetCollections(&c.ResourceSets) + c.ResourceSets = flattenPrepareResourceSetPaths(&c.ResourceSets) c.BaseDir = path.Dir(filename) c.ResourceSets = loadAllDefaultValues(&c) @@ -73,19 +74,31 @@ func (ctx *Context) loadImportedVariables() error { return nil } -// Flattens resource set collections, i.e. resource sets that themselves have an additional 'include' field set. +// Correctly prepares the file paths for resource sets by inferring implicit paths and flattening resource set +// collections, i.e. resource sets that themselves have an additional 'include' field set. // Those will be regarded as a short-hand for including multiple resource sets from a subfolder. // See https://github.com/tazjin/kontemplate/issues/9 for more information. -func flattenResourceSetCollections(rs *[]ResourceSet) []ResourceSet { +func flattenPrepareResourceSetPaths(rs *[]ResourceSet) []ResourceSet { flattened := make([]ResourceSet, 0) for _, r := range *rs { + // If a path is not explicitly specified it should default to the resource set name. + // This is also the classic behaviour prior to kontemplate 1.2 + if r.Path == "" { + r.Path = r.Name + } + if len(r.Include) == 0 { flattened = append(flattened, r) } else { for _, subResourceSet := range r.Include { + if subResourceSet.Path == "" { + subResourceSet.Path = subResourceSet.Name + } + subResourceSet.Parent = r.Name subResourceSet.Name = path.Join(r.Name, subResourceSet.Name) + subResourceSet.Path = path.Join(r.Path, subResourceSet.Path) subResourceSet.Values = *util.Merge(&r.Values, &subResourceSet.Values) flattened = append(flattened, subResourceSet) } @@ -114,13 +127,13 @@ func loadDefaultValues(rs *ResourceSet, c *Context) *map[string]interface{} { var defaultVars map[string]interface{} // Attempt to load YAML values - err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Name, "default.yaml"), &defaultVars) + err := util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, "default.yaml"), &defaultVars) if err == nil { return util.Merge(&defaultVars, &rs.Values) } // Attempt to load JSON values - err = util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Name, "default.json"), &defaultVars) + err = util.LoadJsonOrYaml(path.Join(c.BaseDir, rs.Path, "default.json"), &defaultVars) if err == nil { return util.Merge(&defaultVars, &rs.Values) } diff --git a/context/context_test.go b/context/context_test.go index b6acf41..350b2b6 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -21,6 +21,7 @@ func TestLoadFlatContextFromFile(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", + Path: "some-api", Values: map[string]interface{}{ "apiPort": float64(4567), // yep! "importantFeature": true, @@ -55,6 +56,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "some-api", + Path: "some-api", Values: map[string]interface{}{ "apiPort": float64(4567), // yep! "importantFeature": true, @@ -65,6 +67,7 @@ func TestLoadContextWithResourceSetCollections(t *testing.T) { }, { Name: "collection/nested", + Path: "collection/nested", Values: map[string]interface{}{ "lizards": "good", }, @@ -95,6 +98,7 @@ func TestSubresourceVariableInheritance(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", + Path: "parent/child", Values: map[string]interface{}{ "foo": "bar", "bar": "baz", @@ -125,6 +129,7 @@ func TestSubresourceVariableInheritanceOverride(t *testing.T) { ResourceSets: []ResourceSet{ { Name: "parent/child", + Path: "parent/child", Values: map[string]interface{}{ "foo": "newvalue", }, @@ -203,3 +208,66 @@ func TestImportValuesOverride(t *testing.T) { t.Fail() } } + +func TestExplicitPathLoading(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/explicit-path.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "some-api-europe", + Path: "some-api", + Values: map[string]interface{}{ + "location": "europe", + }, + Include: nil, + Parent: "", + }, + { + Name: "some-api-asia", + Path: "some-api", + Values: map[string]interface{}{ + "location": "asia", + }, + Include: nil, + Parent: "", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } +} + +func TestExplicitSubresourcePathLoading(t *testing.T) { + ctx, err := LoadContextFromFile("testdata/explicit-subresource-path.yaml") + if err != nil { + t.Error(err) + t.Fail() + } + + expected := Context{ + Name: "k8s.prod.mydomain.com", + ResourceSets: []ResourceSet{ + { + Name: "parent/child", + Path: "parent-path/child-path", + Parent: "parent", + }, + }, + BaseDir: "testdata", + } + + if !reflect.DeepEqual(*ctx, expected) { + t.Error("Loaded context and expected context did not match") + t.Fail() + } +} diff --git a/context/testdata/explicit-path.yaml b/context/testdata/explicit-path.yaml new file mode 100644 index 0000000..2c81f83 --- /dev/null +++ b/context/testdata/explicit-path.yaml @@ -0,0 +1,11 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: some-api-europe + path: some-api + values: + location: europe + - name: some-api-asia + path: some-api + values: + location: asia diff --git a/context/testdata/explicit-subresource-path.yaml b/context/testdata/explicit-subresource-path.yaml new file mode 100644 index 0000000..6cf8618 --- /dev/null +++ b/context/testdata/explicit-subresource-path.yaml @@ -0,0 +1,8 @@ +--- +context: k8s.prod.mydomain.com +include: + - name: parent + path: parent-path + include: + - name: child + path: child-path