Files
kustomize/api/krusty/replacementtransformer_test.go
zepard 7c04cbb237 Add regex support for Replacement selectors (#5863)
* feat: Add regex support for Replacement selectors

* Add new tests for regex support

* Earlier exit with rejectAny, and fix linting

* Add example Use cases using regex
2025-08-12 11:13:08 -07:00

1005 lines
18 KiB
Go

// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// end to end tests to demonstrate functionality of ReplacementTransformer
func TestReplacementsField(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK(".", `
resources:
- resource.yaml
replacements:
- source:
kind: Deployment
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.1.image
`)
th.WriteF("resource.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
`)
expected := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: foobar:1
name: replaced-with-digest
- image: foobar:1
name: postgresdb
`
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, expected)
}
func TestReplacementsFieldWithPath(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK(".", `
resources:
- resource.yaml
replacements:
- path: replacement.yaml
`)
th.WriteF("replacement.yaml", `
source:
kind: Deployment
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.1.image
`)
th.WriteF("resource.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
`)
expected := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: foobar:1
name: replaced-with-digest
- image: foobar:1
name: postgresdb
`
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, expected)
}
func TestReplacementsFieldWithPathMultiple(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK(".", `
resources:
- resource.yaml
replacements:
- path: replacement.yaml
`)
th.WriteF("replacement.yaml", `
- source:
kind: Deployment
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.1.image
- source:
kind: Deployment
fieldPath: spec.template.spec.containers.0.name
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.1.name
`)
th.WriteF("resource.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
`)
expected := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: foobar:1
name: replaced-with-digest
- image: foobar:1
name: replaced-with-digest
`
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, expected)
}
func TestReplacementTransformerWithDiamondShape(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("base/deployments.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sourceA
spec:
template:
spec:
containers:
- image: nginx:newtagA
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sourceB
spec:
template:
spec:
containers:
- image: nginx:newtagB
name: nginx
`)
th.WriteK("base", `
resources:
- deployments.yaml
`)
th.WriteK("a", `
namePrefix: a-
resources:
- ../base
replacements:
- path: replacement.yaml
`)
th.WriteF("a/replacement.yaml", `
source:
name: a-sourceA
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
name: a-deploy
fieldPaths:
- spec.template.spec.containers.[name=nginx].image
`)
th.WriteK("b", `
namePrefix: b-
resources:
- ../base
replacements:
- path: replacement.yaml
`)
th.WriteF("b/replacement.yaml", `
source:
name: b-sourceB
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
name: b-deploy
fieldPaths:
- spec.template.spec.containers.[name=nginx].image
`)
th.WriteK(".", `
resources:
- a
- b
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: a-deploy
spec:
template:
spec:
containers:
- image: nginx:newtagA
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: a-sourceA
spec:
template:
spec:
containers:
- image: nginx:newtagA
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: a-sourceB
spec:
template:
spec:
containers:
- image: nginx:newtagB
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-deploy
spec:
template:
spec:
containers:
- image: nginx:newtagB
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-sourceA
spec:
template:
spec:
containers:
- image: nginx:newtagA
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-sourceB
spec:
template:
spec:
containers:
- image: nginx:newtagB
name: nginx
`)
}
func TestReplacementTransformerWithOriginalName(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("base/deployments.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: target
spec:
template:
spec:
containers:
- image: nginx:oldtag
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: source
spec:
template:
spec:
containers:
- image: nginx:newtag
name: nginx
`)
th.WriteK("base", `
resources:
- deployments.yaml
`)
th.WriteK("overlay", `
namePrefix: prefix1-
resources:
- ../base
`)
th.WriteK(".", `
namePrefix: prefix2-
resources:
- overlay
replacements:
- path: replacement.yaml
`)
th.WriteF("replacement.yaml", `
source:
name: source
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
name: prefix1-target
fieldPaths:
- spec.template.spec.containers.[name=nginx].image
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: prefix2-prefix1-target
spec:
template:
spec:
containers:
- image: nginx:newtag
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prefix2-prefix1-source
spec:
template:
spec:
containers:
- image: nginx:newtag
name: nginx
`)
}
// TODO: Address namePrefix in overlay not applying to replacement targets
// The property `data.blue-name` should end up being `overlay-blue` instead of `blue`
// https://github.com/kubernetes-sigs/kustomize/issues/4034
func TestReplacementTransformerWithNamePrefixOverlay(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("base", `
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- name: blue
- name: red
replacements:
- source:
kind: ConfigMap
name: blue
fieldPath: metadata.name
targets:
- select:
name: red
fieldPaths:
- data.blue-name
options:
create: true
`)
th.WriteK(".", `
namePrefix: overlay-
resources:
- base
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: overlay-blue
---
apiVersion: v1
data:
blue-name: blue
kind: ConfigMap
metadata:
name: overlay-red
`)
}
// TODO: Address namespace in overlay not applying to replacement targets
// The property `data.blue-namespace` should end up being `overlay-namespace` instead of `base-namespace`
// https://github.com/kubernetes-sigs/kustomize/issues/4034
func TestReplacementTransformerWithNamespaceOverlay(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("base", `
namespace: base-namespace
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- name: blue
- name: red
replacements:
- source:
kind: ConfigMap
name: blue
fieldPath: metadata.namespace
targets:
- select:
name: red
fieldPaths:
- data.blue-namespace
options:
create: true
`)
th.WriteK(".", `
namespace: overlay-namespace
resources:
- base
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: blue
namespace: overlay-namespace
---
apiVersion: v1
data:
blue-namespace: base-namespace
kind: ConfigMap
metadata:
name: red
namespace: overlay-namespace
`)
}
// TODO: Address configMapGenerator suffix not applying to replacement targets
// The property `data.blue-name` should end up being `blue-6ct58987ht` instead of `blue`
// https://github.com/kubernetes-sigs/kustomize/issues/4034
func TestReplacementTransformerWithConfigMapGenerator(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK(".", `
configMapGenerator:
- name: blue
- name: red
replacements:
- source:
kind: ConfigMap
name: blue
fieldPath: metadata.name
targets:
- select:
name: red
fieldPaths:
- data.blue-name
options:
create: true
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: blue-6ct58987ht
---
apiVersion: v1
data:
blue-name: blue
kind: ConfigMap
metadata:
name: red-dc6gc5btkc
`)
}
func TestReplacementTransformerWithSuffixTransformerAndReject(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("base/app.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: original-name
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
`)
th.WriteK("base", `
resources:
- app.yaml
`)
th.WriteK("overlay", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
nameSuffix: -dev
resources:
- ../base
configMapGenerator:
- name: app-config
literals:
- name=something-else
replacements:
- source:
kind: ConfigMap
name: app-config
fieldPath: data.name
targets:
- fieldPaths:
- spec.template.spec.containers.0.name
select:
kind: Deployment
reject:
- name: original-name
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: original-name-dev
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
---
apiVersion: v1
data:
name: something-else
kind: ConfigMap
metadata:
name: app-config-dev-97544dk6t8
`)
}
// regex selector: append in annotation by visitor name
func TestReplacementTransformerAppendToAnnotationUsingRegex(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("base/app1.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: d1
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
`)
th.WriteF("base/app2.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: d2
spec:
template:
spec:
containers:
- image: app2:1.0
name: app
`)
th.WriteF("base/cm1.yaml", `
apiVersion: apps/v1
kind: ConfigMap
metadata:
name: cm1
`)
th.WriteF("base/cm2.yaml", `
apiVersion: apps/v1
kind: ConfigMap
metadata:
name: cm2
`)
th.WriteF("base/pg1.yaml", `
apiVersion: apps/v1
kind: postgresql
metadata:
name: pg1
`)
th.WriteK("base", `
resources:
- app1.yaml
- app2.yaml
- cm1.yaml
- cm2.yaml
- pg1.yaml
replacements:
- source:
kind: ConfigMap
name: cm1
targets:
- reject:
- kind: ConfigMap
name: c.1
select:
kind: Deployment|ConfigMap|postgresql
fieldPaths:
- metadata.annotations.visitedby
options:
index: -1
delimiter: ","
create: true
- source:
kind: ConfigMap
name: cm2
targets:
- reject:
- kind: ConfigMap
name: .*2
select:
kind: Deployment|ConfigMap|postgresql
fieldPaths:
- metadata.annotations.visitedby
options:
index: -1
delimiter: ","
create: true
`)
m := th.Run("base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
visitedby: cm2,cm1,
name: d1
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
visitedby: cm2,cm1,
name: d2
spec:
template:
spec:
containers:
- image: app2:1.0
name: app
---
apiVersion: apps/v1
kind: ConfigMap
metadata:
annotations:
visitedby: cm2,
name: cm1
---
apiVersion: apps/v1
kind: ConfigMap
metadata:
annotations:
visitedby: cm1,
name: cm2
---
apiVersion: apps/v1
kind: postgresql
metadata:
annotations:
visitedby: cm2,cm1,
name: pg1
`)
}
// selector regex: construct service url
func TestReplacementTransformerServiceNamespaceUrlUsingRegex(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("base/d1.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: d1
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
env:
- name: APP1_SERVICE
value: "d1.app1"
`)
th.WriteF("base/d2.yaml", `
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: d2
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
env:
- name: APP1_SERVICE
value: "d2.app1"
`)
th.WriteF("base/sts1.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sts1
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
env:
- name: APP1_SERVICE
value: "app1"
`)
th.WriteF("base/cm1.yaml", `
apiVersion: apps/v1
kind: ConfigMap
metadata:
name: cm1
data:
APP1_SERVICE_PORT: "8080"
`)
th.WriteF("base/svc1.yaml", `
apiVersion: v1
kind: Service
metadata:
name: svc1
namespace: svc1-namespace
spec:
selector:
app.kubernetes.io/name: app1
ports:
- protocol: TCP
port: 80
targetPort: 9376
`)
th.WriteK("base", `
resources:
- d1.yaml
- d2.yaml
- sts1.yaml
- cm1.yaml
- svc1.yaml
replacements:
- source:
kind: Service
name: svc1
fieldPath: metadata.namespace
targets:
- select:
kind: Deployment|.*Set
fieldPaths:
- spec.template.spec.containers.*.env.[name=APP1_SERVICE].value
options:
index: 99
delimiter: "."
- source:
kind: ConfigMap
name: cm1
fieldPath: data.APP1_SERVICE_PORT
targets:
- select:
kind: Deployment|.*Set
fieldPaths:
- spec.template.spec.containers.*.env.[name=APP1_SERVICE].value
options:
index: 99
delimiter: ":"
`)
m := th.Run("base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: d1
spec:
template:
spec:
containers:
- env:
- name: APP1_SERVICE
value: d1.app1.svc1-namespace:8080
image: app1:1.0
name: app
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: d2
spec:
template:
spec:
containers:
- env:
- name: APP1_SERVICE
value: d2.app1.svc1-namespace:8080
image: app1:1.0
name: app
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sts1
spec:
template:
spec:
containers:
- env:
- name: APP1_SERVICE
value: app1.svc1-namespace:8080
image: app1:1.0
name: app
---
apiVersion: apps/v1
data:
APP1_SERVICE_PORT: "8080"
kind: ConfigMap
metadata:
name: cm1
---
apiVersion: v1
kind: Service
metadata:
name: svc1
namespace: svc1-namespace
spec:
ports:
- port: 80
protocol: TCP
targetPort: 9376
selector:
app.kubernetes.io/name: app1
`)
}
func TestReplacementTransformerWithSuffixTransformerAndRejectUsingRegex(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("base/app.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: original-name
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
`)
th.WriteK("base", `
resources:
- app.yaml
`)
th.WriteK("overlay", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
nameSuffix: -dev
namePrefix: pre-
resources:
- ../base
configMapGenerator:
- name: app-config
literals:
- name=something-else
replacements:
- source:
kind: ConfigMap
name: app-config
fieldPath: data.name
targets:
- reject:
- name: .*original.*
select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.0.name
- select:
kind: ConfigMap
name: app-config
fieldPaths:
- data.name-copy
options:
create: true
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: pre-original-name-dev
spec:
template:
spec:
containers:
- image: app1:1.0
name: app
---
apiVersion: v1
data:
name: something-else
name-copy: something-else
kind: ConfigMap
metadata:
name: pre-app-config-dev-7266b7f2m9
`)
}