Configuration override does not work when resources include subdirectories
What happened?
Override configuration for transformers does not work when resources include “folders” (for example ./sub).
After resource accumulation (kusttarget.go#L201), if resources come from subdirectories, the resulting ResAccumulator contains a non-empty configuration.
Even if the configuration in the subdirectory is not explicitly overridden, it still holds a default (but non-empty) config, which causes broader FieldSpec entries not to be replaced by narrower ones during merge kusttarget.go#L210.
Example:
# configuration in root directory
namePrefix:
- path: metadata/name
kind: Deployment
# nameprefix fieldspecs from subdirectory with no configurations
- path: metadata/name
---
# nameprefix fieldpecs from root directory
- path: metadata/name
kind: Deployment
---
# result
- path: metadata/name
- path: metadata/name
kind: Deployment
What did you expect to happen?
Configuration overrides should be performed even if there are folders in resources.
How can we reproduce it (as minimally and precisely as possible)?
# ./folder/kustomization.yaml
resources:
- deployment.yaml
- secret.yaml
# ./folder/deployment.yaml
apiVersion: apps/v1
metadata:
name: deployment1
kind: Deployment
# ./folder/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: secret
# kustomization.yaml
namePrefix: foo-
resources:
- ./folder
configurations:
- cfg.yaml
# cfg.yaml
namePrefix:
- path: metadata/name
kind: Deployment
Test case like this
func TestConfigurationsOverrideDefaultSubfolder(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/merge-config-subfolder/sub", `
resources:
- deployment.yaml
- secret.yaml
`)
th.WriteF("/merge-config-subfolder/sub/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment1
`)
th.WriteF("/merge-config-subfolder/sub/secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
name: secret
`)
th.WriteK("/merge-config-subfolder", `
namePrefix: foo-
resources:
- sub
configurations:
- name-prefix-rules.yaml
`)
th.WriteF("/merge-config-subfolder/name-prefix-rules.yaml", `
namePrefix:
- path: metadata/name
kind: Deployment
`)
pvd := provider.NewDefaultDepProvider()
resFactory := pvd.GetResourceFactory()
name0 := "deployment1"
r0, err0 := resFactory.FromMapWithName(name0, map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "foo-deployment1",
},
})
if err0 != nil {
t.Fatalf("failed to get instance with given name %v: %v", name0, err0)
}
name1 := "secret1"
r1, err1 := resFactory.FromMapWithName(name1, map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "secret1",
},
})
if err1 != nil {
t.Fatalf("failed to get instance with given name %v: %v", name1, err1)
}
var resources = []*resource.Resource{r0, r1}
expected := resmap.New()
for _, r := range resources {
err := expected.Append(r)
require.NoError(t, err)
}
expected.RemoveBuildAnnotations()
expYaml, err := expected.AsYaml()
require.NoError(t, err)
kt := makeKustTargetWithRf(t, th.GetFSys(), "/merge-config-subfolder", pvd)
require.NoError(t, kt.Load())
actual, err := kt.MakeCustomizedResMap()
require.NoError(t, err)
actual.RemoveBuildAnnotations()
actYaml, err := actual.AsYaml()
require.NoError(t, err)
require.Equal(t, string(expYaml), string(actYaml))
}
Result:
Diff:
--- Expected
+++ Actual
@@ -3,3 +3,3 @@
metadata:
- name: foo-deployment1
+ name: foo-foo-deployment1
---
@@ -8,3 +8,3 @@
metadata:
- name: secret1
+ name: foo-secret
Because of this same bug, the prefix was added to deployment twice more.
Expected output
apiVersion: v1
kind: Secret
metadata:
name: secret
---
apiVersion: apps/v1
metadata:
name: foo-deployment1
kind: Deployment
Actual output
apiVersion: v1
kind: Secret
metadata:
name: foo-secret # <--- changed
---
apiVersion: apps/v1
metadata:
name: foo-foo-deployment1 # <-- prefix was added twice
kind: Deployment
Kustomize version
5.7.1
Operating system
MacOS
This issue is currently awaiting triage.
SIG CLI takes a lead on issue triage for this repo, but any Kubernetes member can accept issues by applying the triage/accepted label.
The triage/accepted label can be added by org members by writing /triage accepted in a comment.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.
I propose two solutions:
-
Add ResAccumulator.MergeConfigLeft — introduce a new function that merges configuration from an this ResAccumulator into a input one (kusttarget.go#L210)
Like thisra.tConfig = input.Merge(ra.tConfig) -
Update FieldSpec.MergeOne logic — modify the merging behavior (fieldspec.go#L81): when a similar FieldSpec already exists and is broader than the new one, replace it with the new (narrower) one instead of ignoring it.
I can make a MR
Override configuration for transformers does not work when resources include “folders” (for example ./sub).
As far as I know, configuration is expected to be appended to the built-in default not overridden, it is also applied to all resources not only folders. Although for this job we can use transformers field.
example of configuration with in-folder and sub-folder resources: (click to expand)
#! /bin/sh
R=root
[ ! -d $R ] || rm -r $R
mk() { mkdir -p ${1%/*} && echo "$2" >$1 ; }
mk $R/kustomization.yaml '
namePrefix: foo-
resources:
- ./folder
- ./secret0.yaml
- ./deployment0.yaml
configurations:
- cfg.yaml
'
mk $R/cfg.yaml '
namePrefix:
- path: metadata/name
kind: Deployment
'
mk $R/secret0.yaml '
apiVersion: v1
kind: Secret
metadata:
name: secret0
'
mk $R/deployment0.yaml '
apiVersion: apps/v1
metadata:
name: deployment0
kind: Deployment
'
mk $R/folder/kustomization.yaml '
resources:
- deployment1.yaml
- secret1.yaml
'
mk $R/folder/secret1.yaml '
apiVersion: v1
kind: Secret
metadata:
name: secret1
'
mk $R/folder/deployment1.yaml '
# ./folder/deployment.yaml
apiVersion: apps/v1
metadata:
name: deployment1
kind: Deployment
'
#---
cat <<EOF > expected.yaml
apiVersion: v1
kind: Secret
metadata:
name: foo-secret0
---
apiVersion: v1
kind: Secret
metadata:
name: foo-secret1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-foo-deployment0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-foo-deployment1
EOF
#---
kustomize build --stack-trace $R/ > result.yaml
printf "%-64s%s\n" expected.yaml result.yaml
printf "%130s\n" " " | tr ' ' '-'
diff -W130 -y expected.yaml result.yaml
example using a prefix transformer: (click to expand)
#! /bin/sh
R=root
[ ! -d $R ] || rm -r $R
mk() { mkdir -p ${1%/*} && echo "$2" >$1 ; }
mk $R/kustomization.yaml '
resources:
- ./folder
- ./secret0.yaml
- ./deployment0.yaml
transformers:
- trfr.yaml
'
mk $R/trfr.yaml '
apiVersion: builtin
kind: PrefixTransformer
metadata:
name: notImportantHere
prefix: baked-
fieldSpecs:
- path: metadata/name
kind: Deployment
'
mk $R/secret0.yaml '
apiVersion: v1
kind: Secret
metadata:
name: secret0
'
mk $R/deployment0.yaml '
apiVersion: apps/v1
metadata:
name: deployment0
kind: Deployment
'
mk $R/folder/kustomization.yaml '
resources:
- deployment1.yaml
- secret1.yaml
'
mk $R/folder/secret1.yaml '
apiVersion: v1
kind: Secret
metadata:
name: secret1
'
mk $R/folder/deployment1.yaml '
# ./folder/deployment.yaml
apiVersion: apps/v1
metadata:
name: deployment1
kind: Deployment
'
#---
cat <<EOF > expected.yaml
apiVersion: v1
kind: Secret
metadata:
name: secret0
---
apiVersion: v1
kind: Secret
metadata:
name: secret1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: baked-deployment0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: baked-deployment1
EOF
#---
kustomize build --stack-trace $R/ > result.yaml
printf "%-64s%s\n" expected.yaml result.yaml
printf "%130s\n" " " | tr ' ' '-'
diff -W130 -y expected.yaml result.yaml
Hello, @sda399!
not overridden
You're mistaken. Fieldspecs are sorted from more specific to less specific, and duplicates are removed on config merging. There's an issue confirming this and a merge request adding a test.
The problem I found was that the configuration was being overriden incorrectly when there was a subdirectory. Even in your first example, namePrefix was applied twice to deployment0 and secret0 due to the same bug.
thanks for this reference: https://github.com/kubernetes-sigs/kustomize/issues/4811 It certainly clarifies the use case you initially described.