mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
add varialber reference support
This commit is contained in:
@@ -19,8 +19,11 @@ package app
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||||
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
||||||
@@ -41,6 +44,8 @@ type Application interface {
|
|||||||
// 1) untransformed resources from current kustomization file
|
// 1) untransformed resources from current kustomization file
|
||||||
// 2) transformed resources from sub packages
|
// 2) transformed resources from sub packages
|
||||||
RawResources() (resmap.ResMap, error)
|
RawResources() (resmap.ResMap, error)
|
||||||
|
// Vars returns all the variables defined by the app
|
||||||
|
Vars() ([]types.Var, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Application = &applicationImpl{}
|
var _ Application = &applicationImpl{}
|
||||||
@@ -74,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()
|
t, err := a.getHashAndReferenceTransformer(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -128,7 +133,6 @@ func (a *applicationImpl) SemiResources() (resmap.ResMap, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return allRes, nil
|
return allRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,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()
|
t, err := a.getHashAndReferenceTransformer(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -194,6 +198,28 @@ func (a *applicationImpl) subAppResources() (resmap.ResMap, *interror.Kustomizat
|
|||||||
return allResources, errs
|
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:
|
// getTransformer generates the following transformers:
|
||||||
// 1) apply overlay
|
// 1) apply overlay
|
||||||
// 2) name prefix
|
// 2) name prefix
|
||||||
@@ -234,7 +260,8 @@ func (a *applicationImpl) getTransformer(patches []*resource.Resource) (transfor
|
|||||||
// getHashAndReferenceTransformer generates the following transformers:
|
// getHashAndReferenceTransformer 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
|
||||||
func (a *applicationImpl) getHashAndReferenceTransformer() (transformers.Transformer, error) {
|
// 3) apply reference variables
|
||||||
|
func (a *applicationImpl) getHashAndReferenceTransformer(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)
|
||||||
@@ -244,9 +271,30 @@ func (a *applicationImpl) getHashAndReferenceTransformer() (transformers.Transfo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ts = append(ts, nrt)
|
ts = append(ts, nrt)
|
||||||
|
t, err := a.getVariableReferenceTransformer(allRes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ts = append(ts, t)
|
||||||
|
|
||||||
return transformers.NewMultiTransformer(ts), nil
|
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 {
|
func unmarshal(y []byte, o interface{}) error {
|
||||||
j, err := yaml.YAMLToJSON(y)
|
j, err := yaml.YAMLToJSON(y)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -257,3 +305,51 @@ func unmarshal(y []byte, o interface{}) error {
|
|||||||
dec.DisallowUnknownFields()
|
dec.DisallowUnknownFields()
|
||||||
return dec.Decode(o)
|
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
|
||||||
|
}
|
||||||
|
|||||||
38
pkg/app/var.go
Normal file
38
pkg/app/var.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
83
pkg/transformers/refvars.go
Normal file
83
pkg/transformers/refvars.go
Normal file
@@ -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
|
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
|
||||||
@@ -61,6 +65,9 @@ type Kustomization struct {
|
|||||||
// If a secret want to have a base and an overlay, it should go to Bases and
|
// If a secret want to have a base and an overlay, it should go to Bases and
|
||||||
// Overlays fields.
|
// Overlays fields.
|
||||||
SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"`
|
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.
|
// 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.
|
// 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 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"`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user