mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Assert keeps going after failure, but require immediately fails the tests, making it easier to find the output related to the test failure, rather than having to comb through a bunch of subsequent assertion failures. For equality tests, we may or may not want to continue, but for error checks we almost always want to immediately fail the test. Exceptions can be changed as-needed.
1210 lines
26 KiB
Go
1210 lines
26 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
|
)
|
|
|
|
func errorContains(err error, possibilities ...string) bool {
|
|
for _, x := range possibilities {
|
|
if strings.Contains(err.Error(), x) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
const (
|
|
target = `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
replica: 2
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx
|
|
`
|
|
targetWithNamespace = `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
namespace: namespace1
|
|
kind: Deployment
|
|
spec:
|
|
replica: 2
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx
|
|
`
|
|
targetNoschema = `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
B: Y
|
|
`
|
|
)
|
|
|
|
func TestPatchStrategicMergeTransformerMissingFile(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
_, err := th.RunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`, target)
|
|
if assert.Error(t, err) && !errorContains(err,
|
|
"'/patch.yaml' doesn't exist",
|
|
"cannot unmarshal string") {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBadPatchStrategicMergeTransformer(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
_, err := th.RunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
patches: 'thisIsNotAPatch'
|
|
`, target)
|
|
if assert.Error(t, err) && !errorContains(err,
|
|
"cannot unmarshal string into Go value of type map[string]interface {}",
|
|
"fails configuration: missing Resource metadata") {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBothEmptyPatchStrategicMergeTransformer(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
_, err := th.RunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
`, target)
|
|
if assert.Error(t, err) && !errorContains(
|
|
err, "empty file path and empty patch content") {
|
|
t.Fatalf("unexpected err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerFromFiles(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("/patch.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
template:
|
|
metadata:
|
|
labels:
|
|
new-label: new-value
|
|
replica: 3
|
|
`)
|
|
|
|
th.RunTransformerAndCheckResult(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`,
|
|
target, `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myDeploy
|
|
spec:
|
|
replica: 3
|
|
template:
|
|
metadata:
|
|
labels:
|
|
new-label: new-value
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- image: nginx
|
|
name: nginx
|
|
`)
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerWithInlineJSON(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.RunTransformerAndCheckResult(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
patches: '{"apiVersion": "apps/v1", "metadata": {"name": "myDeploy"}, "kind": "Deployment", "spec": {"replica": 3}}'
|
|
`,
|
|
target, `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myDeploy
|
|
spec:
|
|
replica: 3
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- image: nginx
|
|
name: nginx
|
|
`)
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerWithInlineYAML(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.RunTransformerAndCheckResult(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
patches: |-
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
replica: 3
|
|
---
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:latest
|
|
`,
|
|
target, `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myDeploy
|
|
spec:
|
|
replica: 3
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- image: nginx:latest
|
|
name: nginx
|
|
`)
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerMultiplePatches(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("patch1.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:latest
|
|
env:
|
|
- name: SOMEENV
|
|
value: BAR
|
|
`)
|
|
|
|
th.WriteF("patch2.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
env:
|
|
- name: ANOTHERENV
|
|
value: HELLO
|
|
- name: busybox
|
|
image: busybox
|
|
`)
|
|
|
|
th.RunTransformerAndCheckResult(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch1.yaml
|
|
- patch2.yaml
|
|
`,
|
|
target, `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myDeploy
|
|
spec:
|
|
replica: 2
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- env:
|
|
- name: ANOTHERENV
|
|
value: HELLO
|
|
- name: SOMEENV
|
|
value: BAR
|
|
image: nginx:latest
|
|
name: nginx
|
|
- image: busybox
|
|
name: busybox
|
|
`)
|
|
}
|
|
|
|
func TestStrategicMergeTransformerWrongNamespace(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
th.WriteF("patch.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
namespace: namespace2
|
|
kind: Deployment
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:latest
|
|
env:
|
|
- name: SOMEENV
|
|
value: BAR
|
|
`)
|
|
_, err := th.RunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`, targetWithNamespace)
|
|
if assert.Error(t, err) && !errorContains(
|
|
err, "failed to find unique target for patch") {
|
|
t.Fatalf("expected error to contain %q but get %v", "failed to find target for patch", err)
|
|
}
|
|
}
|
|
|
|
// issue #2734 -- https://github.com/kubernetes-sigs/kustomize/issues/2734
|
|
// kyaml cleans up things some folks prefer it didnt
|
|
// 1. [] -- see initContainers and imagePullSecrets
|
|
// 2. {} -- see emptyDir
|
|
// 3. null -- see creationTimestamp
|
|
|
|
const anUncleanDeploymentResource = `apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: sas-crunchy-data-postgres-operator
|
|
spec:
|
|
replicas: 1
|
|
template:
|
|
metadata:
|
|
creationTimestamp: null
|
|
spec:
|
|
serviceAccountName: postgres-operator
|
|
containers:
|
|
- name: apiserver
|
|
image: sas-crunchy-data-operator-api-server
|
|
imagePullPolicy: IfNotPresent
|
|
ports:
|
|
- containerPort: 8443
|
|
envFrom: []
|
|
volumeMounts:
|
|
- name: security-ssh
|
|
mountPath: /security-ssh
|
|
- mountPath: /tmp
|
|
name: tmp
|
|
imagePullSecrets: []
|
|
initContainers: []
|
|
volumes:
|
|
- emptyDir: {}
|
|
name: security-ssh
|
|
- emptyDir: {}
|
|
name: tmp
|
|
`
|
|
|
|
// This is the preffered result (and what we get with kustomize 3.7.0)
|
|
const expectedCleanedDeployment = `apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
labels:
|
|
workload.sas.com/class: stateless
|
|
name: sas-crunchy-data-postgres-operator
|
|
spec:
|
|
replicas: 1
|
|
template:
|
|
metadata:
|
|
creationTimestamp: null
|
|
labels:
|
|
workload.sas.com/class: stateless
|
|
spec:
|
|
containers:
|
|
- envFrom: []
|
|
image: sas-crunchy-data-operator-api-server
|
|
imagePullPolicy: IfNotPresent
|
|
name: apiserver
|
|
ports:
|
|
- containerPort: 8443
|
|
volumeMounts:
|
|
- mountPath: /security-ssh
|
|
name: security-ssh
|
|
- mountPath: /tmp
|
|
name: tmp
|
|
imagePullSecrets: []
|
|
initContainers: []
|
|
serviceAccountName: postgres-operator
|
|
tolerations:
|
|
- effect: NoSchedule
|
|
key: workload.sas.com/class
|
|
operator: Equal
|
|
value: stateful
|
|
volumes:
|
|
- emptyDir: {}
|
|
name: security-ssh
|
|
- emptyDir: {}
|
|
name: tmp
|
|
`
|
|
|
|
func TestPatchStrategicMergeTransformerCleanupItems(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("/patch.yaml", `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: sas-crunchy-data-postgres-operator
|
|
labels:
|
|
workload.sas.com/class: stateless
|
|
spec:
|
|
template:
|
|
metadata:
|
|
labels:
|
|
workload.sas.com/class: stateless
|
|
spec:
|
|
tolerations:
|
|
- effect: NoSchedule
|
|
key: workload.sas.com/class
|
|
operator: Equal
|
|
value: stateful
|
|
`)
|
|
|
|
th.RunTransformerAndCheckResult(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`,
|
|
anUncleanDeploymentResource,
|
|
expectedCleanedDeployment) // prefer expectedCleanedDeployment
|
|
}
|
|
|
|
func TestStrategicMergeTransformerNoSchema(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("patch.yaml", `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
B:
|
|
C: Z
|
|
`)
|
|
th.RunTransformerAndCheckResult(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`,
|
|
targetNoschema,
|
|
`
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
C: Z
|
|
`)
|
|
}
|
|
|
|
func TestStrategicMergeTransformerNoSchemaMultiPatches(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
// This patch wants to delete "B".
|
|
th.WriteF("patch1.yaml", `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
B:
|
|
C: Z
|
|
`)
|
|
th.WriteF("patch2.yaml", `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`)
|
|
resMap, err := th.RunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch1.yaml
|
|
- patch2.yaml
|
|
`, `apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
B: Y
|
|
`)
|
|
require.NoError(t, err)
|
|
th.AssertActualEqualsExpectedNoIdAnnotations(
|
|
resMap, `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`)
|
|
resMap, err = th.RunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch2.yaml
|
|
`, `apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
B: Y
|
|
`)
|
|
require.NoError(t, err)
|
|
th.AssertActualEqualsExpectedNoIdAnnotations(
|
|
// This time only patch2 is applied.
|
|
resMap, `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
B: "Y"
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`)
|
|
}
|
|
|
|
// simple utility function to add an namespace in a resource
|
|
// used as base, patch or expected result. Simply looks
|
|
// for specs: in order to add namespace: xxxx before this line
|
|
func addNamespace(namespace string, base string) string {
|
|
res := strings.Replace(base,
|
|
"\nspec:\n",
|
|
"\n namespace: "+namespace+"\nspec:\n",
|
|
1)
|
|
return res
|
|
}
|
|
|
|
// compareExpectedError compares the expectedError and the actualError return by GetFieldValue
|
|
func compareExpectedError(t *testing.T, name string, err error, errorMsg string) {
|
|
if assert.Error(t, err, name) && !errorContains(err, errorMsg) {
|
|
t.Fatalf("%q; - expected error: \"%s\", got error: \"%v\"",
|
|
name, errorMsg, err.Error())
|
|
}
|
|
}
|
|
|
|
const Deployment string = "Deployment"
|
|
const MyCRD string = "MyCRD"
|
|
|
|
// 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 {
|
|
res := `
|
|
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`
|
|
|
|
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 second container in the container list
|
|
// Note that for SMP/WithSchema merge, the name:nginx entry
|
|
// is mandatory
|
|
func addContainerAndEnvPatch(kind string) string {
|
|
|
|
res := `
|
|
apiVersion: apps/v1
|
|
kind: %s
|
|
metadata:
|
|
name: deploy1
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
env:
|
|
- name: ANOTHERENV
|
|
value: ANOTHERVALUE
|
|
- name: anothercontainer
|
|
image: anotherimage`
|
|
|
|
return fmt.Sprintf(res, 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 {
|
|
|
|
res := `
|
|
apiVersion: apps/v1
|
|
kind: %s
|
|
metadata:
|
|
name: deploy1
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: %s`
|
|
|
|
return fmt.Sprintf(res, kind, newImage)
|
|
}
|
|
|
|
// utility method building the expected output of a SMP
|
|
func expectedResultSMP() string {
|
|
|
|
return `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
|
|
name: nginx
|
|
`
|
|
}
|
|
|
|
// 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 {
|
|
|
|
res := `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
|
|
`
|
|
|
|
if imagename == "" {
|
|
return res
|
|
}
|
|
|
|
res = `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
|
|
`
|
|
|
|
return fmt.Sprintf(res, imagename)
|
|
}
|
|
|
|
// 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 {
|
|
|
|
res := `apiVersion: apps/v1
|
|
kind: %s
|
|
metadata:
|
|
name: deploy1
|
|
spec:
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
some-label: some-value
|
|
spec:
|
|
containers:
|
|
- env:
|
|
- name: ANOTHERENV
|
|
value: ANOTHERVALUE
|
|
- name: SOMEENV
|
|
value: SOMEVALUE
|
|
image: nginx:latest
|
|
name: nginx
|
|
- image: anotherimage
|
|
name: anothercontainer
|
|
`
|
|
|
|
reversedres := `apiVersion: apps/v1
|
|
kind: %s
|
|
metadata:
|
|
name: deploy1
|
|
spec:
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
some-label: some-value
|
|
spec:
|
|
containers:
|
|
- env:
|
|
- name: SOMEENV
|
|
value: SOMEVALUE
|
|
- name: ANOTHERENV
|
|
value: ANOTHERVALUE
|
|
image: nginx:latest
|
|
name: nginx
|
|
- image: anotherimage
|
|
name: anothercontainer
|
|
`
|
|
|
|
if reversed {
|
|
return fmt.Sprintf(reversedres, kind)
|
|
}
|
|
|
|
return fmt.Sprintf(res, kind)
|
|
}
|
|
|
|
func toConfig(patches ...string) string {
|
|
config := `
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
`
|
|
for idx := range patches {
|
|
config = fmt.Sprintf("%s\n- ./patch%d.yaml", config, idx)
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
// TestSinglePatch validates the single patch use cases
|
|
// regarless of the schema availibility, which in turns
|
|
// relies on StrategicMergePatch or simple JSON Patch.
|
|
func TestSinglePatch(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
base string
|
|
patch string
|
|
expected string
|
|
errorExpected bool
|
|
errorMsg string
|
|
}{
|
|
{
|
|
name: "withschema",
|
|
base: baseResource(Deployment),
|
|
patch: addLabelAndEnvPatch(Deployment),
|
|
errorExpected: false,
|
|
expected: expectedResultSMP(),
|
|
},
|
|
{
|
|
name: "noschema",
|
|
base: baseResource(MyCRD),
|
|
patch: addLabelAndEnvPatch(MyCRD),
|
|
errorExpected: false,
|
|
expected: expectedResultJMP(""),
|
|
},
|
|
}
|
|
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
for _, test := range tests {
|
|
th.ResetLoaderRoot(fmt.Sprintf("/%s", test.name))
|
|
th.WriteF(fmt.Sprintf("/%s/patch%d.yaml", test.name, 0), test.patch)
|
|
if test.errorExpected {
|
|
_, err := th.RunTransformer(toConfig(test.patch), test.base)
|
|
compareExpectedError(t, test.name, err, test.errorMsg)
|
|
} else {
|
|
th.RunTransformerAndCheckResult(
|
|
toConfig(test.patch), test.base, test.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
type testRecord struct {
|
|
base string
|
|
patch []string
|
|
expected string
|
|
errorExpected bool
|
|
errorMsg string
|
|
}
|
|
|
|
// TestMultiplePatches checks that the patches are applied
|
|
// properly, that the same result is obtained,
|
|
// regardless of the order of the patches and regardless
|
|
// of the schema availability (SMP vs JSON)
|
|
func TestMultiplePatches(t *testing.T) {
|
|
tests := map[string]testRecord{
|
|
"withschema-label-image-container": {
|
|
base: baseResource(Deployment),
|
|
patch: []string{
|
|
addLabelAndEnvPatch(Deployment),
|
|
changeImagePatch(Deployment, "nginx:latest"),
|
|
addContainerAndEnvPatch(Deployment),
|
|
},
|
|
expected: expectedResultMultiPatch(Deployment, false),
|
|
},
|
|
"withschema-image-container-label": {
|
|
base: baseResource(Deployment),
|
|
patch: []string{
|
|
changeImagePatch(Deployment, "nginx:latest"),
|
|
addContainerAndEnvPatch(Deployment),
|
|
addLabelAndEnvPatch(Deployment),
|
|
},
|
|
expected: expectedResultMultiPatch(Deployment, true),
|
|
},
|
|
"withschema-container-label-image": {
|
|
base: baseResource(Deployment),
|
|
patch: []string{
|
|
addContainerAndEnvPatch(Deployment),
|
|
addLabelAndEnvPatch(Deployment),
|
|
changeImagePatch(Deployment, "nginx:latest"),
|
|
},
|
|
expected: expectedResultMultiPatch(Deployment, true),
|
|
},
|
|
"noschema-1.7.9-label-latest": {
|
|
base: baseResource(MyCRD),
|
|
patch: []string{
|
|
changeImagePatch(MyCRD, "nginx:1.7.9"),
|
|
addLabelAndEnvPatch(MyCRD),
|
|
changeImagePatch(MyCRD, "nginx:latest"),
|
|
},
|
|
errorExpected: false,
|
|
// Theses patches aren't commutable (you get a different result
|
|
// if they are ordered differently). This is allowed without error.
|
|
expected: expectedResultJMP("nginx:latest"),
|
|
},
|
|
"noschema-latest-label-1.7.9": {
|
|
base: baseResource(MyCRD),
|
|
patch: []string{
|
|
changeImagePatch(MyCRD, "nginx:latest"),
|
|
addLabelAndEnvPatch(MyCRD),
|
|
changeImagePatch(MyCRD, "nginx:1.7.9"),
|
|
},
|
|
errorExpected: false,
|
|
// Theses patches aren't commutable (you get a different result
|
|
// if they are ordered differently). This is allowed without error.
|
|
expected: expectedResultJMP("nginx:1.7.9"),
|
|
},
|
|
}
|
|
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
th.ResetLoaderRoot(fmt.Sprintf("/%s", name))
|
|
for idx, patch := range test.patch {
|
|
th.WriteF(fmt.Sprintf("/%s/patch%d.yaml", name, idx), patch)
|
|
}
|
|
if test.errorExpected {
|
|
_, err := th.RunTransformer(toConfig(test.patch...), test.base)
|
|
compareExpectedError(t, name, err, test.errorMsg)
|
|
} else {
|
|
th.RunTransformerAndCheckResult(
|
|
toConfig(test.patch...), test.base, test.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMultipleNamespaces before the same patch
|
|
// on two objects have the same name but in a different namespaces
|
|
func TestMultipleNamespaces(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
base []string
|
|
patch []string
|
|
expected []string
|
|
errorExpected bool
|
|
errorMsg string
|
|
}{
|
|
{
|
|
name: "withschema-ns1-ns2",
|
|
base: []string{
|
|
addNamespace("ns1", baseResource(Deployment)),
|
|
addNamespace("ns2", baseResource(Deployment)),
|
|
},
|
|
patch: []string{
|
|
addNamespace("ns1", addLabelAndEnvPatch(Deployment)),
|
|
addNamespace("ns2", addLabelAndEnvPatch(Deployment)),
|
|
},
|
|
errorExpected: false,
|
|
expected: []string{
|
|
addNamespace("ns1", expectedResultSMP()),
|
|
addNamespace("ns2", expectedResultSMP()),
|
|
},
|
|
},
|
|
{
|
|
name: "noschema-ns1-ns2",
|
|
base: []string{
|
|
addNamespace("ns1", baseResource(MyCRD)),
|
|
addNamespace("ns2", baseResource(MyCRD)),
|
|
},
|
|
patch: []string{
|
|
addNamespace("ns1", addLabelAndEnvPatch(MyCRD)),
|
|
addNamespace("ns2", addLabelAndEnvPatch(MyCRD)),
|
|
},
|
|
errorExpected: false,
|
|
expected: []string{
|
|
addNamespace("ns1", expectedResultJMP("")),
|
|
addNamespace("ns2", expectedResultJMP("")),
|
|
},
|
|
},
|
|
{
|
|
name: "withschema-ns1-ns2",
|
|
base: []string{addNamespace("ns1", baseResource(Deployment))},
|
|
patch: []string{addNamespace("ns2", changeImagePatch(Deployment, "nginx:1.7.9"))},
|
|
errorExpected: true,
|
|
errorMsg: "failed to find unique target for patch",
|
|
},
|
|
{
|
|
name: "withschema-nil-ns2",
|
|
base: []string{baseResource(Deployment)},
|
|
patch: []string{addNamespace("ns2", changeImagePatch(Deployment, "nginx:1.7.9"))},
|
|
errorExpected: true,
|
|
errorMsg: "failed to find unique target for patch",
|
|
},
|
|
{
|
|
name: "withschema-ns1-nil",
|
|
base: []string{addNamespace("ns1", baseResource(Deployment))},
|
|
patch: []string{changeImagePatch(Deployment, "nginx:1.7.9")},
|
|
errorExpected: true,
|
|
errorMsg: "failed to find unique target for patch",
|
|
},
|
|
{
|
|
name: "noschema-ns1-ns2",
|
|
base: []string{addNamespace("ns1", baseResource(MyCRD))},
|
|
patch: []string{addNamespace("ns2", changeImagePatch(MyCRD, "nginx:1.7.9"))},
|
|
errorExpected: true,
|
|
errorMsg: "failed to find unique target for patch",
|
|
},
|
|
{
|
|
name: "noschema-nil-ns2",
|
|
base: []string{baseResource(MyCRD)},
|
|
patch: []string{addNamespace("ns2", changeImagePatch(MyCRD, "nginx:1.7.9"))},
|
|
errorExpected: true,
|
|
errorMsg: "failed to find unique target for patch",
|
|
},
|
|
{
|
|
name: "noschema-ns1-nil",
|
|
base: []string{addNamespace("ns1", baseResource(MyCRD))},
|
|
patch: []string{changeImagePatch(MyCRD, "nginx:1.7.9")},
|
|
errorExpected: true,
|
|
errorMsg: "failed to find unique target for patch",
|
|
},
|
|
}
|
|
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
for _, test := range tests {
|
|
th.ResetLoaderRoot(fmt.Sprintf("/%s", test.name))
|
|
for idx, patch := range test.patch {
|
|
th.WriteF(fmt.Sprintf("/%s/patch%d.yaml", test.name, idx), patch)
|
|
}
|
|
|
|
if test.errorExpected {
|
|
_, err := th.RunTransformer(
|
|
toConfig(test.patch...), strings.Join(test.base, "\n---\n"))
|
|
compareExpectedError(t, test.name, err, test.errorMsg)
|
|
} else {
|
|
th.RunTransformerAndCheckResult(
|
|
toConfig(test.patch...),
|
|
strings.Join(test.base, "\n---\n"),
|
|
strings.Join(test.expected, "---\n"))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerPatchDelete1(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("patch.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
replica: 2
|
|
template:
|
|
$patch: delete
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx
|
|
`)
|
|
|
|
rm := th.LoadAndRunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`, target)
|
|
|
|
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myDeploy
|
|
spec:
|
|
replica: 2
|
|
`)
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerPatchDelete2(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("patch.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
spec:
|
|
$patch: delete
|
|
replica: 2
|
|
template:
|
|
metadata:
|
|
labels:
|
|
old-label: old-value
|
|
spec:
|
|
containers:
|
|
- name: nginx
|
|
image: nginx
|
|
`)
|
|
|
|
rm := th.LoadAndRunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`, target)
|
|
|
|
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: myDeploy
|
|
`)
|
|
}
|
|
|
|
func TestPatchStrategicMergeTransformerPatchDelete3(t *testing.T) {
|
|
th := kusttest_test.MakeEnhancedHarness(t).
|
|
PrepBuiltin("PatchStrategicMergeTransformer")
|
|
defer th.Reset()
|
|
|
|
th.WriteF("patch.yaml", `
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: myDeploy
|
|
kind: Deployment
|
|
$patch: delete
|
|
`)
|
|
|
|
rm := th.LoadAndRunTransformer(`
|
|
apiVersion: builtin
|
|
kind: PatchStrategicMergeTransformer
|
|
metadata:
|
|
name: notImportantHere
|
|
paths:
|
|
- patch.yaml
|
|
`, target)
|
|
|
|
th.AssertActualEqualsExpected(rm, ``)
|
|
}
|