Dmitry Ukov 47d8d8c886 Replacement transformer improvement
* Add ability to to replace whole list element filtered by key and
  value
* Raise an error in if user has specified wrong list filer in JSON path
* Enables support for a dot character in a value used in list filtering

Closes: #191
Closes: #192

Change-Id: I889550ee88d5738a1fb808ddef6478c3731b1561
2020-04-28 17:12:29 +04:00

898 lines
16 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 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: 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())
}
}