mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 09:24:23 +00:00
[fix 4124] Skip local resource until all transformations have completed.
Resources annotated as "local-config" are expected to be ignored. This skip local resource happens in "accumulateResources" which happens before any transformation operations. However, the local resource may be needed in transformations. Thus, this change removes the "drop local resource" logic from accumulateResources and removes these local resource after all transformation operations and var operations are done. Note: None of the existing ResMap functions can drop the resource slice easily: "Clear" will ruin the resource order, "AppendAll" only adds non-existing resource, "AbsorbAll" only add or modify but not delete. Thus, we introduce a new func "Intersection" for resourceAccumulator that specificaly removes the resource by ID and keep the original order.
This commit is contained in:
@@ -168,3 +168,23 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
|
|||||||
return ra.Transform(
|
return ra.Transform(
|
||||||
newNameReferenceTransformer(ra.tConfig.NameReference))
|
newNameReferenceTransformer(ra.tConfig.NameReference))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intersection drops the resources which "other" does not have.
|
||||||
|
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
|
||||||
|
for _, curId := range ra.resMap.AllIds() {
|
||||||
|
toDelete := true
|
||||||
|
for _, otherId := range other.AllIds() {
|
||||||
|
if otherId == curId {
|
||||||
|
toDelete = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if toDelete {
|
||||||
|
err := ra.resMap.Remove(curId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/builtins"
|
"sigs.k8s.io/kustomize/api/builtins"
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
"sigs.k8s.io/kustomize/api/internal/accumulator"
|
"sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
@@ -218,9 +219,26 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator, origin *r
|
|||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
err, "merging vars %v", kt.kustomization.Vars)
|
err, "merging vars %v", kt.kustomization.Vars)
|
||||||
}
|
}
|
||||||
|
err = kt.IgnoreLocal(ra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return ra, nil
|
return ra, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IgnoreLocal drops the local resource by checking the annotation "config.kubernetes.io/local-config".
|
||||||
|
func (kt *KustTarget) IgnoreLocal(ra *accumulator.ResAccumulator) error {
|
||||||
|
rf := kt.rFactory.RF()
|
||||||
|
if rf.IncludeLocalConfigs {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
remainRes, err := rf.DropLocalNodes(ra.ResMap().ToRNodeSlice())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ra.Intersection(kt.rFactory.FromResourceSlice(remainRes))
|
||||||
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) runGenerators(
|
func (kt *KustTarget) runGenerators(
|
||||||
ra *accumulator.ResAccumulator) error {
|
ra *accumulator.ResAccumulator) error {
|
||||||
var generators []resmap.Generator
|
var generators []resmap.Generator
|
||||||
|
|||||||
@@ -124,14 +124,34 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
|||||||
return rf.resourcesFromRNodes(nodes), nil
|
return rf.resourcesFromRNodes(nodes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourcesFromRNodes converts RNodes to Resources.
|
// DropLocalNodes removes the local nodes by default. Local nodes are detected via the annotation `config.kubernetes.io/local-config: "true"`
|
||||||
func (rf *Factory) ResourcesFromRNodes(
|
func (rf *Factory) DropLocalNodes(nodes []*yaml.RNode) ([]*Resource, error) {
|
||||||
nodes []*yaml.RNode) (result []*Resource, err error) {
|
var result []*yaml.RNode
|
||||||
nodes, err = rf.dropBadNodes(nodes)
|
for _, node := range nodes {
|
||||||
|
if node.IsNilOrEmpty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
md, err := node.GetValidatedMetadata()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return rf.resourcesFromRNodes(nodes), nil
|
|
||||||
|
if rf.IncludeLocalConfigs {
|
||||||
|
result = append(result, node)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
localConfig, exist := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
||||||
|
if !exist || localConfig == "false" {
|
||||||
|
result = append(result, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rf.resourcesFromRNodes(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourcesFromRNodes converts RNodes to Resources.
|
||||||
|
func (rf *Factory) ResourcesFromRNodes(
|
||||||
|
nodes []*yaml.RNode) (result []*Resource, err error) {
|
||||||
|
return rf.DropLocalNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourcesFromRNode assumes all nodes are good.
|
// resourcesFromRNode assumes all nodes are good.
|
||||||
@@ -216,38 +236,20 @@ func (rf *Factory) convertObjectSliceToNodeSlice(
|
|||||||
func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
var result []*yaml.RNode
|
var result []*yaml.RNode
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
ignore, err := rf.shouldIgnore(n)
|
if n.IsNilOrEmpty() {
|
||||||
if err != nil {
|
continue
|
||||||
|
}
|
||||||
|
if _, err := n.GetValidatedMetadata(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !ignore {
|
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
||||||
|
return nil, fmt.Errorf("empty item at %v in object %v", path, n)
|
||||||
|
}
|
||||||
result = append(result, n)
|
result = append(result, n)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldIgnore returns true if there's some reason to ignore the node.
|
|
||||||
func (rf *Factory) shouldIgnore(n *yaml.RNode) (bool, error) {
|
|
||||||
if n.IsNilOrEmpty() {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if !rf.IncludeLocalConfigs {
|
|
||||||
md, err := n.GetValidatedMetadata()
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
|
||||||
if ignore {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
|
||||||
return true, fmt.Errorf("empty item at %v in object %v", path, n)
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
|
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
|
||||||
// name.
|
// name.
|
||||||
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
|
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
. "sigs.k8s.io/kustomize/api/resource"
|
. "sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRNodesFromBytes(t *testing.T) {
|
func TestRNodesFromBytes(t *testing.T) {
|
||||||
@@ -465,30 +466,6 @@ metadata:
|
|||||||
`, `
|
`, `
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
|
||||||
name: winnie
|
|
||||||
`},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"localConfigYaml": {
|
|
||||||
input: []byte(`
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: winnie-skip
|
|
||||||
annotations:
|
|
||||||
# this annotation causes the Resource to be ignored by kustomize
|
|
||||||
config.kubernetes.io/local-config: ""
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: winnie
|
|
||||||
`),
|
|
||||||
exp: expected{
|
|
||||||
out: []string{`
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
metadata:
|
||||||
name: winnie
|
name: winnie
|
||||||
`},
|
`},
|
||||||
@@ -673,3 +650,93 @@ data:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDropLocalNodes(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
input []byte
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
"localConfigUnset": {
|
||||||
|
input: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie
|
||||||
|
`),
|
||||||
|
expected: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
"localConfigSet": {
|
||||||
|
input: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie-skip
|
||||||
|
annotations:
|
||||||
|
# this annotation causes the Resource to be ignored by kustomize
|
||||||
|
config.kubernetes.io/local-config: ""
|
||||||
|
`),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
"localConfigSetToTrue": {
|
||||||
|
input: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie-skip
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
|
`),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
"localConfigSetToFalse": {
|
||||||
|
input: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "false"
|
||||||
|
`),
|
||||||
|
expected: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "false"
|
||||||
|
name: winnie
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
"localConfigMultiInput": {
|
||||||
|
input: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie-skip
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
|
`),
|
||||||
|
expected: []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: winnie
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for n := range testCases {
|
||||||
|
tc := testCases[n]
|
||||||
|
t.Run(n, func(t *testing.T) {
|
||||||
|
nin, _ := kio.FromBytes(tc.input)
|
||||||
|
res, err := factory.DropLocalNodes(nin)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if tc.expected == nil {
|
||||||
|
assert.Equal(t, 0, len(res))
|
||||||
|
} else {
|
||||||
|
actual, _ := res[0].AsYAML()
|
||||||
|
assert.Equal(t, tc.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user