mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
cmd/config run scoping and path defaulting
- default the path and index for Resources generated by functions - scope functions to only operate against resources in subdirectories
This commit is contained in:
@@ -5,8 +5,10 @@ package kioutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
@@ -41,13 +43,101 @@ func ErrorIfMissingAnnotation(nodes []*yaml.RNode, keys ...AnnotationKey) error
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if val == nil {
|
||||
return errors.Errorf("missing package annotation %s", key)
|
||||
return errors.Errorf("missing annotation %s", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePathAnnotationValue creates a default path annotation value for a Resource.
|
||||
// The path prefix will be dir.
|
||||
func CreatePathAnnotationValue(dir string, m yaml.ResourceMeta) string {
|
||||
filename := fmt.Sprintf("%s_%s.yaml", strings.ToLower(m.Kind), m.Name)
|
||||
return path.Join(dir, m.Namespace, filename)
|
||||
}
|
||||
|
||||
// DefaultPathAndIndexAnnotation sets a default path or index value on any nodes missing the
|
||||
// annotation
|
||||
func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
counts := map[string]int{}
|
||||
|
||||
// check each node for the path annotation
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// calculate the max index in each file in case we are appending
|
||||
if p, found := m.Annotations[PathAnnotation]; found {
|
||||
// record the max indexes into each file
|
||||
if i, found := m.Annotations[IndexAnnotation]; found {
|
||||
index, _ := strconv.Atoi(i)
|
||||
if index > counts[p] {
|
||||
counts[p] = index
|
||||
}
|
||||
}
|
||||
|
||||
// has the path annotation already -- do nothing
|
||||
continue
|
||||
}
|
||||
|
||||
// set a path annotation on the Resource
|
||||
path := CreatePathAnnotationValue(dir, m)
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set the index annotations
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, found := m.Annotations[IndexAnnotation]; found {
|
||||
continue
|
||||
}
|
||||
|
||||
p := m.Annotations[PathAnnotation]
|
||||
|
||||
// set an index annotation on the Resource
|
||||
c := counts[p]
|
||||
counts[p] = c + 1
|
||||
if err := nodes[i].PipeE(
|
||||
yaml.SetAnnotation(IndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultPathAnnotation sets a default path annotation on any Reources
|
||||
// missing it.
|
||||
func DefaultPathAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
// check each node for the path annotation
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, found := m.Annotations[PathAnnotation]; found {
|
||||
// has the path annotation already -- do nothing
|
||||
continue
|
||||
}
|
||||
|
||||
// set a path annotation on the Resource
|
||||
path := CreatePathAnnotationValue(dir, m)
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Map invokes fn for each element in nodes.
|
||||
func Map(nodes []*yaml.RNode, fn func(*yaml.RNode) (*yaml.RNode, error)) ([]*yaml.RNode, error) {
|
||||
var returnNodes []*yaml.RNode
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestSortNodes_moreThan10(t *testing.T) {
|
||||
@@ -75,3 +76,257 @@ y: z
|
||||
|
||||
assert.Equal(t, strings.TrimSpace(input), strings.TrimSpace(actual.String()))
|
||||
}
|
||||
|
||||
func TestDefaultPathAnnotation(t *testing.T) {
|
||||
var tests = []struct {
|
||||
dir string
|
||||
input string // input
|
||||
expected string // expected result
|
||||
name string
|
||||
}{
|
||||
{
|
||||
`foo`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/b/bar_a.yaml'
|
||||
`, `with namespace`},
|
||||
{
|
||||
`foo`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar_a.yaml'
|
||||
`, `without namespace`},
|
||||
|
||||
{
|
||||
``,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'b/bar_a.yaml'
|
||||
`, `without dir`},
|
||||
{
|
||||
``,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
`, `skip`},
|
||||
}
|
||||
|
||||
for _, s := range tests {
|
||||
n := yaml.MustParse(s.input)
|
||||
err := kioutil.DefaultPathAnnotation(s.dir, []*yaml.RNode{n})
|
||||
if !assert.NoError(t, err, s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, s.expected, n.MustString(), s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultPathAndIndexAnnotation(t *testing.T) {
|
||||
var tests = []struct {
|
||||
dir string
|
||||
input string // input
|
||||
expected string // expected result
|
||||
name string
|
||||
}{
|
||||
{
|
||||
`foo`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/b/bar_a.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
`, `with namespace`},
|
||||
{
|
||||
`foo`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'foo/bar_a.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
`, `without namespace`},
|
||||
|
||||
{
|
||||
``,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'b/bar_a.yaml'
|
||||
config.kubernetes.io/index: '0'
|
||||
`, `without dir`},
|
||||
{
|
||||
``,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
config.kubernetes.io/index: '5'
|
||||
`,
|
||||
`apiVersion: v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: a
|
||||
namespace: b
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'a/b.yaml'
|
||||
config.kubernetes.io/index: '5'
|
||||
`, `skip`},
|
||||
}
|
||||
|
||||
for _, s := range tests {
|
||||
out := &bytes.Buffer{}
|
||||
r := kio.ByteReadWriter{
|
||||
Reader: bytes.NewBufferString(s.input),
|
||||
Writer: out,
|
||||
KeepReaderAnnotations: true,
|
||||
OmitReaderAnnotations: true,
|
||||
}
|
||||
n, err := r.Read()
|
||||
if !assert.NoError(t, err, s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, kioutil.DefaultPathAndIndexAnnotation(s.dir, n), s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, r.Write(n), s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, s.expected, out.String(), s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePathAnnotationValue(t *testing.T) {
|
||||
var tests = []struct {
|
||||
dir string
|
||||
meta yaml.ResourceMeta // input
|
||||
expected string // expected result
|
||||
name string
|
||||
}{
|
||||
{
|
||||
`dir`,
|
||||
yaml.ResourceMeta{Kind: "foo",
|
||||
APIVersion: "apps/v1",
|
||||
ObjectMeta: yaml.ObjectMeta{Name: "bar", Namespace: "baz"},
|
||||
},
|
||||
`dir/baz/foo_bar.yaml`, `with namespace`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
yaml.ResourceMeta{Kind: "foo",
|
||||
APIVersion: "apps/v1",
|
||||
ObjectMeta: yaml.ObjectMeta{Name: "bar", Namespace: "baz"},
|
||||
},
|
||||
`baz/foo_bar.yaml`, `without dir`,
|
||||
},
|
||||
{
|
||||
`dir`,
|
||||
yaml.ResourceMeta{Kind: "foo",
|
||||
APIVersion: "apps/v1",
|
||||
ObjectMeta: yaml.ObjectMeta{Name: "bar"},
|
||||
},
|
||||
`dir/foo_bar.yaml`, `without namespace`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
yaml.ResourceMeta{Kind: "foo",
|
||||
APIVersion: "apps/v1",
|
||||
ObjectMeta: yaml.ObjectMeta{Name: "bar"},
|
||||
},
|
||||
`foo_bar.yaml`, `without namespace or dir`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
yaml.ResourceMeta{Kind: "foo",
|
||||
APIVersion: "apps/v1",
|
||||
ObjectMeta: yaml.ObjectMeta{},
|
||||
},
|
||||
`foo_.yaml`, `without namespace, dir or name`,
|
||||
},
|
||||
{
|
||||
``,
|
||||
yaml.ResourceMeta{
|
||||
APIVersion: "apps/v1",
|
||||
ObjectMeta: yaml.ObjectMeta{},
|
||||
},
|
||||
`_.yaml`, `without any`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range tests {
|
||||
p := kioutil.CreatePathAnnotationValue(s.dir, s.meta)
|
||||
if !assert.Equal(t, s.expected, p, s.name) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user