mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Otherwise, it is incompatible with filters like fieldSpec.Filter that recurse through a tree of RNodes. In the case of the bug this commit fixes, the leaf RNode is a metadata.annotation value field, and appendCsvAnnotation is called immediately before updating that leaf's value. If appendCsvAnnotation replaces the entire metadata.annotation RNode on the resource, the leaf RNode that gets updated is no longer attached to the resource. Alternatively, `func (f Filter) setScalar` could be updated to change the value of the leaf RNode BEFORE calling appendCsvAnnotation. However, the modification of the entire metadata segment of the RNode in a function that nominally just edits an annotation feels like a side-effect prone to creating other difficult-to-debug situations, beyond this ordering dependency.
1557 lines
33 KiB
Go
1557 lines
33 KiB
Go
// Copyright 2020 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package resource_test
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
|
"sigs.k8s.io/kustomize/api/provider"
|
|
. "sigs.k8s.io/kustomize/api/resource"
|
|
"sigs.k8s.io/kustomize/api/types"
|
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
|
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
var factory = provider.NewDefaultDepProvider().GetResourceFactory()
|
|
|
|
var testConfigMap = factory.FromMap(
|
|
map[string]interface{}{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]interface{}{
|
|
"name": "winnie",
|
|
"namespace": "hundred-acre-wood",
|
|
},
|
|
})
|
|
|
|
//nolint:gosec
|
|
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
|
|
|
var testDeployment = factory.FromMap(
|
|
map[string]interface{}{
|
|
"apiVersion": "apps/v1",
|
|
"kind": "Deployment",
|
|
"metadata": map[string]interface{}{
|
|
"name": "pooh",
|
|
},
|
|
})
|
|
|
|
const deploymentAsString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
|
|
|
|
func TestAsYAML(t *testing.T) {
|
|
expected := `apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: pooh
|
|
`
|
|
yaml, err := testDeployment.AsYAML()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(yaml) != expected {
|
|
t.Fatalf("--- expected\n%s\n--- got\n%s\n", expected, string(yaml))
|
|
}
|
|
}
|
|
|
|
func TestResourceString(t *testing.T) {
|
|
tests := []struct {
|
|
in *Resource
|
|
s string
|
|
}{
|
|
{
|
|
in: testConfigMap,
|
|
s: configMapAsString,
|
|
},
|
|
{
|
|
in: testDeployment,
|
|
s: deploymentAsString,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
assert.Equal(t, test.in.String(), test.s)
|
|
}
|
|
}
|
|
|
|
func TestResourceId(t *testing.T) {
|
|
tests := []struct {
|
|
in *Resource
|
|
id resid.ResId
|
|
}{
|
|
{
|
|
in: testConfigMap,
|
|
id: resid.NewResIdWithNamespace(
|
|
resid.NewGvk("", "v1", "ConfigMap"),
|
|
"winnie", "hundred-acre-wood"),
|
|
},
|
|
{
|
|
in: testDeployment,
|
|
id: resid.NewResId(
|
|
resid.NewGvk("apps", "v1", "Deployment"), "pooh"),
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
if test.in.OrgId() != test.id {
|
|
t.Fatalf("Expected %v, but got %v\n", test.id, test.in.OrgId())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDeepCopy(t *testing.T) {
|
|
r := factory.FromMap(
|
|
map[string]interface{}{
|
|
"apiVersion": "apps/v1",
|
|
"kind": "Deployment",
|
|
"metadata": map[string]interface{}{
|
|
"name": "pooh",
|
|
},
|
|
})
|
|
r.AppendRefBy(resid.NewResId(resid.Gvk{Group: "somegroup", Kind: "MyKind"}, "random"))
|
|
|
|
var1 := types.Var{
|
|
Name: "SERVICE_ONE",
|
|
ObjRef: types.Target{
|
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
|
Name: "backendOne"},
|
|
}
|
|
r.AppendRefVarName(var1)
|
|
|
|
cr := r.DeepCopy()
|
|
if !reflect.DeepEqual(r, cr) {
|
|
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_2(t *testing.T) {
|
|
resource, err := factory.FromBytes([]byte(`
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
B: Y
|
|
`))
|
|
assert.NoError(t, err)
|
|
patch, err := factory.FromBytes([]byte(`
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
B:
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`))
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resource.ApplySmPatch(patch))
|
|
bytes, err := resource.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, `apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
A: X
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`, string(bytes))
|
|
}
|
|
|
|
func TestApplySmPatch_3(t *testing.T) {
|
|
resource, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`))
|
|
assert.NoError(t, err)
|
|
patch, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 999
|
|
`))
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resource.ApplySmPatch(patch))
|
|
bytes, err := resource.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 999
|
|
`, string(bytes))
|
|
}
|
|
|
|
func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
skip bool
|
|
patch string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
name: "Order should not change when patch has foo only",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: foo
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: foo
|
|
- name: bar
|
|
`,
|
|
},
|
|
{
|
|
name: "Order changes when patch has bar only",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: bar
|
|
`,
|
|
// This test records current behavior, but this behavior might be undesirable.
|
|
// If so, feel free to change the test to pass with some improved algorithm.
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: bar
|
|
- name: foo
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should not change and should include a new item at the beginning when patch has a new list item",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: baz
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: baz
|
|
- name: foo
|
|
- name: bar
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should not change when patch has foo and bar in same order",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: foo
|
|
- name: bar
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: foo
|
|
- name: bar
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should change when patch has foo and bar in different order",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: bar
|
|
- name: foo
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: bar
|
|
- name: foo
|
|
`,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
resource, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
spec:
|
|
initContainers:
|
|
- name: foo
|
|
- name: bar
|
|
`))
|
|
assert.NoError(t, err)
|
|
|
|
patch, err := factory.FromBytes([]byte(tc.patch))
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resource.ApplySmPatch(patch))
|
|
bytes, err := resource.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expectedOutput, string(bytes))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApplySmPatchShouldOutputPrimitiveListItemsInCorrectOrder(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
skip bool
|
|
patch string
|
|
expectedOutput string
|
|
}{
|
|
{
|
|
name: "Order should not change when patch has foo only",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
finalizers: ["foo"]
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
finalizers:
|
|
- foo
|
|
- bar
|
|
name: test
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should not change when patch has bar only",
|
|
skip: true, // TODO: This test should pass but fails currently. Fix the problem and unskip this test
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
finalizers: ["bar"]
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
finalizers:
|
|
- foo
|
|
- bar
|
|
name: test
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should not change and should include a new item at the beginning when patch has a new list item",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
finalizers: ["baz"]
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
finalizers:
|
|
- baz
|
|
- foo
|
|
- bar
|
|
name: test
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should not change when patch has foo and bar in same order",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
finalizers: ["foo", "bar"]
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
finalizers:
|
|
- foo
|
|
- bar
|
|
name: test
|
|
`,
|
|
},
|
|
{
|
|
name: "Order should change when patch has foo and bar in different order",
|
|
patch: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
finalizers: ["bar", "foo"]
|
|
`,
|
|
expectedOutput: `apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
finalizers:
|
|
- bar
|
|
- foo
|
|
name: test
|
|
`,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
resource, err := factory.FromBytes([]byte(`
|
|
kind: Pod
|
|
metadata:
|
|
name: test
|
|
finalizers: ["foo", "bar"]
|
|
`))
|
|
assert.NoError(t, err)
|
|
|
|
patch, err := factory.FromBytes([]byte(tc.patch))
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resource.ApplySmPatch(patch))
|
|
bytes, err := resource.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expectedOutput, string(bytes))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMergeDataMapFrom(t *testing.T) {
|
|
resource, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: BlahBlah
|
|
metadata:
|
|
name: clown
|
|
data:
|
|
fruit: pear
|
|
`))
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
patch, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Whatever
|
|
metadata:
|
|
name: spaceship
|
|
data:
|
|
spaceship: enterprise
|
|
`))
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
resource.MergeDataMapFrom(patch)
|
|
bytes, err := resource.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, `apiVersion: v1
|
|
data:
|
|
fruit: pear
|
|
spaceship: enterprise
|
|
kind: BlahBlah
|
|
metadata:
|
|
name: clown
|
|
`, string(bytes))
|
|
}
|
|
|
|
func TestApplySmPatch_SwapOrder(t *testing.T) {
|
|
s1 := `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
B:
|
|
C: Z
|
|
`
|
|
s2 := `
|
|
apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`
|
|
expected := `apiVersion: example.com/v1
|
|
kind: Foo
|
|
metadata:
|
|
name: my-foo
|
|
spec:
|
|
bar:
|
|
C: Z
|
|
D: W
|
|
baz:
|
|
hello: world
|
|
`
|
|
r1, err := factory.FromBytes([]byte(s1))
|
|
assert.NoError(t, err)
|
|
r2, err := factory.FromBytes([]byte(s2))
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, r1.ApplySmPatch(r2))
|
|
bytes, err := r1.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, expected, string(bytes))
|
|
|
|
r1, _ = factory.FromBytes([]byte(s1))
|
|
r2, _ = factory.FromBytes([]byte(s2))
|
|
assert.NoError(t, r2.ApplySmPatch(r1))
|
|
bytes, err = r2.AsYAML()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, expected, 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceStorePreviousId(t *testing.T) {
|
|
tests := map[string]struct {
|
|
input string
|
|
newName string
|
|
newNs string
|
|
expected string
|
|
}{
|
|
"default namespace, first previous name": {
|
|
input: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
name: oldName
|
|
`,
|
|
newName: "newName",
|
|
newNs: "",
|
|
expected: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret
|
|
internal.config.kubernetes.io/previousNames: oldName
|
|
internal.config.kubernetes.io/previousNamespaces: default
|
|
name: newName
|
|
`,
|
|
},
|
|
|
|
"default namespace, second previous name": {
|
|
input: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret
|
|
internal.config.kubernetes.io/previousNames: oldName
|
|
internal.config.kubernetes.io/previousNamespaces: default
|
|
name: oldName2
|
|
`,
|
|
newName: "newName",
|
|
newNs: "",
|
|
expected: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
|
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
|
internal.config.kubernetes.io/previousNamespaces: default,default
|
|
name: newName
|
|
`,
|
|
},
|
|
|
|
"non-default namespace": {
|
|
input: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret
|
|
internal.config.kubernetes.io/previousNames: oldName
|
|
internal.config.kubernetes.io/previousNamespaces: default
|
|
name: oldName2
|
|
namespace: oldNamespace
|
|
`,
|
|
newName: "newName",
|
|
newNs: "newNamespace",
|
|
expected: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
|
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
|
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
|
|
name: newName
|
|
namespace: newNamespace
|
|
`,
|
|
},
|
|
}
|
|
factory := provider.NewDefaultDepProvider().GetResourceFactory()
|
|
for i := range tests {
|
|
test := tests[i]
|
|
t.Run(i, func(t *testing.T) {
|
|
resources, err := factory.SliceFromBytes([]byte(test.input))
|
|
if !assert.NoError(t, err) || len(resources) == 0 {
|
|
t.FailNow()
|
|
}
|
|
r := resources[0]
|
|
r.StorePreviousId()
|
|
r.SetName(test.newName)
|
|
if test.newNs != "" {
|
|
r.SetNamespace(test.newNs)
|
|
}
|
|
bytes, err := r.AsYAML()
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
assert.Equal(t, test.expected, string(bytes))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResource_PrevIds(t *testing.T) {
|
|
tests := map[string]struct {
|
|
input string
|
|
expected []resid.ResId
|
|
}{
|
|
"no previous IDs": {
|
|
input: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
name: name
|
|
`,
|
|
expected: nil,
|
|
},
|
|
|
|
"one previous ID": {
|
|
input: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret
|
|
internal.config.kubernetes.io/previousNames: oldName
|
|
internal.config.kubernetes.io/previousNamespaces: default
|
|
name: newName
|
|
`,
|
|
expected: []resid.ResId{
|
|
{
|
|
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
|
|
Name: "oldName",
|
|
Namespace: resid.DefaultNamespace,
|
|
},
|
|
},
|
|
},
|
|
|
|
"two ids": {
|
|
input: `apiVersion: apps/v1
|
|
kind: Secret
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/previousKinds: Secret,Secret
|
|
internal.config.kubernetes.io/previousNames: oldName,oldName2
|
|
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
|
|
name: newName
|
|
namespace: newNamespace
|
|
`,
|
|
expected: []resid.ResId{
|
|
{
|
|
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
|
|
Name: "oldName",
|
|
Namespace: resid.DefaultNamespace,
|
|
},
|
|
{
|
|
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
|
|
Name: "oldName2",
|
|
Namespace: "oldNamespace",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
factory := provider.NewDefaultDepProvider().GetResourceFactory()
|
|
for i := range tests {
|
|
test := tests[i]
|
|
t.Run(i, func(t *testing.T) {
|
|
resources, err := factory.SliceFromBytes([]byte(test.input))
|
|
if !assert.NoError(t, err) || len(resources) == 0 {
|
|
t.FailNow()
|
|
}
|
|
r := resources[0]
|
|
assert.Equal(t, test.expected, r.PrevIds())
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
func TestSameEndingSubarray(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
a []string
|
|
b []string
|
|
expected bool
|
|
}{
|
|
"both nil": {
|
|
expected: true,
|
|
},
|
|
"one nil": {
|
|
b: []string{},
|
|
expected: true,
|
|
},
|
|
"both empty": {
|
|
a: []string{},
|
|
b: []string{},
|
|
expected: true,
|
|
},
|
|
"no1": {
|
|
a: []string{"a"},
|
|
b: []string{},
|
|
expected: false,
|
|
},
|
|
"no2": {
|
|
a: []string{"b", "a"},
|
|
b: []string{"b"},
|
|
expected: false,
|
|
},
|
|
"yes1": {
|
|
a: []string{"a", "b"},
|
|
b: []string{"b"},
|
|
expected: true,
|
|
},
|
|
"yes2": {
|
|
a: []string{"a", "b", "c"},
|
|
b: []string{"b", "c"},
|
|
expected: true,
|
|
},
|
|
"yes3": {
|
|
a: []string{"a", "b", "c", "d", "e", "f"},
|
|
b: []string{"f"},
|
|
expected: true,
|
|
},
|
|
}
|
|
for n := range testCases {
|
|
tc := testCases[n]
|
|
t.Run(n, func(t *testing.T) {
|
|
assert.Equal(t, tc.expected, utils.SameEndingSubSlice(tc.a, tc.b))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetGvk(t *testing.T) {
|
|
r, err := factory.FromBytes([]byte(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`))
|
|
assert.NoError(t, err)
|
|
|
|
gvk := r.GetGvk()
|
|
expected := "apps"
|
|
actual := gvk.Group
|
|
if expected != actual {
|
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
|
}
|
|
expected = "v1"
|
|
actual = gvk.Version
|
|
if expected != actual {
|
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
|
}
|
|
expected = "Deployment"
|
|
actual = gvk.Kind
|
|
if expected != actual {
|
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
|
}
|
|
}
|
|
func TestSetGvk(t *testing.T) {
|
|
r, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`))
|
|
assert.NoError(t, err)
|
|
r.SetGvk(resid.GvkFromString("knd.ver.grp"))
|
|
gvk := r.GetGvk()
|
|
if expected, actual := "grp", gvk.Group; expected != actual {
|
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
|
}
|
|
if expected, actual := "ver", gvk.Version; expected != actual {
|
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
|
}
|
|
if expected, actual := "knd", gvk.Kind; expected != actual {
|
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestRefBy(t *testing.T) {
|
|
r, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`))
|
|
assert.NoError(t, err)
|
|
r.AppendRefBy(resid.FromString("knd1.ver1.gr1/name1.ns1"))
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
annotations:
|
|
internal.config.kubernetes.io/refBy: 'knd1.ver1.gr1/name1.ns1'
|
|
spec:
|
|
numReplicas: 1
|
|
`, r.RNode.MustString())
|
|
assert.Equal(t, r.GetRefBy(), []resid.ResId{resid.FromString("knd1.ver1.gr1/name1.ns1")})
|
|
|
|
r.AppendRefBy(resid.FromString("knd2.ver2.gr2/name2.ns2"))
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
annotations:
|
|
internal.config.kubernetes.io/refBy: 'knd1.ver1.gr1/name1.ns1,knd2.ver2.gr2/name2.ns2'
|
|
spec:
|
|
numReplicas: 1
|
|
`, r.RNode.MustString())
|
|
assert.Equal(t, []resid.ResId{
|
|
resid.FromString("knd1.ver1.gr1/name1.ns1"),
|
|
resid.FromString("knd2.ver2.gr2/name2.ns2"),
|
|
}, r.GetRefBy())
|
|
}
|
|
|
|
func TestOrigin(t *testing.T) {
|
|
r, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`))
|
|
assert.NoError(t, err)
|
|
origin := &Origin{
|
|
Path: "deployment.yaml",
|
|
Repo: "github.com/myrepo",
|
|
Ref: "master",
|
|
}
|
|
assert.NoError(t, r.SetOrigin(origin))
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
annotations:
|
|
config.kubernetes.io/origin: |
|
|
path: deployment.yaml
|
|
repo: github.com/myrepo
|
|
ref: master
|
|
spec:
|
|
numReplicas: 1
|
|
`, r.MustString())
|
|
or, err := r.GetOrigin()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, origin, or)
|
|
}
|
|
|
|
func TestTransformations(t *testing.T) {
|
|
r, err := factory.FromBytes([]byte(`
|
|
apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`))
|
|
assert.NoError(t, err)
|
|
origin1 := &Origin{
|
|
Repo: "github.com/myrepo",
|
|
Ref: "master",
|
|
ConfiguredIn: "config.yaml",
|
|
ConfiguredBy: kyaml.ResourceIdentifier{
|
|
TypeMeta: kyaml.TypeMeta{
|
|
APIVersion: "builtin",
|
|
Kind: "Generator",
|
|
},
|
|
NameMeta: kyaml.NameMeta{
|
|
Name: "my-name",
|
|
Namespace: "my-namespace",
|
|
},
|
|
},
|
|
}
|
|
origin2 := &Origin{
|
|
ConfiguredIn: "../base/config.yaml",
|
|
ConfiguredBy: kyaml.ResourceIdentifier{
|
|
TypeMeta: kyaml.TypeMeta{
|
|
APIVersion: "builtin",
|
|
Kind: "Generator",
|
|
},
|
|
NameMeta: kyaml.NameMeta{
|
|
Name: "my-name",
|
|
Namespace: "my-namespace",
|
|
},
|
|
},
|
|
}
|
|
assert.NoError(t, r.AddTransformation(origin1))
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
annotations:
|
|
alpha.config.kubernetes.io/transformations: |
|
|
- repo: github.com/myrepo
|
|
ref: master
|
|
configuredIn: config.yaml
|
|
configuredBy:
|
|
apiVersion: builtin
|
|
kind: Generator
|
|
name: my-name
|
|
namespace: my-namespace
|
|
spec:
|
|
numReplicas: 1
|
|
`, r.MustString())
|
|
assert.NoError(t, r.AddTransformation(origin2))
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
annotations:
|
|
alpha.config.kubernetes.io/transformations: |
|
|
- repo: github.com/myrepo
|
|
ref: master
|
|
configuredIn: config.yaml
|
|
configuredBy:
|
|
apiVersion: builtin
|
|
kind: Generator
|
|
name: my-name
|
|
namespace: my-namespace
|
|
- configuredIn: ../base/config.yaml
|
|
configuredBy:
|
|
apiVersion: builtin
|
|
kind: Generator
|
|
name: my-name
|
|
namespace: my-namespace
|
|
spec:
|
|
numReplicas: 1
|
|
`, r.MustString())
|
|
transformations, err := r.GetTransformations()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, Transformations{origin1, origin2}, transformations)
|
|
assert.NoError(t, r.ClearTransformations())
|
|
assert.Equal(t, `apiVersion: v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: clown
|
|
spec:
|
|
numReplicas: 1
|
|
`, r.MustString())
|
|
}
|