mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 01:50:55 +00:00
Fix names/doc in transformation config code.
This commit is contained in:
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
package defaultconfig
|
||||
|
||||
const commonAnnotationPathConfigs = `
|
||||
const commonAnnotationFieldSpecs = `
|
||||
commonAnnotations:
|
||||
- path: metadata/annotations
|
||||
create: true
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
package defaultconfig
|
||||
|
||||
const commonLabelPathConfigs = `
|
||||
const commonLabelFieldSpecs = `
|
||||
commonLabels:
|
||||
- path: metadata/labels
|
||||
create: true
|
||||
|
||||
@@ -22,27 +22,28 @@ import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// GetDefaultPathConfigs returns the default pathConfigs data
|
||||
func GetDefaultPathConfigs() []byte {
|
||||
// GetDefaultFieldSpecs returns default fieldSpecs.
|
||||
func GetDefaultFieldSpecs() []byte {
|
||||
configData := [][]byte{
|
||||
[]byte(namePrefixPathConfigs),
|
||||
[]byte(commonLabelPathConfigs),
|
||||
[]byte(commonAnnotationPathConfigs),
|
||||
[]byte(namespacePathConfigs),
|
||||
[]byte(varReferencePathConfigs),
|
||||
[]byte(nameReferencePathConfigs),
|
||||
[]byte(namePrefixFieldSpecs),
|
||||
[]byte(commonLabelFieldSpecs),
|
||||
[]byte(commonAnnotationFieldSpecs),
|
||||
[]byte(namespaceFieldSpecs),
|
||||
[]byte(varReferenceFieldSpecs),
|
||||
[]byte(nameReferenceFieldSpecs),
|
||||
}
|
||||
return bytes.Join(configData, []byte("\n"))
|
||||
}
|
||||
|
||||
// GetDefaultPathConfigStrings returns the default pathConfigs in string format
|
||||
func GetDefaultPathConfigStrings() map[string]string {
|
||||
// GetDefaultFieldSpecsAsMap returns default fieldSpecs
|
||||
// as a string->string map.
|
||||
func GetDefaultFieldSpecsAsMap() map[string]string {
|
||||
result := make(map[string]string)
|
||||
result["nameprefix"] = namePrefixPathConfigs
|
||||
result["commonlabels"] = commonLabelPathConfigs
|
||||
result["commonannotations"] = commonAnnotationPathConfigs
|
||||
result["namespace"] = namespacePathConfigs
|
||||
result["varreference"] = varReferencePathConfigs
|
||||
result["namereference"] = namespacePathConfigs
|
||||
result["nameprefix"] = namePrefixFieldSpecs
|
||||
result["commonlabels"] = commonLabelFieldSpecs
|
||||
result["commonannotations"] = commonAnnotationFieldSpecs
|
||||
result["namespace"] = namespaceFieldSpecs
|
||||
result["varreference"] = varReferenceFieldSpecs
|
||||
result["namereference"] = nameReferenceFieldSpecs
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
package defaultconfig
|
||||
|
||||
const (
|
||||
namePrefixPathConfigs = `
|
||||
namePrefixFieldSpecs = `
|
||||
namePrefix:
|
||||
- path: metadata/name
|
||||
`
|
||||
|
||||
@@ -17,26 +17,26 @@ limitations under the License.
|
||||
package defaultconfig
|
||||
|
||||
const (
|
||||
nameReferencePathConfigs = `
|
||||
nameReferenceFieldSpecs = `
|
||||
nameReference:
|
||||
- kind: Deployment
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/scaleTargetRef/name
|
||||
kind: HorizontalPodAutoscaler
|
||||
|
||||
- kind: ReplicationController
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/scaleTargetRef/name
|
||||
kind: HorizontalPodAutoscaler
|
||||
|
||||
- kind: ReplicaSet
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/scaleTargetRef/name
|
||||
kind: HorizontalPodAutoscaler
|
||||
|
||||
- kind: ConfigMap
|
||||
version: v1
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/volumes/configMap/name
|
||||
version: v1
|
||||
kind: Pod
|
||||
@@ -119,7 +119,7 @@ nameReference:
|
||||
|
||||
- kind: Secret
|
||||
version: v1
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/volumes/secret/secretName
|
||||
version: v1
|
||||
kind: Pod
|
||||
@@ -229,7 +229,7 @@ nameReference:
|
||||
|
||||
- kind: Service
|
||||
version: v1
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/serviceName
|
||||
kind: StatefulSet
|
||||
group: apps
|
||||
@@ -240,14 +240,14 @@ nameReference:
|
||||
|
||||
- kind: Role
|
||||
group: rbac.authorization.k8s.io
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: roleRef/name
|
||||
kind: RoleBinding
|
||||
group: rbac.authorization.k8s.io
|
||||
|
||||
- kind: ClusterRole
|
||||
group: rbac.authorization.k8s.io
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: roleRef/name
|
||||
kind: RoleBinding
|
||||
group: rbac.authorization.k8s.io
|
||||
@@ -257,7 +257,7 @@ nameReference:
|
||||
|
||||
- kind: ServiceAccount
|
||||
version: v1
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: subjects/name
|
||||
kind: RoleBinding
|
||||
group: rbac.authorization.k8s.io
|
||||
@@ -281,7 +281,7 @@ nameReference:
|
||||
|
||||
- kind: PersistentVolumeClaim
|
||||
version: v1
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/volumes/persistentVolumeClaim/claimName
|
||||
kind: Pod
|
||||
- path: spec/template/spec/volumes/persistentVolumeClaim/claimName
|
||||
@@ -299,7 +299,7 @@ nameReference:
|
||||
|
||||
- kind: PersistentVolume
|
||||
version: v1
|
||||
pathConfigs:
|
||||
fieldSpecs:
|
||||
- path: spec/volumeName
|
||||
kind: PersistentVolumeClaim
|
||||
`
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
package defaultconfig
|
||||
|
||||
const (
|
||||
namespacePathConfigs = `
|
||||
namespaceFieldSpecs = `
|
||||
namespace:
|
||||
- path: metadata/namespace
|
||||
create: true
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
package defaultconfig
|
||||
|
||||
const (
|
||||
varReferencePathConfigs = `
|
||||
varReferenceFieldSpecs = `
|
||||
varReference:
|
||||
- path: spec/template/spec/initContainers/command
|
||||
kind: StatefulSet
|
||||
|
||||
@@ -78,7 +78,7 @@ func (tf *Factory) EmptyConfig() *TransformerConfig {
|
||||
// This should never fail, hence the Fatal panic.
|
||||
func (tf *Factory) DefaultConfig() *TransformerConfig {
|
||||
c, err := makeTransformerConfigFromBytes(
|
||||
defaultconfig.GetDefaultPathConfigs())
|
||||
defaultconfig.GetDefaultFieldSpecs())
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to make default transformconfig: %v", err)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namePrefix:
|
||||
fakeFS.WriteFile("transformerconfig/test/config.yaml", []byte(transformerConfig))
|
||||
ldr := loader.NewFileLoader(fakeFS)
|
||||
expected := &TransformerConfig{
|
||||
NamePrefix: []PathConfig{
|
||||
NamePrefix: []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{Kind: "SomeKind"},
|
||||
Path: "nameprefix/path",
|
||||
|
||||
@@ -27,15 +27,15 @@ import (
|
||||
|
||||
// LoadCRDs parse CRD schemas from paths into a TransformerConfig
|
||||
func (tf *Factory) LoadCRDs(paths []string) (*TransformerConfig, error) {
|
||||
pathConfigs := tf.EmptyConfig()
|
||||
tc := tf.EmptyConfig()
|
||||
for _, path := range paths {
|
||||
pathConfig, err := tf.loadCRD(path)
|
||||
otherTc, err := tf.loadCRD(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pathConfigs = pathConfigs.Merge(pathConfig)
|
||||
tc = tc.Merge(otherTc)
|
||||
}
|
||||
return pathConfigs, nil
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
func (tf *Factory) loadCRD(path string) (*TransformerConfig, error) {
|
||||
@@ -57,13 +57,13 @@ func (tf *Factory) loadCRD(path string) (*TransformerConfig, error) {
|
||||
|
||||
crds := getCRDs(types)
|
||||
for crd, k := range crds {
|
||||
crdPathConfigs := tf.EmptyConfig()
|
||||
err = getCRDPathConfig(
|
||||
types, crd, crd, k, []string{}, crdPathConfigs)
|
||||
tc := tf.EmptyConfig()
|
||||
err = loadCrdIntoConfig(
|
||||
types, crd, crd, k, []string{}, tc)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = result.Merge(crdPathConfigs)
|
||||
result = result.Merge(tc)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -88,10 +88,11 @@ func getCRDs(types map[string]common.OpenAPIDefinition) map[string]gvk.Gvk {
|
||||
return crds
|
||||
}
|
||||
|
||||
// getCRDPathConfig gets pathConfigs for one CRD recursively
|
||||
func getCRDPathConfig(
|
||||
types map[string]common.OpenAPIDefinition, atype string, crd string, in gvk.Gvk,
|
||||
path []string, configs *TransformerConfig) error {
|
||||
// loadCrdIntoConfig loads a CRD spec into a TransformerConfig
|
||||
func loadCrdIntoConfig(
|
||||
types map[string]common.OpenAPIDefinition,
|
||||
atype string, crd string, in gvk.Gvk,
|
||||
path []string, config *TransformerConfig) error {
|
||||
if _, ok := types[crd]; !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -99,8 +100,8 @@ func getCRDPathConfig(
|
||||
for propname, property := range types[atype].Schema.SchemaProps.Properties {
|
||||
_, annotate := property.Extensions.GetString(Annotation)
|
||||
if annotate {
|
||||
configs.AddAnnotationPathConfig(
|
||||
PathConfig{
|
||||
config.AddAnnotationFieldSpec(
|
||||
FieldSpec{
|
||||
CreateIfNotPresent: false,
|
||||
Gvk: in,
|
||||
Path: strings.Join(append(path, propname), "/"),
|
||||
@@ -109,8 +110,8 @@ func getCRDPathConfig(
|
||||
}
|
||||
_, label := property.Extensions.GetString(LabelSelector)
|
||||
if label {
|
||||
configs.AddLabelPathConfig(
|
||||
PathConfig{
|
||||
config.AddLabelFieldSpec(
|
||||
FieldSpec{
|
||||
CreateIfNotPresent: false,
|
||||
Gvk: in,
|
||||
Path: strings.Join(append(path, propname), "/"),
|
||||
@@ -119,8 +120,8 @@ func getCRDPathConfig(
|
||||
}
|
||||
_, identity := property.Extensions.GetString(Identity)
|
||||
if identity {
|
||||
configs.AddPrefixPathConfig(
|
||||
PathConfig{
|
||||
config.AddPrefixFieldSpec(
|
||||
FieldSpec{
|
||||
CreateIfNotPresent: false,
|
||||
Gvk: in,
|
||||
Path: strings.Join(append(path, propname), "/"),
|
||||
@@ -135,9 +136,9 @@ func getCRDPathConfig(
|
||||
if !ok {
|
||||
nameKey = "name"
|
||||
}
|
||||
configs.AddNamereferencePathConfig(ReferencePathConfig{
|
||||
config.AddNamereferenceFieldSpec(NameBackReferences{
|
||||
Gvk: gvk.Gvk{Kind: kind, Version: version},
|
||||
PathConfigs: []PathConfig{
|
||||
FieldSpecs: []FieldSpec{
|
||||
{
|
||||
CreateIfNotPresent: false,
|
||||
Gvk: in,
|
||||
@@ -149,7 +150,9 @@ func getCRDPathConfig(
|
||||
}
|
||||
|
||||
if property.Ref.GetURL() != nil {
|
||||
getCRDPathConfig(types, property.Ref.String(), crd, in, append(path, propname), configs)
|
||||
loadCrdIntoConfig(
|
||||
types, property.Ref.String(), crd, in,
|
||||
append(path, propname), config)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -25,6 +25,12 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
)
|
||||
|
||||
// This defines two CRD's: Bee and MyKind.
|
||||
//
|
||||
// Bee is boring, it's spec has no dependencies.
|
||||
//
|
||||
// MyKind, however, has a spec that contains
|
||||
// a Bee and a (k8s native) Secret.
|
||||
const (
|
||||
crdContent = `
|
||||
{
|
||||
@@ -149,10 +155,10 @@ func makeLoader(t *testing.T) ifc.Loader {
|
||||
}
|
||||
|
||||
func TestLoadCRDs(t *testing.T) {
|
||||
refpathconfigs := []ReferencePathConfig{
|
||||
nbrs := []NameBackReferences{
|
||||
{
|
||||
Gvk: gvk.Gvk{Kind: "Secret", Version: "v1"},
|
||||
PathConfigs: []PathConfig{
|
||||
FieldSpecs: []FieldSpec{
|
||||
{
|
||||
CreateIfNotPresent: false,
|
||||
Gvk: gvk.Gvk{Kind: "MyKind"},
|
||||
@@ -162,7 +168,7 @@ func TestLoadCRDs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Gvk: gvk.Gvk{Kind: "Bee", Version: "v1beta1"},
|
||||
PathConfigs: []PathConfig{
|
||||
FieldSpecs: []FieldSpec{
|
||||
{
|
||||
CreateIfNotPresent: false,
|
||||
Gvk: gvk.Gvk{Kind: "MyKind"},
|
||||
@@ -172,14 +178,14 @@ func TestLoadCRDs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
expected := &TransformerConfig{
|
||||
NameReference: refpathconfigs,
|
||||
expectedTc := &TransformerConfig{
|
||||
NameReference: nbrs,
|
||||
}
|
||||
|
||||
pathconfig, _ := NewFactory(makeLoader(t)).LoadCRDs(
|
||||
actualTc, _ := NewFactory(makeLoader(t)).LoadCRDs(
|
||||
[]string{"/testpath/crd.json"})
|
||||
|
||||
if !reflect.DeepEqual(pathconfig, expected) {
|
||||
t.Fatalf("expected\n %v\n but got\n %v\n", expected, pathconfig)
|
||||
if !reflect.DeepEqual(actualTc, expectedTc) {
|
||||
t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc)
|
||||
}
|
||||
}
|
||||
|
||||
87
pkg/transformers/config/fieldspec.go
Normal file
87
pkg/transformers/config/fieldspec.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
)
|
||||
|
||||
// FieldSpec completely specifies a kustomizable field in
|
||||
// an unstructured representation of a k8s API object.
|
||||
// It helps define the operands of transformations.
|
||||
//
|
||||
// For example, a directive to add a common label to objects
|
||||
// will need to know that a 'Deployment' object (in API group
|
||||
// 'apps', any version) can have labels at field path
|
||||
// 'spec/template/metadata/labels', and further that it is OK
|
||||
// (or not OK) to add that field path to the object if the
|
||||
// field path doesn't exist already.
|
||||
//
|
||||
// This would look like
|
||||
// {
|
||||
// group: apps
|
||||
// kind: Deployment
|
||||
// path: spec/template/metadata/labels
|
||||
// create: true
|
||||
// }
|
||||
type FieldSpec struct {
|
||||
gvk.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
CreateIfNotPresent bool `json:"create,omitempty" yaml:"create,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
escapedForwardSlash = "\\/"
|
||||
tempSlashReplacement = "???"
|
||||
)
|
||||
|
||||
func (fs FieldSpec) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%s:%v:%s", fs.Gvk.String(), fs.CreateIfNotPresent, fs.Path)
|
||||
}
|
||||
|
||||
// PathSlice converts the path string to a slice of strings,
|
||||
// separated by a '/'. Forward slash can be contained in a
|
||||
// fieldname. such as ingress.kubernetes.io/auth-secret in
|
||||
// Ingress annotations. To deal with this special case, the
|
||||
// path to this field should be formatted as
|
||||
//
|
||||
// metadata/annotations/ingress.kubernetes.io\/auth-secret
|
||||
//
|
||||
// Then PathSlice will return
|
||||
//
|
||||
// []string{
|
||||
// "metadata",
|
||||
// "annotations",
|
||||
// "ingress.auth-secretkubernetes.io/auth-secret"
|
||||
// }
|
||||
func (fs FieldSpec) PathSlice() []string {
|
||||
if !strings.Contains(fs.Path, escapedForwardSlash) {
|
||||
return strings.Split(fs.Path, "/")
|
||||
}
|
||||
|
||||
s := strings.Replace(fs.Path, escapedForwardSlash, tempSlashReplacement, -1)
|
||||
paths := strings.Split(s, "/")
|
||||
var result []string
|
||||
for _, path := range paths {
|
||||
result = append(result, strings.Replace(path, tempSlashReplacement, "/", -1))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -37,8 +37,8 @@ func TestPathSlice(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, p := range paths {
|
||||
pathConfig := PathConfig{Path: p.input}
|
||||
actual := pathConfig.PathSlice()
|
||||
fs := FieldSpec{Path: p.input}
|
||||
actual := fs.PathSlice()
|
||||
if !reflect.DeepEqual(actual, p.parsed) {
|
||||
t.Fatalf("expected %v, but got %v", p.parsed, actual)
|
||||
}
|
||||
91
pkg/transformers/config/namebackreferences.go
Normal file
91
pkg/transformers/config/namebackreferences.go
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NameBackReferences is an association between a gvk.GVK and a list
|
||||
// of FieldSpec instances that could refer to it.
|
||||
//
|
||||
// It is used to handle name changes, and can be thought of as a
|
||||
// a contact list. If you change your own contact info (name,
|
||||
// phone number, etc.), you must tell your contacts or they won't
|
||||
// know about the change.
|
||||
//
|
||||
// For example, ConfigMaps can be used by Pods and everything that
|
||||
// contains a Pod; Deployment, Job, StatefulSet, etc. To change
|
||||
// the name of a ConfigMap instance from 'alice' to 'bob', one
|
||||
// must visit all objects that could refer to the ConfigMap, see if
|
||||
// they mention 'alice', and if so, change the reference to 'bob'.
|
||||
//
|
||||
// The NameBackReferences instance to aid in this could look like
|
||||
// {
|
||||
// kind: ConfigMap
|
||||
// version: v1
|
||||
// FieldSpecs:
|
||||
// - kind: Pod
|
||||
// version: v1
|
||||
// path: spec/volumes/configMap/name
|
||||
// - kind: Deployment
|
||||
// path: spec/template/spec/volumes/configMap/name
|
||||
// - kind: Job
|
||||
// path: spec/template/spec/volumes/configMap/name
|
||||
// (etc.)
|
||||
// }
|
||||
type NameBackReferences struct {
|
||||
gvk.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
FieldSpecs []FieldSpec `json:"FieldSpecs,omitempty" yaml:"FieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
func (n NameBackReferences) String() string {
|
||||
var r []string
|
||||
for _, f := range n.FieldSpecs {
|
||||
r = append(r, f.String())
|
||||
}
|
||||
return n.Gvk.String() + ": (\n" +
|
||||
strings.Join(r, "\n") + "\n)"
|
||||
}
|
||||
|
||||
func mergeNameBackReferences(
|
||||
a, b []NameBackReferences) []NameBackReferences {
|
||||
for _, r := range b {
|
||||
a = merge(a, r)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func merge(
|
||||
backRefsSlice []NameBackReferences,
|
||||
other NameBackReferences) []NameBackReferences {
|
||||
var result []NameBackReferences
|
||||
found := false
|
||||
for _, c := range backRefsSlice {
|
||||
if c.Equals(other.Gvk) {
|
||||
c.FieldSpecs = append(c.FieldSpecs, other.FieldSpecs...)
|
||||
found = true
|
||||
}
|
||||
result = append(result, c)
|
||||
}
|
||||
|
||||
if !found {
|
||||
result = append(result, other)
|
||||
}
|
||||
return result
|
||||
}
|
||||
106
pkg/transformers/config/namebackreferences_test.go
Normal file
106
pkg/transformers/config/namebackreferences_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFoo(t *testing.T) {
|
||||
fsSlice1 := []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "Pod",
|
||||
},
|
||||
Path: "path/to/a/name",
|
||||
CreateIfNotPresent: false,
|
||||
},
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "Deployment",
|
||||
},
|
||||
Path: "another/path/to/some/name",
|
||||
CreateIfNotPresent: false,
|
||||
},
|
||||
}
|
||||
fsSlice2 := []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "Job",
|
||||
},
|
||||
Path: "morepath/to/name",
|
||||
CreateIfNotPresent: false,
|
||||
},
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "StatefulSet",
|
||||
},
|
||||
Path: "yet/another/path/to/a/name",
|
||||
CreateIfNotPresent: false,
|
||||
},
|
||||
}
|
||||
|
||||
nbrsSlice1 := []NameBackReferences{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
FieldSpecs: fsSlice1,
|
||||
},
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "Secret",
|
||||
},
|
||||
FieldSpecs: fsSlice2,
|
||||
},
|
||||
}
|
||||
nbrsSlice2 := []NameBackReferences{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
FieldSpecs: fsSlice1,
|
||||
},
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "Secret",
|
||||
},
|
||||
FieldSpecs: fsSlice2,
|
||||
},
|
||||
}
|
||||
expected := []NameBackReferences{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
// Current behavior allows repeats of FieldSpec
|
||||
FieldSpecs: append(fsSlice1, fsSlice1...),
|
||||
},
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "Secret",
|
||||
},
|
||||
FieldSpecs: append(fsSlice2, fsSlice2...),
|
||||
},
|
||||
}
|
||||
actual := mergeNameBackReferences(nbrsSlice1, nbrsSlice2)
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("expected\n %v\n but got\n %v\n", expected, actual)
|
||||
}
|
||||
}
|
||||
@@ -1,60 +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 config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
)
|
||||
|
||||
// PathConfig contains the configuration of a field, including the gvk it ties to,
|
||||
// path to the field, etc.
|
||||
type PathConfig struct {
|
||||
gvk.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
CreateIfNotPresent bool `json:"create,omitempty" yaml:"create,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
escapedForwardSlash = "\\/"
|
||||
tempSlashReplacement = "???"
|
||||
)
|
||||
|
||||
// PathSlice converts the path string to a slice of strings, separated by "/"
|
||||
// "/" can be contained in a fieldname.
|
||||
// such as ingress.kubernetes.io/auth-secret in Ingress annotations.
|
||||
// To deal with this special case, the path to this field should be formatted as
|
||||
//
|
||||
// metadata/annotations/ingress.kubernetes.io\/auth-secret
|
||||
//
|
||||
// Then PathSlice will return
|
||||
//
|
||||
// []string{"metadata", "annotations", "ingress.auth-secretkubernetes.io/auth-secret"}
|
||||
func (p PathConfig) PathSlice() []string {
|
||||
if !strings.Contains(p.Path, escapedForwardSlash) {
|
||||
return strings.Split(p.Path, "/")
|
||||
}
|
||||
|
||||
s := strings.Replace(p.Path, escapedForwardSlash, tempSlashReplacement, -1)
|
||||
paths := strings.Split(s, "/")
|
||||
var result []string
|
||||
for _, path := range paths {
|
||||
result = append(result, strings.Replace(path, tempSlashReplacement, "/", -1))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,64 +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 config
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
)
|
||||
|
||||
// ReferencePathConfig contains the configuration of a field that references
|
||||
// the name of another resource whose GroupVersionKind is specified in referencedGVK.
|
||||
// e.g. pod.spec.template.volumes.configMap.name references the name of a configmap
|
||||
// Its corresponding referencePathConfig will look like:
|
||||
//
|
||||
// ReferencePathConfig{
|
||||
// referencedGVK: schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"},
|
||||
// pathConfigs: []PathConfig{
|
||||
// {
|
||||
// GroupVersionKind: &schema.GroupVersionKind{Version: "v1", Kind: "Pod"},
|
||||
// Path: []string{"spec", "volumes", "configMap", "name"},
|
||||
// },
|
||||
// }
|
||||
type ReferencePathConfig struct {
|
||||
gvk.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
// PathConfig is the gvk that is referencing the referencedGVK object's name.
|
||||
PathConfigs []PathConfig `json:"pathConfigs,omitempty" yaml:"pathConfigs,omitempty"`
|
||||
}
|
||||
|
||||
func merge(configs []ReferencePathConfig, config ReferencePathConfig) []ReferencePathConfig {
|
||||
var result []ReferencePathConfig
|
||||
found := false
|
||||
for _, c := range configs {
|
||||
if c.Equals(config.Gvk) {
|
||||
c.PathConfigs = append(c.PathConfigs, config.PathConfigs...)
|
||||
found = true
|
||||
}
|
||||
result = append(result, c)
|
||||
}
|
||||
|
||||
if !found {
|
||||
result = append(result, config)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func mergeNameReferencePathConfigs(a, b []ReferencePathConfig) []ReferencePathConfig {
|
||||
for _, r := range b {
|
||||
a = merge(a, r)
|
||||
}
|
||||
return a
|
||||
}
|
||||
@@ -22,30 +22,30 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type rpcSlice []ReferencePathConfig
|
||||
type nbrSlice []NameBackReferences
|
||||
|
||||
func (s rpcSlice) Len() int { return len(s) }
|
||||
func (s rpcSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s rpcSlice) Less(i, j int) bool {
|
||||
func (s nbrSlice) Len() int { return len(s) }
|
||||
func (s nbrSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s nbrSlice) Less(i, j int) bool {
|
||||
return s[i].Gvk.IsLessThan(s[j].Gvk)
|
||||
}
|
||||
|
||||
type pcSlice []PathConfig
|
||||
type fsSlice []FieldSpec
|
||||
|
||||
func (s pcSlice) Len() int { return len(s) }
|
||||
func (s pcSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s pcSlice) Less(i, j int) bool {
|
||||
func (s fsSlice) Len() int { return len(s) }
|
||||
func (s fsSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s fsSlice) Less(i, j int) bool {
|
||||
return s[i].Gvk.IsLessThan(s[j].Gvk)
|
||||
}
|
||||
|
||||
// TransformerConfig represents the path configurations for different transformations
|
||||
// TransformerConfig holds the data needed to perform transformations.
|
||||
type TransformerConfig struct {
|
||||
NamePrefix pcSlice `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
|
||||
NameSpace pcSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
CommonLabels pcSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
|
||||
CommonAnnotations pcSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
|
||||
NameReference rpcSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"`
|
||||
VarReference pcSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"`
|
||||
NamePrefix fsSlice `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
|
||||
NameSpace fsSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
CommonLabels fsSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
|
||||
CommonAnnotations fsSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
|
||||
NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"`
|
||||
VarReference fsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"`
|
||||
}
|
||||
|
||||
// sortFields provides determinism in logging, tests, etc.
|
||||
@@ -58,24 +58,24 @@ func (t *TransformerConfig) sortFields() {
|
||||
sort.Sort(t.VarReference)
|
||||
}
|
||||
|
||||
// AddPrefixPathConfig adds a PathConfig to NamePrefix
|
||||
func (t *TransformerConfig) AddPrefixPathConfig(config PathConfig) {
|
||||
t.NamePrefix = append(t.NamePrefix, config)
|
||||
// AddPrefixFieldSpec adds a FieldSpec to NamePrefix
|
||||
func (t *TransformerConfig) AddPrefixFieldSpec(fs FieldSpec) {
|
||||
t.NamePrefix = append(t.NamePrefix, fs)
|
||||
}
|
||||
|
||||
// AddLabelPathConfig adds a PathConfig to CommonLabels
|
||||
func (t *TransformerConfig) AddLabelPathConfig(config PathConfig) {
|
||||
t.CommonLabels = append(t.CommonLabels, config)
|
||||
// AddLabelFieldSpec adds a FieldSpec to CommonLabels
|
||||
func (t *TransformerConfig) AddLabelFieldSpec(fs FieldSpec) {
|
||||
t.CommonLabels = append(t.CommonLabels, fs)
|
||||
}
|
||||
|
||||
// AddAnnotationPathConfig adds a PathConfig to CommonAnnotations
|
||||
func (t *TransformerConfig) AddAnnotationPathConfig(config PathConfig) {
|
||||
t.CommonAnnotations = append(t.CommonAnnotations, config)
|
||||
// AddAnnotationFieldSpec adds a FieldSpec to CommonAnnotations
|
||||
func (t *TransformerConfig) AddAnnotationFieldSpec(fs FieldSpec) {
|
||||
t.CommonAnnotations = append(t.CommonAnnotations, fs)
|
||||
}
|
||||
|
||||
// AddNamereferencePathConfig adds a ReferencePathConfig to NameReference
|
||||
func (t *TransformerConfig) AddNamereferencePathConfig(config ReferencePathConfig) {
|
||||
t.NameReference = mergeNameReferencePathConfigs(t.NameReference, []ReferencePathConfig{config})
|
||||
// AddNamereferenceFieldSpec adds a NameBackReferences to NameReference
|
||||
func (t *TransformerConfig) AddNamereferenceFieldSpec(nbrs NameBackReferences) {
|
||||
t.NameReference = mergeNameBackReferences(t.NameReference, []NameBackReferences{nbrs})
|
||||
}
|
||||
|
||||
// Merge merges two TransformerConfigs objects into a new TransformerConfig object
|
||||
@@ -86,7 +86,7 @@ func (t *TransformerConfig) Merge(input *TransformerConfig) *TransformerConfig {
|
||||
merged.CommonAnnotations = append(t.CommonAnnotations, input.CommonAnnotations...)
|
||||
merged.CommonLabels = append(t.CommonLabels, input.CommonLabels...)
|
||||
merged.VarReference = append(t.VarReference, input.VarReference...)
|
||||
merged.NameReference = mergeNameReferencePathConfigs(t.NameReference, input.NameReference)
|
||||
merged.NameReference = mergeNameBackReferences(t.NameReference, input.NameReference)
|
||||
merged.sortFields()
|
||||
return merged
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
)
|
||||
|
||||
func TestAddNameReferencePathConfigs(t *testing.T) {
|
||||
func TestAddNamereferenceFieldSpec(t *testing.T) {
|
||||
cfg := &TransformerConfig{}
|
||||
|
||||
pathConfig := ReferencePathConfig{
|
||||
nbrs := NameBackReferences{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "KindA",
|
||||
},
|
||||
PathConfigs: []PathConfig{
|
||||
FieldSpecs: []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "KindB",
|
||||
@@ -42,42 +42,42 @@ func TestAddNameReferencePathConfigs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
cfg.AddNamereferencePathConfig(pathConfig)
|
||||
cfg.AddNamereferenceFieldSpec(nbrs)
|
||||
if len(cfg.NameReference) != 1 {
|
||||
t.Fatal("failed to add namerefence pathconfig")
|
||||
t.Fatal("failed to add namerefence FieldSpec")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPathConfigs(t *testing.T) {
|
||||
func TestAddFieldSpecs(t *testing.T) {
|
||||
cfg := &TransformerConfig{}
|
||||
|
||||
pathConfig := PathConfig{
|
||||
fieldSpec := FieldSpec{
|
||||
Gvk: gvk.Gvk{Group: "GroupA", Kind: "KindB"},
|
||||
Path: "path/to/a/field",
|
||||
CreateIfNotPresent: true,
|
||||
}
|
||||
|
||||
cfg.AddPrefixPathConfig(pathConfig)
|
||||
cfg.AddPrefixFieldSpec(fieldSpec)
|
||||
if len(cfg.NamePrefix) != 1 {
|
||||
t.Fatalf("failed to add nameprefix pathconfig")
|
||||
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||
}
|
||||
cfg.AddLabelPathConfig(pathConfig)
|
||||
cfg.AddLabelFieldSpec(fieldSpec)
|
||||
if len(cfg.CommonLabels) != 1 {
|
||||
t.Fatalf("failed to add nameprefix pathconfig")
|
||||
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||
}
|
||||
cfg.AddAnnotationPathConfig(pathConfig)
|
||||
cfg.AddAnnotationFieldSpec(fieldSpec)
|
||||
if len(cfg.CommonAnnotations) != 1 {
|
||||
t.Fatalf("failed to add nameprefix pathconfig")
|
||||
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
nameReference := []ReferencePathConfig{
|
||||
nameReference := []NameBackReferences{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "KindA",
|
||||
},
|
||||
PathConfigs: []PathConfig{
|
||||
FieldSpecs: []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "KindB",
|
||||
@@ -91,7 +91,7 @@ func TestMerge(t *testing.T) {
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "KindA",
|
||||
},
|
||||
PathConfigs: []PathConfig{
|
||||
FieldSpecs: []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{
|
||||
Kind: "KindC",
|
||||
@@ -102,7 +102,7 @@ func TestMerge(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
pathConfigs := []PathConfig{
|
||||
fieldSpecs := []FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{Group: "GroupA", Kind: "KindB"},
|
||||
Path: "path/to/a/field",
|
||||
@@ -115,28 +115,28 @@ func TestMerge(t *testing.T) {
|
||||
},
|
||||
}
|
||||
cfga := &TransformerConfig{}
|
||||
cfga.AddNamereferencePathConfig(nameReference[0])
|
||||
cfga.AddPrefixPathConfig(pathConfigs[0])
|
||||
cfga.AddNamereferenceFieldSpec(nameReference[0])
|
||||
cfga.AddPrefixFieldSpec(fieldSpecs[0])
|
||||
|
||||
cfgb := &TransformerConfig{}
|
||||
cfgb.AddNamereferencePathConfig(nameReference[1])
|
||||
cfgb.AddPrefixPathConfig(pathConfigs[1])
|
||||
cfgb.AddNamereferenceFieldSpec(nameReference[1])
|
||||
cfgb.AddPrefixFieldSpec(fieldSpecs[1])
|
||||
|
||||
actual := cfga.Merge(cfgb)
|
||||
|
||||
if len(actual.NamePrefix) != 2 {
|
||||
t.Fatal("merge failed for namePrefix pathconfig")
|
||||
t.Fatal("merge failed for namePrefix FieldSpec")
|
||||
}
|
||||
|
||||
if len(actual.NameReference) != 1 {
|
||||
t.Fatal("merge failed for namereference pathconfig")
|
||||
t.Fatal("merge failed for namereference FieldSpec")
|
||||
}
|
||||
|
||||
expected := &TransformerConfig{}
|
||||
expected.AddNamereferencePathConfig(nameReference[0])
|
||||
expected.AddNamereferencePathConfig(nameReference[1])
|
||||
expected.AddPrefixPathConfig(pathConfigs[0])
|
||||
expected.AddPrefixPathConfig(pathConfigs[1])
|
||||
expected.AddNamereferenceFieldSpec(nameReference[0])
|
||||
expected.AddNamereferenceFieldSpec(nameReference[1])
|
||||
expected.AddPrefixFieldSpec(fieldSpecs[0])
|
||||
expected.AddPrefixFieldSpec(fieldSpecs[1])
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("expected: %v\n but got: %v\n", expected, actual)
|
||||
|
||||
Reference in New Issue
Block a user