add comments in refvar transformer

Refactor change
This commit is contained in:
Jingfang Liu
2018-06-06 13:16:10 -07:00
parent 1a28f3a391
commit 95f568b857
8 changed files with 126 additions and 142 deletions

View File

@@ -79,7 +79,7 @@ func (a *applicationImpl) Resources() (resmap.ResMap, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
t, err := a.getHashAndReferenceTransformer(res) t, err := a.newHashAndReferenceTransformer(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -125,7 +125,7 @@ func (a *applicationImpl) SemiResources() (resmap.ResMap, error) {
return nil, errs return nil, errs
} }
t, err := a.getTransformer(patches) t, err := a.newTransformer(patches)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -144,7 +144,7 @@ func (a *applicationImpl) RawResources() (resmap.ResMap, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
t, err := a.getHashAndReferenceTransformer(res) t, err := a.newHashAndReferenceTransformer(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -220,12 +220,12 @@ func (a *applicationImpl) subApp() ([]Application, error) {
return apps, nil return apps, nil
} }
// getTransformer generates the following transformers: // newTransformer generates the following transformers:
// 1) apply overlay // 1) apply overlay
// 2) name prefix // 2) name prefix
// 3) apply labels // 3) apply labels
// 4) apply annotations // 4) apply annotations
func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transformers.Transformer, error) { func (a *applicationImpl) newTransformer(patches []*resource.Resource) (transformers.Transformer, error) {
ts := []transformers.Transformer{} ts := []transformers.Transformer{}
ot, err := transformers.NewOverlayTransformer(patches) ot, err := transformers.NewOverlayTransformer(patches)
@@ -257,11 +257,11 @@ func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transfor
return transformers.NewMultiTransformer(ts), nil return transformers.NewMultiTransformer(ts), nil
} }
// getHashAndReferenceTransformer generates the following transformers: // newHashAndReferenceTransformer generates the following transformers:
// 1) name hash for configmap and secrests // 1) name hash for configmap and secrests
// 2) apply name reference // 2) apply name reference
// 3) apply reference variables // 3) apply reference variables
func (a *applicationImpl) getHashAndReferenceTransformer(allRes resmap.ResMap) (transformers.Transformer, error) { func (a *applicationImpl) newHashAndReferenceTransformer(allRes resmap.ResMap) (transformers.Transformer, error) {
ts := []transformers.Transformer{} ts := []transformers.Transformer{}
nht := transformers.NewNameHashTransformer() nht := transformers.NewNameHashTransformer()
ts = append(ts, nht) ts = append(ts, nht)
@@ -271,7 +271,7 @@ func (a *applicationImpl) getHashAndReferenceTransformer(allRes resmap.ResMap) (
return nil, err return nil, err
} }
ts = append(ts, nrt) ts = append(ts, nrt)
t, err := a.getVariableReferenceTransformer(allRes) t, err := a.newVariableReferenceTransformer(allRes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -280,7 +280,7 @@ func (a *applicationImpl) getHashAndReferenceTransformer(allRes resmap.ResMap) (
return transformers.NewMultiTransformer(ts), nil return transformers.NewMultiTransformer(ts), nil
} }
func (a *applicationImpl) getVariableReferenceTransformer(allRes resmap.ResMap) (transformers.Transformer, error) { func (a *applicationImpl) newVariableReferenceTransformer(allRes resmap.ResMap) (transformers.Transformer, error) {
refvars, err := a.resolveRefVars(allRes) refvars, err := a.resolveRefVars(allRes)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -314,9 +314,9 @@ func (a *applicationImpl) resolveRefVars(resources resmap.ResMap) (map[string]st
} }
for _, refvar := range vars { for _, refvar := range vars {
refGVKN := gvkn(refvar) refGVKN := resource.NewResId(refvar.ObjRef.GroupVersionKind(), refvar.ObjRef.Name)
if r, found := resources[refGVKN]; found { if r, found := resources[refGVKN]; found {
s, err := getFieldAsString(r.Unstruct().UnstructuredContent(), strings.Split(refvar.FieldRef.FieldPath, ".")) s, err := resource.GetFieldValue(r.Unstruct().UnstructuredContent(), strings.Split(refvar.FieldRef.FieldPath, "."))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to resolve referred var: %+v", refvar) return nil, fmt.Errorf("failed to resolve referred var: %+v", refvar)
} }

View File

@@ -1,54 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package app
import (
"fmt"
"github.com/kubernetes-sigs/kustomize/pkg/resource"
"github.com/kubernetes-sigs/kustomize/pkg/types"
)
func gvkn(rv types.Var) resource.ResId {
return resource.NewResId(rv.ObjRef.GroupVersionKind(), rv.ObjRef.Name)
}
func getFieldAsString(m map[string]interface{}, pathToField []string) (string, error) {
if len(pathToField) == 0 {
return "", fmt.Errorf("Field not found")
}
if len(pathToField) == 1 {
if v, found := m[pathToField[0]]; found {
if s, ok := v.(string); ok {
return s, nil
}
return "", fmt.Errorf("value at fieldpath is not of string type")
}
return "", fmt.Errorf("field at given fieldpath does not exist")
}
curr, rest := pathToField[0], pathToField[1]
v := m[curr]
switch typedV := v.(type) {
case map[string]interface{}:
return getFieldAsString(typedV, []string{rest})
default:
return "", fmt.Errorf("%#v is not expected to be a primitive type", typedV)
}
}

View File

@@ -18,6 +18,7 @@ package resource
import ( import (
"encoding/json" "encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@@ -108,3 +109,29 @@ func newUnstructuredFromObject(in runtime.Object) (*unstructured.Unstructured, e
err = out.UnmarshalJSON(marshaled) err = out.UnmarshalJSON(marshaled)
return &out, err return &out, err
} }
func GetFieldValue(m map[string]interface{}, pathToField []string) (string, error) {
if len(pathToField) == 0 {
return "", fmt.Errorf("Field not found")
}
if len(pathToField) == 1 {
if v, found := m[pathToField[0]]; found {
if s, ok := v.(string); ok {
return s, nil
}
return "", fmt.Errorf("value at fieldpath is not of string type")
}
return "", fmt.Errorf("field at given fieldpath does not exist")
}
curr, rest := pathToField[0], pathToField[1]
v := m[curr]
switch typedV := v.(type) {
case map[string]interface{}:
return GetFieldValue(typedV, []string{rest})
default:
return "", fmt.Errorf("%#v is not expected to be a primitive type", typedV)
}
}

View File

@@ -14,10 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package app package resource
import ( import (
"strings"
"testing" "testing"
) )
@@ -31,21 +30,37 @@ func TestGetFieldAsString(t *testing.T) {
"name": "service-name", "name": "service-name",
}, },
} }
p := []string{"Kind"}
s, _ := getFieldAsString(m, p) tests := []struct {
if s != "Service" { pathToField []string
t.Errorf("Expected to get Service, but actually got %s", s) expectedName string
expectedError bool
}{
{
pathToField: []string{"Kind"},
expectedName: "Service",
expectedError: false,
},
{
pathToField: []string{"metadata", "name"},
expectedName: "service-name",
expectedError: false,
},
{
pathToField: []string{"metadata", "non-existing-field"},
expectedName: "",
expectedError: true,
},
} }
p = []string{"metadata", "name"} for _, test := range tests {
s, _ = getFieldAsString(m, p) s, err := GetFieldValue(m, test.pathToField)
if s != "service-name" { if test.expectedError && err == nil {
t.Errorf("Expected to get service-name, but actually got %s", s) t.Fatalf("should return error, but no error returned")
} } else {
if test.expectedName != s {
p = []string{"metadata", "non-existing-field"} t.Fatalf("Got:%s expected:%s", s, test.expectedName)
s, err := getFieldAsString(m, p) }
if !strings.HasSuffix(err.Error(), "field at given fieldpath does not exist") { }
t.Errorf("Unexpected failure due to incorrect error message %s", err.Error())
} }
} }

