mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 01:50:55 +00:00
Introduce resmap.ApplyFilter.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -173,7 +174,7 @@ type ResMap interface {
|
||||
// in the resid.DefaultNamespace entry.
|
||||
GroupedByCurrentNamespace() map[string][]*resource.Resource
|
||||
|
||||
// GroupByOrginalNamespace performs as GroupByNamespace
|
||||
// GroupedByOriginalNamespace performs as GroupByNamespace
|
||||
// but use the original namespace instead of the current
|
||||
// one to perform the grouping.
|
||||
GroupedByOriginalNamespace() map[string][]*resource.Resource
|
||||
@@ -253,4 +254,14 @@ type ResMap interface {
|
||||
|
||||
// RemoveBuildAnnotations removes annotations created by the build process.
|
||||
RemoveBuildAnnotations()
|
||||
|
||||
// ApplyFilter applies an RNode filter to all Resources in the ResMap.
|
||||
// TODO: Send/recover ancillary Resource data to/from subprocesses.
|
||||
// Assure that the ancillary data in Resource (everything not in the RNode)
|
||||
// is sent to and re-captured from transformer subprocess (as the process
|
||||
// might edit that information). One way to do this would be to solely use
|
||||
// RNode metadata annotation reading and writing instead of using Resource
|
||||
// struct data members, i.e. the Resource struct is replaced by RNode
|
||||
// and use of (slow) k8s metadata annotations inside the RNode.
|
||||
ApplyFilter(f kio.Filter) error
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -423,6 +424,7 @@ func getNamespacesForRoleBinding(r *resource.Resource) map[string]bool {
|
||||
if r.GetKind() != "RoleBinding" {
|
||||
return result
|
||||
}
|
||||
//nolint staticcheck
|
||||
subjects, err := r.GetSlice("subjects")
|
||||
if err != nil || subjects == nil {
|
||||
return result
|
||||
@@ -617,3 +619,41 @@ func (m *resWrangler) RemoveBuildAnnotations() {
|
||||
r.RemoveBuildAnnotations()
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyFilter implements ResMap.
|
||||
func (m *resWrangler) ApplyFilter(f kio.Filter) error {
|
||||
reverseLookup := make(map[*kyaml.RNode]*resource.Resource, len(m.rList))
|
||||
nodes := make([]*kyaml.RNode, len(m.rList))
|
||||
for i, r := range m.rList {
|
||||
ptr := r.Node()
|
||||
nodes[i] = ptr
|
||||
reverseLookup[ptr] = r
|
||||
}
|
||||
// The filter can modify nodes, but also delete and create them.
|
||||
// The filtered list might be smaller or larger than the nodes list.
|
||||
filtered, err := f.Filter(nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Rebuild the resmap from the filtered RNodes.
|
||||
var nRList []*resource.Resource
|
||||
for _, rn := range filtered {
|
||||
if rn.IsNilOrEmpty() {
|
||||
// A node might make it through the filter as an object,
|
||||
// but still be empty. Drop such entries.
|
||||
continue
|
||||
}
|
||||
res, ok := reverseLookup[rn]
|
||||
if !ok {
|
||||
// A node was created; make a Resource to wrap it.
|
||||
// Leave remaining Resource fields empty.
|
||||
// At time of writing, seeking to eliminate those fields.
|
||||
// Alternatively, could just return error on creation attempt
|
||||
// until remaining fields eliminated.
|
||||
res = resource.NewResource(rn)
|
||||
}
|
||||
nRList = append(nRList, res)
|
||||
}
|
||||
m.rList = nRList
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,12 +11,16 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/filters/labels"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
. "sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var depProvider = provider.NewDefaultDepProvider()
|
||||
@@ -1144,6 +1148,186 @@ func addNamespace(namespace string, base string) string {
|
||||
return res
|
||||
}
|
||||
|
||||
// DeleteOddsFilter deletes the odd entries, removing nodes.
|
||||
// This is a ridiculous filter for testing.
|
||||
type DeleteOddsFilter struct{}
|
||||
|
||||
func (f DeleteOddsFilter) Filter(
|
||||
nodes []*yaml.RNode) (result []*yaml.RNode, err error) {
|
||||
for i := range nodes {
|
||||
if i%2 == 0 {
|
||||
// Keep the even entries, drop the odd entries.
|
||||
result = append(result, nodes[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CloneOddsFilter deletes even entries and clones odd entries,
|
||||
// making new nodes.
|
||||
// This is a ridiculous filter for testing.
|
||||
type CloneOddsFilter struct{}
|
||||
|
||||
func (f CloneOddsFilter) Filter(
|
||||
nodes []*yaml.RNode) (result []*yaml.RNode, err error) {
|
||||
for i := range nodes {
|
||||
if i%2 != 0 {
|
||||
newNode := nodes[i].Copy()
|
||||
// Add suffix to the name, so that it's unique (w/r to this test).
|
||||
newNode.SetName(newNode.GetName() + "Clone")
|
||||
// Return a ptr to the copy.
|
||||
result = append(result, nodes[i], newNode)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestApplyFilter(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
f kio.Filter
|
||||
expected string
|
||||
}{
|
||||
"labels": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Beans
|
||||
metadata:
|
||||
name: myBeans
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Franks
|
||||
metadata:
|
||||
name: myFranks
|
||||
`,
|
||||
f: labels.Filter{
|
||||
Labels: map[string]string{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
},
|
||||
FsSlice: types.FsSlice{
|
||||
{
|
||||
Gvk: resid.NewGvk("example.com", "v1", "Beans"),
|
||||
Path: "metadata/labels",
|
||||
CreateIfNotPresent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Beans
|
||||
metadata:
|
||||
labels:
|
||||
a: foo
|
||||
b: bar
|
||||
name: myBeans
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Franks
|
||||
metadata:
|
||||
name: myFranks
|
||||
`,
|
||||
},
|
||||
"deleteOddNodes": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Zero
|
||||
metadata:
|
||||
name: r0
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: One
|
||||
metadata:
|
||||
name: r1
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Two
|
||||
metadata:
|
||||
name: r2
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Three
|
||||
metadata:
|
||||
name: r3
|
||||
`,
|
||||
f: DeleteOddsFilter{},
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Zero
|
||||
metadata:
|
||||
name: r0
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Two
|
||||
metadata:
|
||||
name: r2
|
||||
`,
|
||||
},
|
||||
"cloneOddNodes": {
|
||||
// input list has five entries
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Zero
|
||||
metadata:
|
||||
name: r0
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: One
|
||||
metadata:
|
||||
name: r1
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Two
|
||||
metadata:
|
||||
name: r2
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Three
|
||||
metadata:
|
||||
name: r3
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Four
|
||||
metadata:
|
||||
name: r4
|
||||
`,
|
||||
f: CloneOddsFilter{},
|
||||
// output has four, but half are newly created nodes.
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: One
|
||||
metadata:
|
||||
name: r1
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: One
|
||||
metadata:
|
||||
name: r1Clone
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Three
|
||||
metadata:
|
||||
name: r3
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Three
|
||||
metadata:
|
||||
name: r3Clone
|
||||
`,
|
||||
},
|
||||
}
|
||||
for name := range tests {
|
||||
tc := tests[name]
|
||||
t.Run(name, func(t *testing.T) {
|
||||
m, err := rmF.NewResMapFromBytes([]byte(tc.input))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, m.ApplyFilter(tc.f))
|
||||
kusttest_test.AssertActualEqualsExpectedWithTweak(
|
||||
t, m, nil, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplySmPatch_Deletion(t *testing.T) {
|
||||
target := `
|
||||
apiVersion: apps/v1
|
||||
|
||||
Reference in New Issue
Block a user