Skip to content
Permalink
Browse files

add varialber reference support

  • Loading branch information...
Liujingfang1 committed Jun 4, 2018
1 parent ea2d2c9 commit 526ba2df0c9cffd81e1eaa7c6a1ac44ef6a4ec49
Showing with 242 additions and 4 deletions.
  1. +100 −4 pkg/app/application.go
  2. +38 −0 pkg/app/var.go
  3. +83 −0 pkg/transformers/refvars.go
  4. +21 −0 pkg/types/kustomization.go
@@ -19,8 +19,11 @@ package app
import (
"bytes"
"encoding/json"
"fmt"
"strings"

"github.com/ghodss/yaml"
"github.com/golang/glog"

"github.com/kubernetes-sigs/kustomize/pkg/constants"
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
@@ -41,6 +44,8 @@ type Application interface {
// 1) untransformed resources from current kustomization file
// 2) transformed resources from sub packages
RawResources() (resmap.ResMap, error)
// Vars returns all the variables defined by the app
Vars() ([]types.Var, error)
}

var _ Application = &applicationImpl{}
@@ -74,7 +79,7 @@ func (a *applicationImpl) Resources() (resmap.ResMap, error) {
if err != nil {
return nil, err
}
t, err := a.getHashAndReferenceTransformer()
t, err := a.getHashAndReferenceTransformer(res)
if err != nil {
return nil, err
}
@@ -128,7 +133,6 @@ func (a *applicationImpl) SemiResources() (resmap.ResMap, error) {
if err != nil {
return nil, err
}

return allRes, nil
}

@@ -140,7 +144,7 @@ func (a *applicationImpl) RawResources() (resmap.ResMap, error) {
if err != nil {
return nil, err
}
t, err := a.getHashAndReferenceTransformer()
t, err := a.getHashAndReferenceTransformer(res)
if err != nil {
return nil, err
}
@@ -194,6 +198,28 @@ func (a *applicationImpl) subAppResources() (resmap.ResMap, *interror.Kustomizat
return allResources, errs
}

func (a *applicationImpl) subApp() ([]Application, error) {
var apps []Application
errs := &interror.KustomizationErrors{}
for _, basePath := range a.kustomization.Bases {
subloader, err := a.loader.New(basePath)
if err != nil {
errs.Append(err)
continue
}
subapp, err := New(subloader)
if err != nil {
errs.Append(err)
continue
}
apps = append(apps, subapp)
}
if len(errs.Get()) > 0 {
return nil, errs
}
return apps, nil
}

// getTransformer generates the following transformers:
// 1) apply overlay
// 2) name prefix
@@ -234,7 +260,8 @@ func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transfor
// getHashAndReferenceTransformer generates the following transformers:
// 1) name hash for configmap and secrests
// 2) apply name reference
func (a *applicationImpl) getHashAndReferenceTransformer() (transformers.Transformer, error) {
// 3) apply reference variables
func (a *applicationImpl) getHashAndReferenceTransformer(allRes resmap.ResMap) (transformers.Transformer, error) {
ts := []transformers.Transformer{}
nht := transformers.NewNameHashTransformer()
ts = append(ts, nht)
@@ -244,9 +271,30 @@ func (a *applicationImpl) getHashAndReferenceTransformer() (transformers.Transfo
return nil, err
}
ts = append(ts, nrt)
t, err := a.getVariableReferenceTransformer(allRes)
if err != nil {
return nil, err
}
ts = append(ts, t)

return transformers.NewMultiTransformer(ts), nil
}

func (a *applicationImpl) getVariableReferenceTransformer(allRes resmap.ResMap) (transformers.Transformer, error) {
refvars, err := a.resolveRefVars(allRes)
if err != nil {
return nil, err
}

glog.Infof("found all the refvars: %+v", refvars)

varExpander, err := transformers.NewRefVarTransformer(refvars)
if err != nil {
return nil, err
}
return varExpander, nil
}

func unmarshal(y []byte, o interface{}) error {
j, err := yaml.YAMLToJSON(y)
if err != nil {
@@ -257,3 +305,51 @@ func unmarshal(y []byte, o interface{}) error {
dec.DisallowUnknownFields()
return dec.Decode(o)
}

func (a *applicationImpl) resolveRefVars(resources resmap.ResMap) (map[string]string, error) {
refvars := map[string]string{}
vars, err := a.Vars()
if err != nil {
return refvars, err
}

for _, refvar := range vars {
refGVKN := gvkn(refvar)
if r, found := resources[refGVKN]; found {
s, err := getFieldAsString(r.Unstruct().UnstructuredContent(), strings.Split(refvar.FieldRef.FieldPath, "."))
if err != nil {
return nil, fmt.Errorf("failed to resolve referred var: %+v", refvar)
}
refvars[refvar.Name] = s
} else {
glog.Infof("couldn't resolve refvar: %v", refvar)
}
}
return refvars, nil
}

// Vars returns all the variables defined at the app and subapps of the app
func (a *applicationImpl) Vars() ([]types.Var, error) {
vars := []types.Var{}
errs := &interror.KustomizationErrors{}

apps, err := a.subApp()
if err != nil {
return nil, err
}

// TODO: computing vars and resources for subApps can be combined
for _, subApp := range apps {
subAppVars, err := subApp.Vars()
if err != nil {
errs.Append(err)
continue
}
vars = append(vars, subAppVars...)
}
vars = append(vars, a.kustomization.Vars...)
if len(errs.Get()) > 0 {
return nil, errs
}
return vars, nil
}
@@ -0,0 +1,38 @@
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)
}
}
@@ -0,0 +1,83 @@
package transformers

import (
"fmt"

"github.com/kubernetes-sigs/kustomize/pkg/expansion"
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
"k8s.io/apimachinery/pkg/runtime/schema"
)

type refvarTransformer struct {
pathConfigs []PathConfig
vars map[string]string
}

func NewRefVarTransformer(vars map[string]string) (Transformer, error) {
return &refvarTransformer{
vars: vars,
pathConfigs: []PathConfig{
{
GroupVersionKind: &schema.GroupVersionKind{Kind: "StatefulSet"},
Path: []string{"spec", "template", "spec", "initContainers", "command"},
},
{
GroupVersionKind: &schema.GroupVersionKind{Kind: "StatefulSet"},
Path: []string{"spec", "template", "spec", "containers", "command"},
},
{
GroupVersionKind: &schema.GroupVersionKind{Kind: "Job"},
Path: []string{"spec", "template", "spec", "containers", "command"},
},
},
}, nil
}

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 {
obj := resources[GVKn].Unstruct()
objMap := obj.UnstructuredContent()
for _, pc := range rv.pathConfigs {
if !selectByGVK(GVKn.Gvk(), pc.GroupVersionKind) {
continue
}
err := mutateField(objMap, pc.Path, false, func(in interface{}) (interface{}, error) {
var (
mappingFunc = expansion.MappingFuncFor(rv.vars)
)
switch vt := in.(type) {
case []interface{}:
var xs []string
for _, a := range in.([]interface{}) {
xs = append(xs, expansion.Expand(a.(string), mappingFunc))
}
return xs, nil
case interface{}:
s, ok := in.(string)
if !ok {
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
}
runtimeVal := expansion.Expand(s, mappingFunc)
return runtimeVal, nil
default:
return "", fmt.Errorf("invalid type encountered %T", vt)
}
return "", fmt.Errorf("invalid type encountered")
})
if err != nil {
return err
}
}
}
return nil
}
@@ -16,6 +16,10 @@ limitations under the License.

package types

import (
corev1 "k8s.io/api/core/v1"
)

// Kustomization holds the information needed to generate customized k8s api resources.
type Kustomization struct {
// NamePrefix will prefix the names of all resources mentioned in the kustomization
@@ -61,6 +65,9 @@ type Kustomization struct {
// If a secret want to have a base and an overlay, it should go to Bases and
// Overlays fields.
SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"`

// Variables which will be substituted at runtime
Vars []Var `json:"vars,omitempty" yaml:"vars,omitempty"`
}

// ConfigMapArg contains the metadata of how to generate a configmap.
@@ -129,3 +136,17 @@ type DataSources struct {
// i.e. a Docker .env file or a .ini file.
EnvSource string `json:"env,omitempty" yaml:"env,omitempty"`
}

// Var represents a variable whose value will be source'd from a Kubernetes object
// and will be substituted at runtime.
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 refers to a Kubernetes Resource
ObjRef corev1.ObjectReference `json:"objref" yaml:"objref"`

// FieldRef refers to the fieldpath to extract value from a Kubernetes Object
FieldRef corev1.ObjectFieldSelector `json:"fieldref" yaml:"objref"`
}

0 comments on commit 526ba2d

Please sign in to comment.
You can’t perform that action at this time.