View File

@@ -13,6 +13,7 @@ type refvarTransformer struct {
vars map[string]string vars map[string]string
} }
// NewRefVarTransformer returns a Trasformer that replaces $(VAR) style variables with values.
func NewRefVarTransformer(vars map[string]string) (Transformer, error) { func NewRefVarTransformer(vars map[string]string) (Transformer, error) {
return &refvarTransformer{ return &refvarTransformer{
vars: vars, vars: vars,
@@ -33,17 +34,16 @@ func NewRefVarTransformer(vars map[string]string) (Transformer, error) {
}, nil }, nil
} }
// Transform determines the final values of variables:
//
// 1. Determine the final value of each variable:
// a. If the variable's Value is set, expand the `$(var)` references to other
// variables in the .Value field; the sources of variables are the declared
// variables of the container and the service environment variables
// b. If a source is defined for an environment variable, resolve the source
// 2. Create the container's environment in the order variables are declared
// 3. Add remaining service environment vars
func (rv *refvarTransformer) Transform(resources resmap.ResMap) error { func (rv *refvarTransformer) Transform(resources resmap.ResMap) error {
// Determine the final values of variables:
//
// 1. Determine the final value of each variable:
// a. If the variable's Value is set, expand the `$(var)` references to other
// variables in the .Value field; the sources of variables are the declared
// variables of the container and the service environment variables
// b. If a source is defined for an environment variable, resolve the source
// 2. Create the container's environment in the order variables are declared
// 3. Add remaining service environment vars
for GVKn := range resources { for GVKn := range resources {
obj := resources[GVKn].Unstruct() obj := resources[GVKn].Unstruct()
objMap := obj.UnstructuredContent() objMap := obj.UnstructuredContent()

View File

@@ -1,27 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package types
import (
corev1 "k8s.io/api/core/v1"
)
func (v *Var) Defaulting() {
if (corev1.ObjectFieldSelector{}) == v.FieldRef {
v.FieldRef = corev1.ObjectFieldSelector{FieldPath: "metadata.name"}
}
}

View File

@@ -16,10 +16,6 @@ limitations under the License.
package types package types
import (
corev1 "k8s.io/api/core/v1"
)
// Kustomization holds the information needed to generate customized k8s api resources. // Kustomization holds the information needed to generate customized k8s api resources.
type Kustomization struct { type Kustomization struct {
// NamePrefix will prefix the names of all resources mentioned in the kustomization // NamePrefix will prefix the names of all resources mentioned in the kustomization
@@ -136,23 +132,3 @@ type DataSources struct {
// i.e. a Docker .env file or a .ini file. // i.e. a Docker .env file or a .ini file.
EnvSource string `json:"env,omitempty" yaml:"env,omitempty"` EnvSource string `json:"env,omitempty" yaml:"env,omitempty"`
} }
// Var represents a variable whose value will be sourced
// from a field in a Kubernetes object.
type Var struct {
// Value of identifier name e.g. FOO used in container args, annotations
// Appears in pod template as $(FOO)
Name string `json:"name" yaml:"name"`
// ObjRef must refer to a Kubernetes resource under the
// purview of this kustomization. ObjRef should use the
// raw name of the object (the name specified in its YAML,
// before addition of a namePrefix).
ObjRef corev1.ObjectReference `json:"objref" yaml:"objref"`
// FieldRef refers to the field of the object referred to by
// ObjRef whose value will be extracted for use in
// replacing $(FOO).
// If unspecified, this defaults to fieldpath: metadata.name
FieldRef corev1.ObjectFieldSelector `json:"fieldref,omitempty" yaml:"objref,omitempty"`
}

47
pkg/types/var.go Normal file
View File

@@ -0,0 +1,47 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package types
import (
corev1 "k8s.io/api/core/v1"
)
// Var represents a variable whose value will be sourced
// from a field in a Kubernetes object.
type Var struct {
// Value of identifier name e.g. FOO used in container args, annotations
// Appears in pod template as $(FOO)
Name string `json:"name" yaml:"name"`
// ObjRef must refer to a Kubernetes resource under the
// purview of this kustomization. ObjRef should use the
// raw name of the object (the name specified in its YAML,
// before addition of a namePrefix).
ObjRef corev1.ObjectReference `json:"objref" yaml:"objref"`
// FieldRef refers to the field of the object referred to by
// ObjRef whose value will be extracted for use in
// replacing $(FOO).
// If unspecified, this defaults to fieldpath: metadata.name
FieldRef corev1.ObjectFieldSelector `json:"fieldref,omitempty" yaml:"objref,omitempty"`
}
func (v *Var) Defaulting() {
if (corev1.ObjectFieldSelector{}) == v.FieldRef {
v.FieldRef = corev1.ObjectFieldSelector{FieldPath: "metadata.name"}
}
}