Files
kustomize/kyaml/kio/kioutil/kioutil_test.go
Karl Isenberg 43868688d5 Use require for Error and NoError
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.
2024-03-20 13:19:18 -07:00

707 lines
14 KiB
Go

// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kioutil_test
import (
"bytes"
"math/rand"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestSortNodes_moreThan10(t *testing.T) {
input := `
a: b
---
c: d
---
e: f
---
g: h
---
i: j
---
k: l
---
m: n
---
o: p
---
q: r
---
s: t
---
u: v
---
w: x
---
y: z
`
actual := &bytes.Buffer{}
rw := kio.ByteReadWriter{Reader: bytes.NewBufferString(input), Writer: actual}
nodes, err := rw.Read()
if !assert.NoError(t, err) {
t.Fail()
}
// randomize the list
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(nodes), func(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] })
// sort them back into their original order
if !assert.NoError(t, kioutil.SortNodes(nodes)) {
t.Fail()
}
// check the sorted values
expected := strings.Split(input, "---")
for i := range nodes {
a := strings.TrimSpace(nodes[i].MustString())
b := strings.TrimSpace(expected[i])
if !assert.Contains(t, a, b) {
t.Fail()
}
}
if !assert.NoError(t, rw.Write(nodes)) {
t.Fail()
}
assert.Equal(t, strings.TrimSpace(input), strings.TrimSpace(actual.String()))
}
func TestDefaultPathAnnotation(t *testing.T) {
var tests = []struct {
dir string
input string // input
expected string // expected result
name string
}{
{
`foo`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
config.kubernetes.io/path: 'foo/b/bar_a.yaml'
`, `with namespace`},
{
`foo`,
`apiVersion: v1
kind: Bar
metadata:
name: a
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
annotations:
internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
config.kubernetes.io/path: 'foo/bar_a.yaml'
`, `without namespace`},
{
``,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'b/bar_a.yaml'
config.kubernetes.io/path: 'b/bar_a.yaml'
`, `without dir`},
{
``,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'a/b.yaml'
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'a/b.yaml'
`, `skip`},
}
for _, s := range tests {
n := yaml.MustParse(s.input)
err := kioutil.DefaultPathAnnotation(s.dir, []*yaml.RNode{n})
if !assert.NoError(t, err, s.name) {
t.FailNow()
}
if !assert.Equal(t, s.expected, n.MustString(), s.name) {
t.FailNow()
}
}
}
func TestDefaultPathAndIndexAnnotation(t *testing.T) {
var tests = []struct {
dir string
input string // input
expected string // expected result
name string
}{
{
`foo`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
config.kubernetes.io/path: 'foo/b/bar_a.yaml'
internal.config.kubernetes.io/index: '0'
config.kubernetes.io/index: '0'
`, `with namespace`},
{
`foo`,
`apiVersion: v1
kind: Bar
metadata:
name: a
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
annotations:
internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
config.kubernetes.io/path: 'foo/bar_a.yaml'
internal.config.kubernetes.io/index: '0'
config.kubernetes.io/index: '0'
`, `without namespace`},
{
``,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'b/bar_a.yaml'
config.kubernetes.io/path: 'b/bar_a.yaml'
internal.config.kubernetes.io/index: '0'
config.kubernetes.io/index: '0'
`, `without dir`},
{
``,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
config.kubernetes.io/index: '5'
`,
`apiVersion: v1
kind: Bar
metadata:
name: a
namespace: b
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
config.kubernetes.io/index: '5'
`, `skip`},
}
for _, s := range tests {
out := &bytes.Buffer{}
r := kio.ByteReadWriter{
Reader: bytes.NewBufferString(s.input),
Writer: out,
KeepReaderAnnotations: true,
OmitReaderAnnotations: true,
}
n, err := r.Read()
if !assert.NoError(t, err, s.name) {
t.FailNow()
}
if !assert.NoError(t, kioutil.DefaultPathAndIndexAnnotation(s.dir, n), s.name) {
t.FailNow()
}
if !assert.NoError(t, r.Write(n), s.name) {
t.FailNow()
}
if !assert.Equal(t, s.expected, out.String(), s.name) {
t.FailNow()
}
}
}
func TestCreatePathAnnotationValue(t *testing.T) {
var tests = []struct {
dir string
meta yaml.ResourceMeta // input
expected string // expected result
name string
}{
{
`dir`,
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{
Name: "bar", Namespace: "baz",
},
},
},
`dir/baz/foo_bar.yaml`, `with namespace`,
},
{
``,
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{
Name: "bar", Namespace: "baz",
},
},
},
`baz/foo_bar.yaml`, `without dir`,
},
{
`dir`,
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{Name: "bar"},
},
},
`dir/foo_bar.yaml`, `without namespace`,
},
{
``,
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{Name: "bar"},
},
},
`foo_bar.yaml`, `without namespace or dir`,
},
{
``,
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{},
},
`foo_.yaml`, `without namespace, dir or name`,
},
{
``,
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
},
ObjectMeta: yaml.ObjectMeta{},
},
`_.yaml`, `without any`,
},
}
for _, s := range tests {
p := kioutil.CreatePathAnnotationValue(s.dir, s.meta)
if !assert.Equal(t, s.expected, p, s.name) {
t.FailNow()
}
}
}
func TestCopyLegacyAnnotations(t *testing.T) {
var tests = []struct {
input string
expected string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/index: '5'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/index: '5'
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/index: '5'
`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'c/d.yaml'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
config.kubernetes.io/path: 'c/d.yaml'
`,
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
require.NoError(t, err)
require.NoError(t, kioutil.CopyLegacyAnnotations(nodes[0]))
assert.Equal(t, tc.expected, nodes[0].MustString())
}
}
func TestCopyInternalAnnotations(t *testing.T) {
var tests = []struct {
input string
exclusions []kioutil.AnnotationKey
expected string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: src
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar'
---
apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
`,
expected: `apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar'
`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: src
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar-src'
---
apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-dst'
`,
exclusions: []kioutil.AnnotationKey{
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
},
expected: `apiVersion: v1
kind: Foo
metadata:
name: dst
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-src'
`,
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
require.NoError(t, err)
require.NoError(t, kioutil.CopyInternalAnnotations(nodes[0], nodes[1], tc.exclusions...))
assert.Equal(t, tc.expected, nodes[1].MustString())
}
}
func TestConfirmInternalAnnotationUnchanged(t *testing.T) {
var tests = []struct {
input string
exclusions []kioutil.AnnotationKey
expectedErr string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
`,
expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
`,
exclusions: []kioutil.AnnotationKey{
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
},
expectedErr: ``,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar-1'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-2'
`,
exclusions: []kioutil.AnnotationKey{
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
},
expectedErr: `internal annotations differ: internal.config.kubernetes.io/foo`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar-1'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/path: 'c/d.yaml'
internal.config.kubernetes.io/index: '10'
internal.config.kubernetes.io/foo: 'bar-1'
`,
expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo-1
annotations:
internal.config.kubernetes.io/a: 'b'
internal.config.kubernetes.io/c: 'd'
---
apiVersion: v1
kind: Foo
metadata:
name: foo-2
annotations:
internal.config.kubernetes.io/e: 'f'
internal.config.kubernetes.io/g: 'h'
`,
expectedErr: `internal annotations differ: ` +
`internal.config.kubernetes.io/a, internal.config.kubernetes.io/c, ` +
`internal.config.kubernetes.io/e, internal.config.kubernetes.io/g`,
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
require.NoError(t, err)
err = kioutil.ConfirmInternalAnnotationUnchanged(nodes[0], nodes[1], tc.exclusions...)
if tc.expectedErr == "" {
require.NoError(t, err)
} else {
if err == nil {
t.Fatalf("expected error: %s\n", tc.expectedErr)
}
assert.Equal(t, tc.expectedErr, err.Error())
}
}
}
func TestGetInternalAnnotations(t *testing.T) {
var tests = []struct {
input string
expected map[string]string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foobar
annotations:
foo: bar
internal.config.kubernetes.io/path: 'a/b.yaml'
internal.config.kubernetes.io/index: '5'
internal.config.kubernetes.io/foo: 'bar'
`,
expected: map[string]string{
"internal.config.kubernetes.io/path": "a/b.yaml",
"internal.config.kubernetes.io/index": "5",
"internal.config.kubernetes.io/foo": "bar",
},
},
}
for _, tc := range tests {
rw := kio.ByteReadWriter{
Reader: bytes.NewBufferString(tc.input),
OmitReaderAnnotations: true,
}
nodes, err := rw.Read()
require.NoError(t, err)
assert.Equal(t, tc.expected, kioutil.GetInternalAnnotations(nodes[0]))
}
}