mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 16:42:51 +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.
870 lines
15 KiB
Go
870 lines
15 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package kio_test
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
func TestByteReadWriter(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
err string
|
|
input string
|
|
expectedOutput string
|
|
instance kio.ByteReadWriter
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "round_trip",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "function_config",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
functionConfig:
|
|
a: b # something
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
functionConfig:
|
|
a: b # something
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "results",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
results:
|
|
a: b # something
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
results:
|
|
a: b # something
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "drop_invalid_resource_list_field",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
foo:
|
|
a: b # something
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "list",
|
|
input: `
|
|
apiVersion: v1
|
|
kind: List
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: v1
|
|
kind: List
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "multiple_documents",
|
|
input: `
|
|
kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
---
|
|
kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
expectedOutput: `
|
|
kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
---
|
|
kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
},
|
|
|
|
{
|
|
name: "keep_annotations",
|
|
input: `
|
|
kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
---
|
|
kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
expectedOutput: `
|
|
kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
metadata:
|
|
annotations:
|
|
config.kubernetes.io/index: '0'
|
|
internal.config.kubernetes.io/index: '0'
|
|
---
|
|
kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
metadata:
|
|
annotations:
|
|
config.kubernetes.io/index: '1'
|
|
internal.config.kubernetes.io/index: '1'
|
|
`,
|
|
instance: kio.ByteReadWriter{KeepReaderAnnotations: true},
|
|
},
|
|
|
|
{
|
|
name: "manual_override_wrap",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
functionConfig:
|
|
a: b # something
|
|
`,
|
|
expectedOutput: `
|
|
kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
---
|
|
kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
`,
|
|
instance: kio.ByteReadWriter{NoWrap: true},
|
|
},
|
|
|
|
{
|
|
name: "manual_override_function_config",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
functionConfig:
|
|
a: b # something
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
spec:
|
|
replicas: 1
|
|
- kind: Service
|
|
spec:
|
|
selectors:
|
|
foo: bar
|
|
functionConfig:
|
|
c: d
|
|
`,
|
|
instance: kio.ByteReadWriter{FunctionConfig: yaml.MustParse(`c: d`)},
|
|
},
|
|
{
|
|
name: "anchors_not_inflated",
|
|
input: `
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: foo
|
|
data:
|
|
color: &color-used blue
|
|
feeling: *color-used
|
|
`,
|
|
// If YAML anchors were automagically inflated,
|
|
// the expectedOutput would be something like
|
|
//
|
|
// kind: ConfigMap
|
|
// metadata:
|
|
// name: foo
|
|
// data:
|
|
// color: blue
|
|
// feeling: blue
|
|
expectedOutput: `
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: foo
|
|
data:
|
|
color: &color-used blue
|
|
feeling: *color-used
|
|
`,
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var in, out bytes.Buffer
|
|
in.WriteString(tc.input)
|
|
w := tc.instance
|
|
w.Writer = &out
|
|
w.Reader = &in
|
|
|
|
nodes, err := w.Read()
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
|
|
err = w.Write(nodes)
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
|
|
if tc.err != "" {
|
|
if !assert.EqualError(t, err, tc.err) {
|
|
t.FailNow()
|
|
}
|
|
return
|
|
}
|
|
|
|
if !assert.Equal(t,
|
|
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) {
|
|
t.FailNow()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestByteReadWriter_RetainSeqIndent(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
err string
|
|
input string
|
|
expectedOutput string
|
|
instance kio.ByteReadWriter
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "round_trip with 2 space seq indent",
|
|
input: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "round_trip with 0 space seq indent",
|
|
input: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "round_trip with different indentations",
|
|
input: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
- baz
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
- baz
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "round_trip with mixed indentations in same resource, wide wins as it is first",
|
|
input: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
env:
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
env:
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "round_trip with mixed indentations in same resource, compact wins as it is first",
|
|
input: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
env:
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
env:
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "unwrap ResourceList with annotations",
|
|
input: `
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/seqindent: "compact"
|
|
spec:
|
|
- foo
|
|
- bar
|
|
- kind: Service
|
|
metadata:
|
|
annotations:
|
|
internal.config.kubernetes.io/seqindent: "wide"
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
---
|
|
kind: Service
|
|
spec:
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "round_trip with mixed indentations in same resource, wide wins as it is first",
|
|
input: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
env:
|
|
- foo
|
|
- bar
|
|
- baz
|
|
`,
|
|
expectedOutput: `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
- foo
|
|
- bar
|
|
env:
|
|
- foo
|
|
- bar
|
|
- baz
|
|
`,
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var in, out bytes.Buffer
|
|
in.WriteString(tc.input)
|
|
w := tc.instance
|
|
w.Writer = &out
|
|
w.Reader = &in
|
|
w.PreserveSeqIndent = true
|
|
|
|
nodes, err := w.Read()
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
|
|
w.WrappingKind = ""
|
|
err = w.Write(nodes)
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
|
|
if tc.err != "" {
|
|
if !assert.EqualError(t, err, tc.err) {
|
|
t.FailNow()
|
|
}
|
|
return
|
|
}
|
|
|
|
if !assert.Equal(t,
|
|
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) {
|
|
t.FailNow()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestByteReadWriter_WrapBareSeqNode(t *testing.T) {
|
|
type testCase struct {
|
|
name string
|
|
readerErr string
|
|
writerErr string
|
|
input string
|
|
wrapBareSeqNode bool
|
|
expectedOutput string
|
|
instance kio.ByteReadWriter
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
name: "round_trip bare seq node simple",
|
|
wrapBareSeqNode: true,
|
|
input: `
|
|
- foo
|
|
- bar
|
|
`,
|
|
expectedOutput: `
|
|
- foo
|
|
- bar
|
|
`,
|
|
},
|
|
{
|
|
name: "round_trip bare seq node",
|
|
wrapBareSeqNode: true,
|
|
input: `# Use the old CRD because of the quantity validation issue:
|
|
# https://github.com/kubeflow/kubeflow/issues/5722
|
|
- op: replace
|
|
path: /spec
|
|
value:
|
|
group: kubeflow.org
|
|
names:
|
|
kind: Notebook
|
|
plural: notebooks
|
|
singular: notebook
|
|
scope: Namespaced
|
|
subresources:
|
|
status: {}
|
|
versions:
|
|
- name: v1alpha1
|
|
served: true
|
|
storage: false
|
|
`,
|
|
expectedOutput: `# Use the old CRD because of the quantity validation issue:
|
|
# https://github.com/kubeflow/kubeflow/issues/5722
|
|
- op: replace
|
|
path: /spec
|
|
value:
|
|
group: kubeflow.org
|
|
names:
|
|
kind: Notebook
|
|
plural: notebooks
|
|
singular: notebook
|
|
scope: Namespaced
|
|
subresources:
|
|
status: {}
|
|
versions:
|
|
- name: v1alpha1
|
|
served: true
|
|
storage: false
|
|
`,
|
|
},
|
|
{
|
|
name: "error round_trip bare seq node simple",
|
|
wrapBareSeqNode: false,
|
|
input: `
|
|
- foo
|
|
- bar
|
|
`,
|
|
readerErr: "wrong node kind: expected MappingNode but got SequenceNode",
|
|
},
|
|
{
|
|
name: "error round_trip bare seq node",
|
|
wrapBareSeqNode: false,
|
|
input: `# Use the old CRD because of the quantity validation issue:
|
|
# https://github.com/kubeflow/kubeflow/issues/5722
|
|
- op: replace
|
|
path: /spec
|
|
value:
|
|
group: kubeflow.org
|
|
names:
|
|
kind: Notebook
|
|
plural: notebooks
|
|
singular: notebook
|
|
scope: Namespaced
|
|
subresources:
|
|
status: {}
|
|
versions:
|
|
- name: v1alpha1
|
|
served: true
|
|
storage: false
|
|
`,
|
|
readerErr: "wrong node kind: expected MappingNode but got SequenceNode",
|
|
},
|
|
{
|
|
name: "round_trip bare seq node json",
|
|
wrapBareSeqNode: true,
|
|
input: `[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--namespaced"}]`,
|
|
expectedOutput: `[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--namespaced"}]`,
|
|
},
|
|
{
|
|
name: "error round_trip invalid yaml node",
|
|
wrapBareSeqNode: false,
|
|
input: "I am not valid",
|
|
readerErr: "wrong node kind: expected MappingNode but got ScalarNode",
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var in, out bytes.Buffer
|
|
in.WriteString(tc.input)
|
|
w := tc.instance
|
|
w.Writer = &out
|
|
w.Reader = &in
|
|
w.PreserveSeqIndent = true
|
|
w.WrapBareSeqNode = tc.wrapBareSeqNode
|
|
|
|
nodes, err := w.Read()
|
|
if tc.readerErr != "" {
|
|
if !assert.Error(t, err) {
|
|
t.FailNow()
|
|
}
|
|
if !assert.Contains(t, err.Error(), tc.readerErr) {
|
|
t.FailNow()
|
|
}
|
|
return
|
|
}
|
|
|
|
w.WrappingKind = ""
|
|
err = w.Write(nodes)
|
|
if !assert.NoError(t, err) {
|
|
t.FailNow()
|
|
}
|
|
|
|
if tc.writerErr != "" {
|
|
if !assert.Error(t, err) {
|
|
t.FailNow()
|
|
}
|
|
if !assert.Contains(t, err.Error(), tc.writerErr) {
|
|
t.FailNow()
|
|
}
|
|
return
|
|
}
|
|
|
|
if !assert.Equal(t,
|
|
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) {
|
|
t.FailNow()
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestByteReadWriter_ResourceListWrapping(t *testing.T) {
|
|
singleDeployment := `kind: Deployment
|
|
apiVersion: v1
|
|
metadata:
|
|
name: tester
|
|
namespace: default
|
|
spec:
|
|
replicas: 0`
|
|
resourceList := `apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
apiVersion: v1
|
|
metadata:
|
|
name: tester
|
|
namespace: default
|
|
spec:
|
|
replicas: 0`
|
|
resourceListWithError := `apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
apiVersion: v1
|
|
metadata:
|
|
name: tester
|
|
namespace: default
|
|
spec:
|
|
replicas: 0
|
|
results:
|
|
- message: some error
|
|
severity: error`
|
|
resourceListDifferentWrapper := strings.NewReplacer(
|
|
"kind: ResourceList", "kind: SomethingElse",
|
|
"apiVersion: config.kubernetes.io/v1", "apiVersion: fakeVersion",
|
|
).Replace(resourceList)
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
noWrap bool
|
|
wrapKind string
|
|
wrapAPIVersion string
|
|
input string
|
|
want string
|
|
}{
|
|
{
|
|
desc: "resource list",
|
|
input: resourceList,
|
|
want: resourceList,
|
|
},
|
|
{
|
|
desc: "individual resources",
|
|
input: singleDeployment,
|
|
want: singleDeployment,
|
|
},
|
|
{
|
|
desc: "no nested wrapping",
|
|
wrapKind: kio.ResourceListKind,
|
|
wrapAPIVersion: kio.ResourceListAPIVersion,
|
|
input: resourceList,
|
|
want: resourceList,
|
|
},
|
|
{
|
|
desc: "unwrap resource list",
|
|
noWrap: true,
|
|
input: resourceList,
|
|
want: singleDeployment,
|
|
},
|
|
{
|
|
desc: "wrap individual resources",
|
|
wrapKind: kio.ResourceListKind,
|
|
wrapAPIVersion: kio.ResourceListAPIVersion,
|
|
input: singleDeployment,
|
|
want: resourceList,
|
|
},
|
|
{
|
|
desc: "NoWrap has precedence",
|
|
noWrap: true,
|
|
wrapKind: kio.ResourceListKind,
|
|
wrapAPIVersion: kio.ResourceListAPIVersion,
|
|
input: singleDeployment,
|
|
want: singleDeployment,
|
|
},
|
|
{
|
|
desc: "honor specified wrapping kind",
|
|
wrapKind: "SomethingElse",
|
|
wrapAPIVersion: "fakeVersion",
|
|
input: resourceList,
|
|
want: resourceListDifferentWrapper,
|
|
},
|
|
{
|
|
desc: "passthrough results",
|
|
input: resourceListWithError,
|
|
want: resourceListWithError,
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
var got bytes.Buffer
|
|
rw := kio.ByteReadWriter{
|
|
Reader: strings.NewReader(tc.input),
|
|
Writer: &got,
|
|
NoWrap: tc.noWrap,
|
|
WrappingAPIVersion: tc.wrapAPIVersion,
|
|
WrappingKind: tc.wrapKind,
|
|
}
|
|
|
|
rnodes, err := rw.Read()
|
|
require.NoError(t, err)
|
|
|
|
err = rw.Write(rnodes)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tc.want, strings.TrimSpace(got.String()))
|
|
})
|
|
}
|
|
}
|