mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 10:00:56 +00:00
Patchtransformers - drop copied code, improve deletion handling.
This commit is contained in:
@@ -9,9 +9,11 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -396,6 +398,23 @@ func (r *Resource) AppendRefVarName(variable types.Var) {
|
||||
r.refVarNames = append(r.refVarNames, variable.Name)
|
||||
}
|
||||
|
||||
// ApplySmPatch applies the provided strategic merge patch.
|
||||
func (r *Resource) ApplySmPatch(patch *Resource) error {
|
||||
node, err := filtersutil.GetRNode(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, ns := r.GetName(), r.GetNamespace()
|
||||
err = filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
|
||||
Patch: node,
|
||||
}, r)
|
||||
if !r.IsEmpty() {
|
||||
r.SetName(n)
|
||||
r.SetNamespace(ns)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Add BinaryData once we sync to new k8s.io/api
|
||||
func mergeConfigmap(
|
||||
mergedTo map[string]interface{},
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
package resource_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
. "sigs.k8s.io/kustomize/api/resource"
|
||||
@@ -123,3 +125,575 @@ func TestDeepCopy(t *testing.T) {
|
||||
t.Errorf("expected %v\nbut got%v", r, cr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplySmPatch_1(t *testing.T) {
|
||||
resource, err := factory.FromBytes([]byte(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
baseAnno: This is a base annotation
|
||||
labels:
|
||||
app: mungebot
|
||||
foo: bar
|
||||
name: bingo
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
foo: bar
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mungebot
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: foo
|
||||
value: bar
|
||||
image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
`))
|
||||
assert.NoError(t, err)
|
||||
patch, err := factory.FromBytes([]byte(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: baseprefix-mungebot
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 777
|
||||
`))
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, resource.ApplySmPatch(patch))
|
||||
bytes, err := resource.AsYAML()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
baseAnno: This is a base annotation
|
||||
labels:
|
||||
app: mungebot
|
||||
foo: bar
|
||||
name: bingo
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
foo: bar
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mungebot
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: foo
|
||||
value: bar
|
||||
image: nginx
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 777
|
||||
- containerPort: 80
|
||||
`, string(bytes))
|
||||
}
|
||||
|
||||
func TestApplySmPatch(t *testing.T) {
|
||||
const (
|
||||
myDeployment = "Deployment"
|
||||
myCRD = "myCRD"
|
||||
)
|
||||
|
||||
tests := map[string]struct {
|
||||
base string
|
||||
patch []string
|
||||
expected string
|
||||
errorExpected bool
|
||||
errorMsg string
|
||||
}{
|
||||
"withschema-label-image-container": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
addContainerAndEnvPatch(myDeployment),
|
||||
},
|
||||
errorExpected: false,
|
||||
expected: expectedResultMultiPatch(myDeployment, false),
|
||||
},
|
||||
"withschema-image-container-label": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
addContainerAndEnvPatch(myDeployment),
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
},
|
||||
errorExpected: false,
|
||||
expected: expectedResultMultiPatch(myDeployment, true),
|
||||
},
|
||||
"withschema-container-label-image": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
addContainerAndEnvPatch(myDeployment),
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
},
|
||||
errorExpected: false,
|
||||
expected: expectedResultMultiPatch(myDeployment, true),
|
||||
},
|
||||
"noschema-label-image-container": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
addContainerAndEnvPatch(myCRD),
|
||||
},
|
||||
// Might be better if this complained about patch conflict.
|
||||
// See plugin/builtin/patchstrategicmergetransformer/psmt_test.go
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: ANOTHERENV
|
||||
value: ANOTHERVALUE
|
||||
name: nginx
|
||||
- image: anotherimage
|
||||
name: anothercontainer
|
||||
`,
|
||||
},
|
||||
"noschema-image-container-label": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
addContainerAndEnvPatch(myCRD),
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
},
|
||||
// Might be better if this complained about patch conflict.
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"noschema-container-label-image": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
addContainerAndEnvPatch(myCRD),
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
},
|
||||
// Might be better if this complained about patch conflict.
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
|
||||
"withschema-label-latest-someV-01": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
changeImagePatch(myDeployment, "nginx:1.7.9"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
image: nginx:1.7.9
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"withschema-latest-label-someV-02": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
changeImagePatch(myDeployment, "nginx:1.7.9"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
image: nginx:1.7.9
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"withschema-latest-label-someV-03": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
changeImagePatch(myDeployment, "nginx:1.7.9"),
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
image: nginx:latest
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"withschema-latest-label-someV-04": {
|
||||
base: baseResource(myDeployment),
|
||||
patch: []string{
|
||||
changeImagePatch(myDeployment, "nginx:1.7.9"),
|
||||
changeImagePatch(myDeployment, "nginx:latest"),
|
||||
addLabelAndEnvPatch(myDeployment),
|
||||
changeImagePatch(myDeployment, "nginx:nginx"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
image: nginx:nginx
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"noschema-latest-label-someV-01": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
changeImagePatch(myCRD, "nginx:1.7.9"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.7.9
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"noschema-latest-label-someV-02": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
changeImagePatch(myCRD, "nginx:1.7.9"),
|
||||
},
|
||||
expected: expectedResultJMP("nginx:1.7.9"),
|
||||
},
|
||||
"noschema-latest-label-someV-03": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
changeImagePatch(myCRD, "nginx:1.7.9"),
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
"noschema-latest-label-someV-04": {
|
||||
base: baseResource(myCRD),
|
||||
patch: []string{
|
||||
changeImagePatch(myCRD, "nginx:1.7.9"),
|
||||
changeImagePatch(myCRD, "nginx:latest"),
|
||||
addLabelAndEnvPatch(myCRD),
|
||||
changeImagePatch(myCRD, "nginx:nginx"),
|
||||
},
|
||||
expected: `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:nginx
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
resource, err := factory.FromBytes([]byte(test.base))
|
||||
assert.NoError(t, err)
|
||||
for _, p := range test.patch {
|
||||
patch, err := factory.FromBytes([]byte(p))
|
||||
assert.NoError(t, err, name)
|
||||
assert.NoError(t, resource.ApplySmPatch(patch), name)
|
||||
}
|
||||
bytes, err := resource.AsYAML()
|
||||
if test.errorExpected {
|
||||
assert.Error(t, err, name)
|
||||
} else {
|
||||
assert.NoError(t, err, name)
|
||||
assert.Equal(t, test.expected, string(bytes), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// baseResource produces a base object which used to test
|
||||
// patch transformation
|
||||
// Also the structure is matching the Deployment syntax
|
||||
// the kind can be replaced to allow testing using CRD
|
||||
// without access to the schema
|
||||
func baseResource(kind string) string {
|
||||
res := `
|
||||
apiVersion: apps/v1
|
||||
kind: %s
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx`
|
||||
return fmt.Sprintf(res, kind)
|
||||
}
|
||||
|
||||
// addContainerAndEnvPatch produces a patch object which adds
|
||||
// an entry in the env slice of the first/nginx container
|
||||
// as well as adding a label in the metadata
|
||||
// Note that for SMP/WithSchema merge, the name:nginx entry
|
||||
// is mandatory
|
||||
func addLabelAndEnvPatch(kind string) string {
|
||||
return fmt.Sprintf(`
|
||||
apiVersion: apps/v1
|
||||
kind: %s
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE`, kind)
|
||||
}
|
||||
|
||||
// addContainerAndEnvPatch produces a patch object which adds
|
||||
// an entry in the env slice of the first/nginx container
|
||||
// as well as adding a second container in the container list
|
||||
// Note that for SMP/WithSchema merge, the name:nginx entry
|
||||
// is mandatory
|
||||
func addContainerAndEnvPatch(kind string) string {
|
||||
return fmt.Sprintf(`
|
||||
apiVersion: apps/v1
|
||||
kind: %s
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
env:
|
||||
- name: ANOTHERENV
|
||||
value: ANOTHERVALUE
|
||||
- name: anothercontainer
|
||||
image: anotherimage`, kind)
|
||||
}
|
||||
|
||||
// addContainerAndEnvPatch produces a patch object which replaces
|
||||
// the value of the image field in the first/nginx container
|
||||
// Note that for SMP/WithSchema merge, the name:nginx entry
|
||||
// is mandatory
|
||||
func changeImagePatch(kind string, newImage string) string {
|
||||
return fmt.Sprintf(`
|
||||
apiVersion: apps/v1
|
||||
kind: %s
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: %s`, kind, newImage)
|
||||
}
|
||||
|
||||
// utility method to build the expected result of a multipatch
|
||||
// the order of the patches still have influence especially
|
||||
// in the insertion location within arrays.
|
||||
func expectedResultMultiPatch(kind string, reversed bool) string {
|
||||
pattern := `apiVersion: apps/v1
|
||||
kind: %s
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
%s
|
||||
image: nginx:latest
|
||||
name: nginx
|
||||
- image: anotherimage
|
||||
name: anothercontainer
|
||||
`
|
||||
if reversed {
|
||||
return fmt.Sprintf(pattern, kind, `- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
- name: ANOTHERENV
|
||||
value: ANOTHERVALUE`)
|
||||
}
|
||||
return fmt.Sprintf(pattern, kind, `- name: ANOTHERENV
|
||||
value: ANOTHERVALUE
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE`)
|
||||
}
|
||||
|
||||
// utility method building the expected output of a JMP.
|
||||
// imagename parameter allows to build a result consistent
|
||||
// with the JMP behavior which basically overrides the
|
||||
// entire "containers" list.
|
||||
func expectedResultJMP(imagename string) string {
|
||||
if imagename == "" {
|
||||
return `apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOMEENV
|
||||
value: SOMEVALUE
|
||||
name: nginx
|
||||
`
|
||||
}
|
||||
return fmt.Sprintf(`apiVersion: apps/v1
|
||||
kind: myCRD
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
some-label: some-value
|
||||
spec:
|
||||
containers:
|
||||
- image: %s
|
||||
name: nginx
|
||||
`, imagename)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user