mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Switch prefix transformer to kyaml.
This commit is contained in:
@@ -5,13 +5,12 @@ package builtins
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/transform"
|
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||||
"sigs.k8s.io/kustomize/api/resid"
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,17 +18,14 @@ import (
|
|||||||
type PrefixSuffixTransformerPlugin struct {
|
type PrefixSuffixTransformerPlugin struct {
|
||||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||||
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||||
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
FieldSpecs types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not placed in a file yet due to lack of demand.
|
// A Gvk skip list for prefix/suffix modification.
|
||||||
var prefixSuffixFieldSpecsToSkip = []types.FieldSpec{
|
// hard coded for now - eventually should be part of config.
|
||||||
{
|
var prefixSuffixFieldSpecsToSkip = types.FsSlice{
|
||||||
Gvk: resid.Gvk{Kind: "CustomResourceDefinition"},
|
{Gvk: resid.Gvk{Kind: "CustomResourceDefinition"}},
|
||||||
},
|
{Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"}},
|
||||||
{
|
|
||||||
Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PrefixSuffixTransformerPlugin) Config(
|
func (p *PrefixSuffixTransformerPlugin) Config(
|
||||||
@@ -48,29 +44,24 @@ func (p *PrefixSuffixTransformerPlugin) Config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
|
func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
|
||||||
// Even if both the Prefix and Suffix are empty we want
|
// Even if both the Prefix and Suffix are empty we want
|
||||||
// to proceed with the transformation. This allows to add contextual
|
// to proceed with the transformation. This allows to add contextual
|
||||||
// information to the resources (AddNamePrefix and AddNameSuffix).
|
// information to the resources (AddNamePrefix and AddNameSuffix).
|
||||||
|
|
||||||
for _, r := range m.Resources() {
|
for _, r := range m.Resources() {
|
||||||
|
// TODO: move this test into the filter (i.e. make a better filter)
|
||||||
if p.shouldSkip(r.OrgId()) {
|
if p.shouldSkip(r.OrgId()) {
|
||||||
// Don't change the actual definition
|
|
||||||
// of a CRD.
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
id := r.OrgId()
|
id := r.OrgId()
|
||||||
// current default configuration contains
|
// current default configuration contains
|
||||||
// only one entry: "metadata/name" with no GVK
|
// only one entry: "metadata/name" with no GVK
|
||||||
for _, path := range p.FieldSpecs {
|
for _, fs := range p.FieldSpecs {
|
||||||
if !id.IsSelected(&path.Gvk) {
|
// TODO: this is redundant to filter (but needed for now)
|
||||||
// With the currrent default configuration,
|
if !id.IsSelected(&fs.Gvk) {
|
||||||
// because no Gvk is specified, so a wild
|
|
||||||
// card
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// TODO: move this test into the filter.
|
||||||
if smellsLikeANameChange(&path) {
|
if smellsLikeANameChange(&fs) {
|
||||||
// "metadata/name" is the only field.
|
// "metadata/name" is the only field.
|
||||||
// this will add a prefix and a suffix
|
// this will add a prefix and a suffix
|
||||||
// to the resource even if those are
|
// to the resource even if those are
|
||||||
@@ -78,15 +69,11 @@ func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
|
|||||||
r.AddNamePrefix(p.Prefix)
|
r.AddNamePrefix(p.Prefix)
|
||||||
r.AddNameSuffix(p.Suffix)
|
r.AddNameSuffix(p.Suffix)
|
||||||
}
|
}
|
||||||
|
err := filtersutil.ApplyToJSON(prefixsuffix.Filter{
|
||||||
// the addPrefixSuffix method will not
|
Prefix: p.Prefix,
|
||||||
// change the name if both the prefix and suffix
|
Suffix: p.Suffix,
|
||||||
// are empty.
|
FieldSpec: fs,
|
||||||
err := transform.MutateField(
|
}, r)
|
||||||
r.Map(),
|
|
||||||
path.PathSlice(),
|
|
||||||
path.CreateIfNotPresent,
|
|
||||||
p.addPrefixSuffix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -99,8 +86,7 @@ func smellsLikeANameChange(fs *types.FieldSpec) bool {
|
|||||||
return fs.Path == "metadata/name"
|
return fs.Path == "metadata/name"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PrefixSuffixTransformerPlugin) shouldSkip(
|
func (p *PrefixSuffixTransformerPlugin) shouldSkip(id resid.ResId) bool {
|
||||||
id resid.ResId) bool {
|
|
||||||
for _, path := range prefixSuffixFieldSpecsToSkip {
|
for _, path := range prefixSuffixFieldSpecsToSkip {
|
||||||
if id.IsSelected(&path.Gvk) {
|
if id.IsSelected(&path.Gvk) {
|
||||||
return true
|
return true
|
||||||
@@ -109,15 +95,6 @@ func (p *PrefixSuffixTransformerPlugin) shouldSkip(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PrefixSuffixTransformerPlugin) addPrefixSuffix(
|
|
||||||
in interface{}) (interface{}, error) {
|
|
||||||
s, ok := in.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s%s%s", p.Prefix, s, p.Suffix), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
|
func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
|
||||||
return &PrefixSuffixTransformerPlugin{}
|
return &PrefixSuffixTransformerPlugin{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if err := node.PipeE(fsslice.Filter{
|
if err := node.PipeE(fsslice.Filter{
|
||||||
FsSlice: f.FsSlice,
|
FsSlice: f.FsSlice,
|
||||||
SetValue: fsslice.SetEntry(k, f.Annotations[k], yaml.StringTag),
|
SetValue: filtersutil.SetEntry(k, f.Annotations[k], yaml.StringTag),
|
||||||
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
|
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
|
||||||
CreateTag: "!!map",
|
CreateTag: "!!map",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
|||||||
6
api/filters/fieldspec/doc.go
Normal file
6
api/filters/fieldspec/doc.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package fieldspec contains a yaml.Filter to modify a resource
|
||||||
|
// that matches the FieldSpec.
|
||||||
|
package fieldspec
|
||||||
61
api/filters/fieldspec/example_test.go
Normal file
61
api/filters/fieldspec/example_test.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package fieldspec_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleFilter() {
|
||||||
|
in := &kio.ByteReader{
|
||||||
|
Reader: bytes.NewBufferString(`
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Foo
|
||||||
|
metadata:
|
||||||
|
name: instance
|
||||||
|
---
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Bar
|
||||||
|
metadata:
|
||||||
|
name: instance
|
||||||
|
`),
|
||||||
|
}
|
||||||
|
fltr := Filter{
|
||||||
|
CreateKind: yaml.ScalarNode,
|
||||||
|
SetValue: filtersutil.SetScalar("green"),
|
||||||
|
FieldSpec: types.FieldSpec{Path: "a/b", CreateIfNotPresent: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{in},
|
||||||
|
Filters: []kio.Filter{kio.FilterAll(fltr)},
|
||||||
|
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||||
|
}.Execute()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// apiVersion: example.com/v1
|
||||||
|
// kind: Foo
|
||||||
|
// metadata:
|
||||||
|
// name: instance
|
||||||
|
// a:
|
||||||
|
// b: green
|
||||||
|
// ---
|
||||||
|
// apiVersion: example.com/v1
|
||||||
|
// kind: Bar
|
||||||
|
// metadata:
|
||||||
|
// name: instance
|
||||||
|
// a:
|
||||||
|
// b: green
|
||||||
|
}
|
||||||
@@ -1,24 +1,27 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package fsslice
|
package fieldspec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fieldSpecFilter applies a single fieldSpec to a single object
|
var _ yaml.Filter = Filter{}
|
||||||
// fieldSpecFilter stores internal state and should not be reused
|
|
||||||
type fieldSpecFilter struct {
|
// Filter applies a single fieldSpec to a single object
|
||||||
|
// Filter stores internal state and should not be reused
|
||||||
|
type Filter struct {
|
||||||
// FieldSpec contains the path to the value to set.
|
// FieldSpec contains the path to the value to set.
|
||||||
FieldSpec types.FieldSpec `yaml:"fieldSpec"`
|
FieldSpec types.FieldSpec `yaml:"fieldSpec"`
|
||||||
|
|
||||||
// Set the field using this function
|
// Set the field using this function
|
||||||
SetValue SetFn
|
SetValue filtersutil.SetFn
|
||||||
|
|
||||||
// CreateKind defines the type of node to create if the field is not found
|
// CreateKind defines the type of node to create if the field is not found
|
||||||
CreateKind yaml.Kind
|
CreateKind yaml.Kind
|
||||||
@@ -29,7 +32,7 @@ type fieldSpecFilter struct {
|
|||||||
path []string
|
path []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fltr fieldSpecFilter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
||||||
// check if the FieldSpec applies to the object
|
// check if the FieldSpec applies to the object
|
||||||
if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil {
|
if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil {
|
||||||
return obj, errors.Wrap(err)
|
return obj, errors.Wrap(err)
|
||||||
@@ -43,7 +46,7 @@ func (fltr fieldSpecFilter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fltr fieldSpecFilter) filter(obj *yaml.RNode) error {
|
func (fltr Filter) filter(obj *yaml.RNode) error {
|
||||||
if len(fltr.path) == 0 {
|
if len(fltr.path) == 0 {
|
||||||
// found the field -- set its value
|
// found the field -- set its value
|
||||||
return fltr.SetValue(obj)
|
return fltr.SetValue(obj)
|
||||||
@@ -60,7 +63,7 @@ func (fltr fieldSpecFilter) filter(obj *yaml.RNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// field calls filter on the field matching the next path element
|
// field calls filter on the field matching the next path element
|
||||||
func (fltr fieldSpecFilter) field(obj *yaml.RNode) error {
|
func (fltr Filter) field(obj *yaml.RNode) error {
|
||||||
fieldName, isSeq := isSequenceField(fltr.path[0])
|
fieldName, isSeq := isSequenceField(fltr.path[0])
|
||||||
|
|
||||||
// lookup the field matching the next path element
|
// lookup the field matching the next path element
|
||||||
@@ -104,9 +107,9 @@ func (fltr fieldSpecFilter) field(obj *yaml.RNode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// seq calls filter on all sequence elements
|
// seq calls filter on all sequence elements
|
||||||
func (fltr fieldSpecFilter) seq(obj *yaml.RNode) error {
|
func (fltr Filter) seq(obj *yaml.RNode) error {
|
||||||
if err := obj.VisitElements(func(node *yaml.RNode) error {
|
if err := obj.VisitElements(func(node *yaml.RNode) error {
|
||||||
// recurse on each element -- re-allocating a fieldSpecFilter is
|
// recurse on each element -- re-allocating a Filter is
|
||||||
// not strictly required, but is more consistent with field
|
// not strictly required, but is more consistent with field
|
||||||
// and less likely to have side effects
|
// and less likely to have side effects
|
||||||
// keep the entire path -- it does not contain parts for sequences
|
// keep the entire path -- it does not contain parts for sequences
|
||||||
380
api/filters/fieldspec/fieldspec_test.go
Normal file
380
api/filters/fieldspec/fieldspec_test.go
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package fieldspec_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestCase struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
filter fieldspec.Filter
|
||||||
|
fieldSpec string
|
||||||
|
error string
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []TestCase{
|
||||||
|
{
|
||||||
|
name: "update",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: foo
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: e
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "update-kind-not-match",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: foo
|
||||||
|
kind: Bar1
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar2
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar2
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "update-group-not-match",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: foo1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo2/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo2/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "update-version-not-match",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: foo
|
||||||
|
version: v1beta1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta2
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta2
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "bad-version",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: foo
|
||||||
|
version: v1beta1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta2/something
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta2/something
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "bad-meta",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: foo
|
||||||
|
version: v1beta1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
a:
|
||||||
|
b: c
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
error: "missing Resource metadata",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "miss-match-type",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b/c
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: a
|
||||||
|
`,
|
||||||
|
error: "obj kind: Bar\na:\n b: a\n at path a/b/c: unsupported yaml node",
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "add",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b/c/d
|
||||||
|
group: foo
|
||||||
|
create: true
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a: {}
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a: {b: {c: {d: e}}}
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
CreateKind: yaml.ScalarNode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "update-in-sequence",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b[]/c/d
|
||||||
|
group: foo
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- c:
|
||||||
|
d: a
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- c:
|
||||||
|
d: e
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Don't create a sequence
|
||||||
|
{
|
||||||
|
name: "empty-sequence-no-create",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b[]/c/d
|
||||||
|
group: foo
|
||||||
|
create: true
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a: {}
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a: {}
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
CreateKind: yaml.ScalarNode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create a new field for an element in a sequence
|
||||||
|
{
|
||||||
|
name: "empty-sequence-create",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b[]/c/d
|
||||||
|
group: foo
|
||||||
|
create: true
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- c: {}
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: foo/v1beta1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
- c: {d: e}
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
CreateKind: yaml.ScalarNode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "group v1",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
group: v1
|
||||||
|
create: true
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
CreateKind: yaml.ScalarNode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "version v1",
|
||||||
|
fieldSpec: `
|
||||||
|
path: a/b
|
||||||
|
version: v1
|
||||||
|
create: true
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Bar
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Bar
|
||||||
|
a:
|
||||||
|
b: e
|
||||||
|
`,
|
||||||
|
filter: fieldspec.Filter{
|
||||||
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
|
CreateKind: yaml.ScalarNode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter_Filter(t *testing.T) {
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
err := yaml.Unmarshal([]byte(test.fieldSpec), &test.filter.FieldSpec)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
rw := &kio.ByteReadWriter{
|
||||||
|
Reader: bytes.NewBufferString(test.input),
|
||||||
|
Writer: out,
|
||||||
|
OmitReaderAnnotations: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the filter
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{rw},
|
||||||
|
Filters: []kio.Filter{kio.FilterAll(test.filter)},
|
||||||
|
Outputs: []kio.Writer{rw},
|
||||||
|
}.Execute()
|
||||||
|
if test.error != "" {
|
||||||
|
if !assert.EqualError(t, err, test.error) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
// stop rest of test
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// check results
|
||||||
|
if !assert.Equal(t,
|
||||||
|
strings.TrimSpace(test.expected),
|
||||||
|
strings.TrimSpace(out.String())) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
package fsslice
|
package fieldspec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
package fsslice
|
package fieldspec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
33
api/filters/filtersutil/setters.go
Normal file
33
api/filters/filtersutil/setters.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package filtersutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetFn is a function that accepts an RNode to possibly modify.
|
||||||
|
type SetFn func(*yaml.RNode) error
|
||||||
|
|
||||||
|
// SetScalar returns a SetFn to set a scalar value
|
||||||
|
func SetScalar(value string) SetFn {
|
||||||
|
return func(node *yaml.RNode) error {
|
||||||
|
return node.PipeE(yaml.FieldSetter{StringValue: value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEntry returns a SetFn to set an entry in a map
|
||||||
|
func SetEntry(key, value, tag string) SetFn {
|
||||||
|
n := &yaml.Node{
|
||||||
|
Kind: yaml.ScalarNode,
|
||||||
|
Value: value,
|
||||||
|
Tag: tag,
|
||||||
|
}
|
||||||
|
if tag == yaml.StringTag && yaml.IsYaml1_1NonString(n) {
|
||||||
|
n.Style = yaml.DoubleQuotedStyle
|
||||||
|
}
|
||||||
|
return func(node *yaml.RNode) error {
|
||||||
|
return node.PipeE(yaml.FieldSetter{
|
||||||
|
Name: key,
|
||||||
|
Value: yaml.NewRNode(n),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Copyright 2020 The Kubernetes Authors.
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package fsslice contains a yaml.Filter to modify a resource using an
|
// Package fsslice contains a yaml.Filter to modify a resource if
|
||||||
// FsSlice to identify fields to be updated within the resource.
|
// it matches one or more FieldSpec entries.
|
||||||
package fsslice
|
package fsslice
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -30,7 +31,7 @@ metadata:
|
|||||||
}
|
}
|
||||||
fltr := fsslice.Filter{
|
fltr := fsslice.Filter{
|
||||||
CreateKind: yaml.ScalarNode,
|
CreateKind: yaml.ScalarNode,
|
||||||
SetValue: fsslice.SetScalar("green"),
|
SetValue: filtersutil.SetScalar("green"),
|
||||||
FsSlice: []types.FieldSpec{
|
FsSlice: []types.FieldSpec{
|
||||||
{Path: "a/b", CreateIfNotPresent: true},
|
{Path: "a/b", CreateIfNotPresent: true},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,48 +4,22 @@
|
|||||||
package fsslice
|
package fsslice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetFn sets a value
|
|
||||||
type SetFn func(*yaml.RNode) error
|
|
||||||
|
|
||||||
// SetScalar returns a SetFn to set a scalar value
|
|
||||||
func SetScalar(value string) SetFn {
|
|
||||||
return func(node *yaml.RNode) error {
|
|
||||||
return node.PipeE(yaml.FieldSetter{StringValue: value})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEntry returns a SetFn to set an entry in a map
|
|
||||||
func SetEntry(key, value, tag string) SetFn {
|
|
||||||
n := &yaml.Node{
|
|
||||||
Kind: yaml.ScalarNode,
|
|
||||||
Value: value,
|
|
||||||
Tag: tag,
|
|
||||||
}
|
|
||||||
if tag == yaml.StringTag && yaml.IsYaml1_1NonString(n) {
|
|
||||||
n.Style = yaml.DoubleQuotedStyle
|
|
||||||
}
|
|
||||||
return func(node *yaml.RNode) error {
|
|
||||||
return node.PipeE(yaml.FieldSetter{
|
|
||||||
Name: key,
|
|
||||||
Value: yaml.NewRNode(n),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ yaml.Filter = Filter{}
|
var _ yaml.Filter = Filter{}
|
||||||
|
|
||||||
// Filter uses an FsSlice to modify fields on a single object
|
// Filter ranges over an FsSlice to modify fields on a single object.
|
||||||
|
// An FsSlice is a range of FieldSpecs. A FieldSpec is a GVK plus a path.
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
// FieldSpecList list of FieldSpecs to set
|
// FieldSpecList list of FieldSpecs to set
|
||||||
FsSlice types.FsSlice `yaml:"fsSlice"`
|
FsSlice types.FsSlice `yaml:"fsSlice"`
|
||||||
|
|
||||||
// SetValue is called on each field that matches one of the FieldSpecs
|
// SetValue is called on each field that matches one of the FieldSpecs
|
||||||
SetValue SetFn
|
SetValue filtersutil.SetFn
|
||||||
|
|
||||||
// CreateKind is used to create fields that do not exist
|
// CreateKind is used to create fields that do not exist
|
||||||
CreateKind yaml.Kind
|
CreateKind yaml.Kind
|
||||||
@@ -59,7 +33,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
// apply this FieldSpec
|
// apply this FieldSpec
|
||||||
// create a new filter for each iteration because they
|
// create a new filter for each iteration because they
|
||||||
// store internal state about the field paths
|
// store internal state about the field paths
|
||||||
_, err := (&fieldSpecFilter{
|
_, err := (&fieldspec.Filter{
|
||||||
FieldSpec: fltr.FsSlice[i],
|
FieldSpec: fltr.FsSlice[i],
|
||||||
SetValue: fltr.SetValue,
|
SetValue: fltr.SetValue,
|
||||||
CreateKind: fltr.CreateKind,
|
CreateKind: fltr.CreateKind,
|
||||||
|
|||||||
@@ -9,335 +9,77 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
|
. "sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
name string
|
|
||||||
input string
|
input string
|
||||||
expected string
|
expected string
|
||||||
filter fsslice.Filter
|
filter Filter
|
||||||
fsSlice string
|
fsSlice string
|
||||||
error string
|
error string
|
||||||
}
|
}
|
||||||
|
|
||||||
var tests = []TestCase{
|
var tests = map[string]TestCase{
|
||||||
{
|
"empty": {
|
||||||
name: "update",
|
|
||||||
fsSlice: `
|
fsSlice: `
|
||||||
- path: a/b
|
|
||||||
group: foo
|
|
||||||
kind: Bar
|
|
||||||
`,
|
`,
|
||||||
input: `
|
input: `
|
||||||
apiVersion: foo/v1beta1
|
apiVersion: foo/v1
|
||||||
kind: Bar
|
kind: Bar
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
`,
|
||||||
expected: `
|
expected: `
|
||||||
apiVersion: foo/v1beta1
|
apiVersion: foo/v1
|
||||||
kind: Bar
|
kind: Bar
|
||||||
a:
|
|
||||||
b: e
|
|
||||||
`,
|
`,
|
||||||
filter: fsslice.Filter{
|
filter: Filter{
|
||||||
SetValue: fsslice.SetScalar("e"),
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "update-kind-not-match",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b
|
|
||||||
group: foo
|
|
||||||
kind: Bar1
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar2
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar2
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "update-group-not-match",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b
|
|
||||||
group: foo1
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo2/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo2/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "update-version-not-match",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b
|
|
||||||
group: foo
|
|
||||||
version: v1beta1
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta2
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta2
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "bad-version",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b
|
|
||||||
group: foo
|
|
||||||
version: v1beta1
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta2/something
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta2/something
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "bad-meta",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b
|
|
||||||
group: foo
|
|
||||||
version: v1beta1
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
a:
|
|
||||||
b: c
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
error: "missing Resource metadata",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "miss-match-type",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b/c
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b: a
|
|
||||||
`,
|
|
||||||
error: "obj kind: Bar\na:\n b: a\n at path a/b/c: unsupported yaml node",
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "add",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b/c/d
|
|
||||||
group: foo
|
|
||||||
create: true
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a: {}
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a: {b: {c: {d: e}}}
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
CreateKind: yaml.ScalarNode,
|
CreateKind: yaml.ScalarNode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"two": {
|
||||||
{
|
|
||||||
name: "update-in-sequence",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b[]/c/d
|
|
||||||
group: foo
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
- c:
|
|
||||||
d: a
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
- c:
|
|
||||||
d: e
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Don't create a sequence
|
|
||||||
{
|
|
||||||
name: "empty-sequence-no-create",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b[]/c/d
|
|
||||||
group: foo
|
|
||||||
create: true
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a: {}
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a: {}
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
CreateKind: yaml.ScalarNode,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Create a new field for an element in a sequence
|
|
||||||
{
|
|
||||||
name: "empty-sequence-create",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b[]/c/d
|
|
||||||
group: foo
|
|
||||||
create: true
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
- c: {}
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: foo/v1beta1
|
|
||||||
kind: Bar
|
|
||||||
a:
|
|
||||||
b:
|
|
||||||
- c: {d: e}
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
CreateKind: yaml.ScalarNode,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "group v1",
|
|
||||||
fsSlice: `
|
fsSlice: `
|
||||||
- path: a/b
|
- path: a/b
|
||||||
group: v1
|
group: foo
|
||||||
|
version: v1
|
||||||
create: true
|
create: true
|
||||||
kind: Bar
|
kind: Bar
|
||||||
`,
|
- path: q/r[]/s/t
|
||||||
input: `
|
group: foo
|
||||||
apiVersion: v1
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
expected: `
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Bar
|
|
||||||
`,
|
|
||||||
filter: fsslice.Filter{
|
|
||||||
SetValue: fsslice.SetScalar("e"),
|
|
||||||
CreateKind: yaml.ScalarNode,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "version v1",
|
|
||||||
fsSlice: `
|
|
||||||
- path: a/b
|
|
||||||
version: v1
|
version: v1
|
||||||
create: true
|
create: true
|
||||||
kind: Bar
|
kind: Bar
|
||||||
`,
|
`,
|
||||||
input: `
|
input: `
|
||||||
apiVersion: v1
|
apiVersion: foo/v1
|
||||||
kind: Bar
|
kind: Bar
|
||||||
|
q:
|
||||||
|
r:
|
||||||
|
- s: {}
|
||||||
`,
|
`,
|
||||||
expected: `
|
expected: `
|
||||||
apiVersion: v1
|
apiVersion: foo/v1
|
||||||
kind: Bar
|
kind: Bar
|
||||||
|
q:
|
||||||
|
r:
|
||||||
|
- s: {t: e}
|
||||||
a:
|
a:
|
||||||
b: e
|
b: e
|
||||||
`,
|
`,
|
||||||
filter: fsslice.Filter{
|
filter: Filter{
|
||||||
SetValue: fsslice.SetScalar("e"),
|
SetValue: filtersutil.SetScalar("e"),
|
||||||
CreateKind: yaml.ScalarNode,
|
CreateKind: yaml.ScalarNode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter_Filter(t *testing.T) {
|
func TestFilter(t *testing.T) {
|
||||||
for i := range tests {
|
for name := range tests {
|
||||||
test := tests[i]
|
test := tests[name]
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err := yaml.Unmarshal([]byte(test.fsSlice), &test.filter.FsSlice)
|
err := yaml.Unmarshal([]byte(test.fsSlice), &test.filter.FsSlice)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package imagetag
|
package imagetag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -35,7 +36,7 @@ func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateImageTagFn(imageTag types.Image) fsslice.SetFn {
|
func updateImageTagFn(imageTag types.Image) filtersutil.SetFn {
|
||||||
return func(node *yaml.RNode) error {
|
return func(node *yaml.RNode) error {
|
||||||
return node.PipeE(imageTagUpdater{
|
return node.PipeE(imageTagUpdater{
|
||||||
ImageTag: imageTag,
|
ImageTag: imageTag,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if err := node.PipeE(fsslice.Filter{
|
if err := node.PipeE(fsslice.Filter{
|
||||||
FsSlice: f.FsSlice,
|
FsSlice: f.FsSlice,
|
||||||
SetValue: fsslice.SetEntry(k, f.Labels[k], yaml.StringTag),
|
SetValue: filtersutil.SetEntry(k, f.Labels[k], yaml.StringTag),
|
||||||
CreateKind: yaml.MappingNode, // Labels are MappingNodes.
|
CreateKind: yaml.MappingNode, // Labels are MappingNodes.
|
||||||
CreateTag: "!!map",
|
CreateTag: "!!map",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
package namespace
|
package namespace
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -42,7 +44,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
// transformations based on data -- :)
|
// transformations based on data -- :)
|
||||||
err := node.PipeE(fsslice.Filter{
|
err := node.PipeE(fsslice.Filter{
|
||||||
FsSlice: ns.FsSlice,
|
FsSlice: ns.FsSlice,
|
||||||
SetValue: fsslice.SetScalar(ns.Namespace),
|
SetValue: filtersutil.SetScalar(ns.Namespace),
|
||||||
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
||||||
CreateTag: yaml.StringTag,
|
CreateTag: yaml.StringTag,
|
||||||
})
|
})
|
||||||
@@ -73,7 +75,7 @@ func (ns Filter) hacks(obj *yaml.RNode) error {
|
|||||||
// if they are cluster scoped through either an annotation on the resources,
|
// if they are cluster scoped through either an annotation on the resources,
|
||||||
// or through inlined OpenAPI on the resource as a YAML comment.
|
// or through inlined OpenAPI on the resource as a YAML comment.
|
||||||
func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) error {
|
func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) error {
|
||||||
gvk := fsslice.GetGVK(meta)
|
gvk := fieldspec.GetGVK(meta)
|
||||||
if !gvk.IsNamespaceableKind() {
|
if !gvk.IsNamespaceableKind() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -81,7 +83,7 @@ func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) erro
|
|||||||
FsSlice: []types.FieldSpec{
|
FsSlice: []types.FieldSpec{
|
||||||
{Path: types.MetadataNamespacePath, CreateIfNotPresent: true},
|
{Path: types.MetadataNamespacePath, CreateIfNotPresent: true},
|
||||||
},
|
},
|
||||||
SetValue: fsslice.SetScalar(ns.Namespace),
|
SetValue: filtersutil.SetScalar(ns.Namespace),
|
||||||
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
||||||
}
|
}
|
||||||
_, err := f.Filter(obj)
|
_, err := f.Filter(obj)
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleFilter() {
|
func ExampleFilter() {
|
||||||
fss := builtinconfig.MakeDefaultConfig().NamePrefix
|
|
||||||
err := kio.Pipeline{
|
err := kio.Pipeline{
|
||||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
@@ -27,7 +26,8 @@ kind: Bar
|
|||||||
metadata:
|
metadata:
|
||||||
name: instance
|
name: instance
|
||||||
`)}},
|
`)}},
|
||||||
Filters: []kio.Filter{prefixsuffix.Filter{Prefix: "baz-", FsSlice: fss}},
|
Filters: []kio.Filter{prefixsuffix.Filter{
|
||||||
|
Prefix: "baz-", FieldSpec: types.FieldSpec{Path: "metadata/name"}}},
|
||||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||||
}.Execute()
|
}.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ package prefixsuffix
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
@@ -17,28 +18,26 @@ type Filter struct {
|
|||||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||||
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||||
|
|
||||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ kio.Filter = Filter{}
|
var _ kio.Filter = Filter{}
|
||||||
|
|
||||||
func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes)
|
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs the filter on a single node rather than a slice
|
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||||
func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
err := node.PipeE(fieldspec.Filter{
|
||||||
// transformations based on data -- :)
|
FieldSpec: f.FieldSpec,
|
||||||
err := node.PipeE(fsslice.Filter{
|
SetValue: f.evaluateField,
|
||||||
FsSlice: ns.FsSlice,
|
|
||||||
SetValue: ns.set,
|
|
||||||
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
||||||
CreateTag: yaml.StringTag,
|
CreateTag: yaml.StringTag,
|
||||||
})
|
})
|
||||||
return node, err
|
return node, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns Filter) set(node *yaml.RNode) error {
|
func (f Filter) evaluateField(node *yaml.RNode) error {
|
||||||
return fsslice.SetScalar(fmt.Sprintf(
|
return filtersutil.SetScalar(fmt.Sprintf(
|
||||||
"%s%s%s", ns.Prefix, node.YNode().Value, ns.Suffix))(node)
|
"%s%s%s", f.Prefix, node.YNode().Value, f.Suffix))(node)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
|
||||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tests = []TestCase{
|
var tests = map[string]TestCase{
|
||||||
{
|
"prefix": {
|
||||||
name: "prefix",
|
|
||||||
input: `
|
input: `
|
||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
kind: Foo
|
kind: Foo
|
||||||
@@ -40,10 +38,10 @@ metadata:
|
|||||||
name: foo-instance
|
name: foo-instance
|
||||||
`,
|
`,
|
||||||
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
||||||
|
fs: types.FieldSpec{Path: "metadata/name"},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
"suffix": {
|
||||||
name: "suffix",
|
|
||||||
input: `
|
input: `
|
||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
kind: Foo
|
kind: Foo
|
||||||
@@ -67,10 +65,10 @@ metadata:
|
|||||||
name: instance-foo
|
name: instance-foo
|
||||||
`,
|
`,
|
||||||
filter: prefixsuffix.Filter{Suffix: "-foo"},
|
filter: prefixsuffix.Filter{Suffix: "-foo"},
|
||||||
|
fs: types.FieldSpec{Path: "metadata/name"},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
"prefix-suffix": {
|
||||||
name: "prefix-suffix",
|
|
||||||
input: `
|
input: `
|
||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
kind: Foo
|
kind: Foo
|
||||||
@@ -94,10 +92,10 @@ metadata:
|
|||||||
name: bar-instance-foo
|
name: bar-instance-foo
|
||||||
`,
|
`,
|
||||||
filter: prefixsuffix.Filter{Prefix: "bar-", Suffix: "-foo"},
|
filter: prefixsuffix.Filter{Prefix: "bar-", Suffix: "-foo"},
|
||||||
|
fs: types.FieldSpec{Path: "metadata/name"},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
"data-fieldspecs": {
|
||||||
name: "data-fieldspecs",
|
|
||||||
input: `
|
input: `
|
||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
kind: Foo
|
kind: Foo
|
||||||
@@ -119,7 +117,7 @@ a:
|
|||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
kind: Foo
|
kind: Foo
|
||||||
metadata:
|
metadata:
|
||||||
name: foo-instance
|
name: instance
|
||||||
a:
|
a:
|
||||||
b:
|
b:
|
||||||
c: foo-d
|
c: foo-d
|
||||||
@@ -127,35 +125,28 @@ a:
|
|||||||
apiVersion: example.com/v1
|
apiVersion: example.com/v1
|
||||||
kind: Bar
|
kind: Bar
|
||||||
metadata:
|
metadata:
|
||||||
name: foo-instance
|
name: instance
|
||||||
a:
|
a:
|
||||||
b:
|
b:
|
||||||
c: foo-d
|
c: foo-d
|
||||||
`,
|
`,
|
||||||
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
||||||
fsslice: []types.FieldSpec{
|
fs: types.FieldSpec{Path: "a/b/c"},
|
||||||
{
|
|
||||||
Path: "a/b/c",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
name string
|
|
||||||
input string
|
input string
|
||||||
expected string
|
expected string
|
||||||
filter prefixsuffix.Filter
|
filter prefixsuffix.Filter
|
||||||
fsslice types.FsSlice
|
fs types.FieldSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = builtinconfig.MakeDefaultConfig()
|
|
||||||
|
|
||||||
func TestFilter(t *testing.T) {
|
func TestFilter(t *testing.T) {
|
||||||
for i := range tests {
|
for name := range tests {
|
||||||
test := tests[i]
|
test := tests[name]
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
test.filter.FsSlice = append(config.NamePrefix, test.fsslice...)
|
test.filter.FieldSpec = test.fs
|
||||||
if !assert.Equal(t,
|
if !assert.Equal(t,
|
||||||
strings.TrimSpace(test.expected),
|
strings.TrimSpace(test.expected),
|
||||||
strings.TrimSpace(
|
strings.TrimSpace(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package replicacount
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -45,5 +46,5 @@ func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rc Filter) set(node *yaml.RNode) error {
|
func (rc Filter) set(node *yaml.RNode) error {
|
||||||
return fsslice.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node)
|
return filtersutil.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/transform"
|
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||||
"sigs.k8s.io/kustomize/api/resid"
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,20 +19,17 @@ import (
|
|||||||
type plugin struct {
|
type plugin struct {
|
||||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||||
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||||
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
FieldSpecs types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection GoUnusedGlobalVariable
|
//noinspection GoUnusedGlobalVariable
|
||||||
var KustomizePlugin plugin
|
var KustomizePlugin plugin
|
||||||
|
|
||||||
// Not placed in a file yet due to lack of demand.
|
// A Gvk skip list for prefix/suffix modification.
|
||||||
var prefixSuffixFieldSpecsToSkip = []types.FieldSpec{
|
// hard coded for now - eventually should be part of config.
|
||||||
{
|
var prefixSuffixFieldSpecsToSkip = types.FsSlice{
|
||||||
Gvk: resid.Gvk{Kind: "CustomResourceDefinition"},
|
{Gvk: resid.Gvk{Kind: "CustomResourceDefinition"}},
|
||||||
},
|
{Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"}},
|
||||||
{
|
|
||||||
Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Config(
|
func (p *plugin) Config(
|
||||||
@@ -52,29 +48,24 @@ func (p *plugin) Config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Transform(m resmap.ResMap) error {
|
func (p *plugin) Transform(m resmap.ResMap) error {
|
||||||
|
|
||||||
// Even if both the Prefix and Suffix are empty we want
|
// Even if both the Prefix and Suffix are empty we want
|
||||||
// to proceed with the transformation. This allows to add contextual
|
// to proceed with the transformation. This allows to add contextual
|
||||||
// information to the resources (AddNamePrefix and AddNameSuffix).
|
// information to the resources (AddNamePrefix and AddNameSuffix).
|
||||||
|
|
||||||
for _, r := range m.Resources() {
|
for _, r := range m.Resources() {
|
||||||
|
// TODO: move this test into the filter (i.e. make a better filter)
|
||||||
if p.shouldSkip(r.OrgId()) {
|
if p.shouldSkip(r.OrgId()) {
|
||||||
// Don't change the actual definition
|
|
||||||
// of a CRD.
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
id := r.OrgId()
|
id := r.OrgId()
|
||||||
// current default configuration contains
|
// current default configuration contains
|
||||||
// only one entry: "metadata/name" with no GVK
|
// only one entry: "metadata/name" with no GVK
|
||||||
for _, path := range p.FieldSpecs {
|
for _, fs := range p.FieldSpecs {
|
||||||
if !id.IsSelected(&path.Gvk) {
|
// TODO: this is redundant to filter (but needed for now)
|
||||||
// With the currrent default configuration,
|
if !id.IsSelected(&fs.Gvk) {
|
||||||
// because no Gvk is specified, so a wild
|
|
||||||
// card
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// TODO: move this test into the filter.
|
||||||
if smellsLikeANameChange(&path) {
|
if smellsLikeANameChange(&fs) {
|
||||||
// "metadata/name" is the only field.
|
// "metadata/name" is the only field.
|
||||||
// this will add a prefix and a suffix
|
// this will add a prefix and a suffix
|
||||||
// to the resource even if those are
|
// to the resource even if those are
|
||||||
@@ -82,15 +73,11 @@ func (p *plugin) Transform(m resmap.ResMap) error {
|
|||||||
r.AddNamePrefix(p.Prefix)
|
r.AddNamePrefix(p.Prefix)
|
||||||
r.AddNameSuffix(p.Suffix)
|
r.AddNameSuffix(p.Suffix)
|
||||||
}
|
}
|
||||||
|
err := filtersutil.ApplyToJSON(prefixsuffix.Filter{
|
||||||
// the addPrefixSuffix method will not
|
Prefix: p.Prefix,
|
||||||
// change the name if both the prefix and suffix
|
Suffix: p.Suffix,
|
||||||
// are empty.
|
FieldSpec: fs,
|
||||||
err := transform.MutateField(
|
}, r)
|
||||||
r.Map(),
|
|
||||||
path.PathSlice(),
|
|
||||||
path.CreateIfNotPresent,
|
|
||||||
p.addPrefixSuffix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -103,8 +90,7 @@ func smellsLikeANameChange(fs *types.FieldSpec) bool {
|
|||||||
return fs.Path == "metadata/name"
|
return fs.Path == "metadata/name"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) shouldSkip(
|
func (p *plugin) shouldSkip(id resid.ResId) bool {
|
||||||
id resid.ResId) bool {
|
|
||||||
for _, path := range prefixSuffixFieldSpecsToSkip {
|
for _, path := range prefixSuffixFieldSpecsToSkip {
|
||||||
if id.IsSelected(&path.Gvk) {
|
if id.IsSelected(&path.Gvk) {
|
||||||
return true
|
return true
|
||||||
@@ -112,12 +98,3 @@ func (p *plugin) shouldSkip(
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) addPrefixSuffix(
|
|
||||||
in interface{}) (interface{}, error) {
|
|
||||||
s, ok := in.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s%s%s", p.Prefix, s, p.Suffix), nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ go 1.14
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
sigs.k8s.io/kustomize/api v0.5.1
|
sigs.k8s.io/kustomize/api v0.5.1
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.4.1
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user