mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 08:20:59 +00:00
add varialber reference support
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user