mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-09-15 12:18:57 +00:00
Merge pull request #4180 from yuwenma/fix-4124
[Fix 4124] Skip local resource until all transformations have completed.
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
|
||||||
|
|||||||
70
api/krusty/localconfig_test.go
Normal file
70
api/krusty/localconfig_test.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package krusty_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This test checks that if a resource is annotated as "local-config", this resource won't be ignored until
|
||||||
|
// all transformations have completed. This makes sure the local resource can be used as a transformation input.
|
||||||
|
// See https://github.com/kubernetes-sigs/kustomize/issues/4124 for details.
|
||||||
|
func TestSKipLocalConfigAfterTransform(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK(".", `apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- pod.yaml
|
||||||
|
- deployment.yaml
|
||||||
|
transformers:
|
||||||
|
- replacement.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("pod.yaml", `apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: buildup
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
image: nginx
|
||||||
|
`)
|
||||||
|
th.WriteF("deployment.yaml", `apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: buildup
|
||||||
|
`)
|
||||||
|
th.WriteF("replacement.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ReplacementTransformer
|
||||||
|
metadata:
|
||||||
|
name: buildup
|
||||||
|
replacements:
|
||||||
|
- source:
|
||||||
|
kind: Pod
|
||||||
|
fieldPath: spec
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
kind: Deployment
|
||||||
|
fieldPaths:
|
||||||
|
- spec.template.spec
|
||||||
|
options:
|
||||||
|
create: true
|
||||||
|
`)
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: buildup
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: app
|
||||||
|
`)
|
||||||
|
}
|
||||||
@@ -124,14 +124,34 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
|||||||
return rf.resourcesFromRNodes(nodes), nil
|
return rf.resourcesFromRNodes(nodes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DropLocalNodes removes the local nodes by default. Local nodes are detected via the annotation `config.kubernetes.io/local-config: "true"`
|
||||||
|
func (rf *Factory) DropLocalNodes(nodes []*yaml.RNode) ([]*Resource, error) {
|
||||||
|
var result []*yaml.RNode
|
||||||
|
for _, node := range nodes {
|
||||||
|
if node.IsNilOrEmpty() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
md, err := node.GetValidatedMetadata()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// ResourcesFromRNodes converts RNodes to Resources.
|
||||||
func (rf *Factory) ResourcesFromRNodes(
|
func (rf *Factory) ResourcesFromRNodes(
|
||||||
nodes []*yaml.RNode) (result []*Resource, err error) {
|
nodes []*yaml.RNode) (result []*Resource, err error) {
|
||||||
nodes, err = rf.dropBadNodes(nodes)
|
return rf.DropLocalNodes(nodes)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return rf.resourcesFromRNodes(nodes), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourcesFromRNode assumes all nodes are good.
|
// resourcesFromRNode assumes all nodes are good.
|
||||||
@@ -224,38 +244,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 {
|
||||||
result = append(result, n)
|
return nil, fmt.Errorf("empty item at %v in object %v", path, 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
|
||||||
`},
|
`},
|
||||||
@@ -670,3 +647,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