Matt McEuen 2e7224a7bf RT: add error handling for missing target docs
The ReplacementTransformer already checks for the error condition where
a replacement source doesn't match any of the input documents.
This change adds similar error handling for missing replacement
target documents, and tweaks the naming/messages to make it clear
which situation is encountered.

This will make it easier for manifest engineers to catch typos
in their RT rules, or documents that have failed to be kustomized in
or authored.

Change-Id: I0efbcfbc2952afae05e48b9ff8e0b3c67df7e7df
2020-05-11 17:45:34 -05:00

925 lines
17 KiB
Go

// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1_test
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
replv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/replacement/v1alpha1"
plugtypes "opendev.org/airship/airshipctl/pkg/document/plugin/types"
)
func samplePlugin(t *testing.T) plugtypes.Plugin {
plugin, err := replv1alpha1.New(nil, []byte(`
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
value: nginx:newtag
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx-latest].image`))
require.NoError(t, err)
return plugin
}
func TestMalformedConfig(t *testing.T) {
_, err := replv1alpha1.New(nil, []byte("--"))
assert.Error(t, err)
}
func TestMalformedInput(t *testing.T) {
plugin := samplePlugin(t)
err := plugin.Run(strings.NewReader("--"), &bytes.Buffer{})
assert.Error(t, err)
}
func TestDuplicatedResources(t *testing.T) {
plugin := samplePlugin(t)
err := plugin.Run(strings.NewReader(`
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- name: myapp-container
image: busybox
`), &bytes.Buffer{})
assert.Error(t, err)
}
func TestReplacementTransformer(t *testing.T) {
testCases := []struct {
cfg string
in string
expectedOut string
expectedErr string
}{
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
value: nginx:newtag
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx.latest].image
- source:
value: postgres:latest
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers.3.image
`,
in: `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx.latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`,
expectedOut: `apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:newtag
name: nginx.latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:latest
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod
fieldref: spec.containers
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy3
`,
expectedOut: `apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy3
spec:
template:
spec:
containers:
- image: busybox
name: myapp-container
`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: ConfigMap
name: cm
fieldref: data.HOSTNAME
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[image=debian].args.0
- spec.template.spec.containers[name=busybox].args.1
- source:
objref:
kind: ConfigMap
name: cm
fieldref: data.PORT
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[image=debian].args.1
- spec.template.spec.containers[name=busybox].args.2`,
in: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
labels:
foo: bar
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: command-demo-container
image: debian
command: ["printenv"]
args:
- HOSTNAME
- PORT
- name: busybox
image: busybox:latest
args:
- echo
- HOSTNAME
- PORT
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
data:
HOSTNAME: example.com
PORT: 8080`,
expectedOut: `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
foo: bar
name: deploy
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- args:
- example.com
- 8080
command:
- printenv
image: debian
name: command-demo-container
- args:
- echo
- example.com
- 8080
image: busybox:latest
name: busybox
---
apiVersion: v1
data:
HOSTNAME: example.com
PORT: 8080
kind: ConfigMap
metadata:
name: cm
`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
value: regexedtag
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx-latest].image%TAG%
- source:
value: postgres:latest
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers.3.image`,
in: `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:TAG
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine`,
expectedOut: `apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:regexedtag
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:latest
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Pod
name: pod2
fieldrefs:
- spec.non.existent.field`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedOut: `apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
containers:
- image: busybox
name: myapp-container
non:
existent:
field: pod1
`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod
fieldref: spec.containers[0]
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=myapp-container]`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- name: repl
image: repl
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: busybox
name: myapp-container
`,
expectedOut: `apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- image: repl
name: repl
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: repl
name: repl
`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx-latest].image`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: "found more than one resources matching from " +
"[{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"name\":\"pod1\"}," +
"\"spec\":{\"containers\":[{\"image\":\"busybox\",\"name\":\"myapp-container\"" +
"}]}}{nsfx:false,beh:unspecified} {\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":" +
"{\"name\":\"pod2\"},\"spec\":{\"containers\":[{\"image\":\"busybox\",\"name\":\"myapp-container\"}]}}" +
"{nsfx:false,beh:unspecified}]",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
namespace: default
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx-latest].image`,
expectedErr: "failed to find any source resources identified by Kind:Pod Name:pod1 Namespace:default",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx-latest].image`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: "failed to find any target resources identified by Kind:Deployment",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Pod
name: pod2
fieldrefs:
- labels.somelabel.key1.subkey1`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
labels:
somelabel: 'some string value'
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: `"some string value" is not expected be a primitive type`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Pod
name: pod2
fieldrefs:
- labels.somelabel[subkey1=val1]`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
labels:
somelabel: 'some string value'
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: `"some string value" is not expected be a primitive type`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Pod
name: pod2
fieldrefs:
- spec[subkey1=val1]`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
labels:
somelabel: 'some string value'
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: "map[string]interface {}{\"containers\":[]interface " +
"{}{map[string]interface {}{\"image\":\"busybox\", \"name\":\"myapp-container\"}}} " +
"is not expected be a primitive type",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Pod
name: pod2
fieldrefs:
- spec.containers.10`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
labels:
somelabel: 'some string value'
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: "index 10 is out of bound",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Pod
name: pod2
fieldrefs:
- spec.containers.notInteger.name`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
apiVersion: v1
kind: Pod
labels:
somelabel: 'some string value'
metadata:
name: pod2
spec:
containers:
- name: myapp-container
image: busybox`,
expectedErr: `strconv.Atoi: parsing "notInteger": invalid syntax`,
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers%TAG%`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:TAG
name: nginx-latest`,
expectedErr: "pattern-based substitution can only be applied to string target fields",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
objref:
kind: Pod
name: pod1
target:
objref:
kind: Deployment
fieldrefs:
- spec.template.spec.containers[name=nginx-latest].image%TAG%`,
in: `
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: myapp-container
image: busybox
---
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:latest
name: nginx-latest`,
expectedErr: "pattern 'TAG' is defined in configuration but was not found in target value nginx:latest",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: notImportantHere
replacements:
- source:
value: 12345678
target:
objref:
kind: KubeadmControlPlane
fieldrefs:
- spec.kubeadmConfigSpec.files[path=konfigadm].content%{k8s-version}%
`,
in: `
kind: KubeadmControlPlane
metadata:
name: cluster-controlplane
spec:
infrastructureTemplate:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: Metal3MachineTemplate
name: $(cluster-name)
kubeadmConfigSpec:
files:
- content: |
kubernetes:
version: {k8s-version}
container_runtime:
type: docker
owner: root:root
path: konfigadm_bug_
permissions: "0640"
`,
expectedErr: "unable to find map key 'path' with the value 'konfigadm' in list under 'files' key",
},
}
for _, tc := range testCases {
plugin, err := replv1alpha1.New(nil, []byte(tc.cfg))
require.NoError(t, err)
buf := &bytes.Buffer{}
err = plugin.Run(strings.NewReader(tc.in), buf)
errString := ""
if err != nil {
errString = err.Error()
}
assert.Equal(t, tc.expectedErr, errString)
assert.Equal(t, tc.expectedOut, buf.String())
}
}