add replacement filter to support replacmenttransformer

This commit is contained in:
Natasha Sarkar
2021-03-17 13:51:31 -07:00
parent 710db98dbf
commit fa0b237178
14 changed files with 722 additions and 103 deletions

View File

@@ -0,0 +1,4 @@
// Package replacement contains a kio.Filter implementation of the kustomize
// replacement transformer (accepts sources and looks for targets to replace
// their values with values from the sources).
package replacement

View File

@@ -0,0 +1,68 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package replacement
import (
"bytes"
"log"
"os"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func ExampleFilter() {
f := Filter{}
err := yaml.Unmarshal([]byte(`
replacements:
- source:
kind: Foo2
fieldPath: spec.replicas
targets:
- select:
kind: Foo1
fieldPaths:
- spec.replicas`), &f)
if err != nil {
log.Fatal(err)
}
err = kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
apiVersion: example.com/v1
kind: Foo1
metadata:
name: instance
spec:
replicas: 3
---
apiVersion: example.com/v1
kind: Foo2
metadata:
name: instance
spec:
replicas: 99
`)}},
Filters: []kio.Filter{f},
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
}.Execute()
if err != nil {
log.Fatal(err)
}
// Output:
// apiVersion: example.com/v1
// kind: Foo1
// metadata:
// name: instance
// spec:
// replicas: 99
// ---
// apiVersion: example.com/v1
// kind: Foo2
// metadata:
// name: instance
// spec:
// replicas: 99
}

View File

@@ -0,0 +1,123 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package replacement
import (
"fmt"
"strings"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type Filter struct {
Replacements []types.Replacement
}
// Filter replaces values of targets with values from sources
// TODO (#3492): Connect this to a replacement transformer plugin
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, r := range f.Replacements {
if r.Source == nil || r.Targets == nil {
return nil, fmt.Errorf("replacements must specify a source and at least one target")
}
value, err := getReplacement(nodes, &r)
if err != nil {
return nil, err
}
nodes, err = applyReplacement(nodes, value, r.Targets)
if err != nil {
return nil, err
}
}
return nodes, nil
}
func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.TargetSelector) ([]*yaml.RNode, error) {
for _, t := range targets {
if len(t.FieldPaths) == 0 {
t.FieldPaths = []string{types.DefaultReplacementFieldPath}
}
for _, n := range nodes {
// TODO (#3492): Don't include matches listed in the `reject` field
if t.Select.KrmId.Match(getKrmId(n)) {
err := applyToNode(n, value, t)
if err != nil {
return nil, err
}
}
}
}
return nodes, nil
}
func applyToNode(node *yaml.RNode, value *yaml.RNode, target *types.TargetSelector) error {
if target.Select == nil {
return fmt.Errorf("target must specify resources to select")
}
for _, fp := range target.FieldPaths {
fieldPath := strings.Split(fp, ".")
// TODO (#3492): Add tests for map keys in the fieldPath (e.g. .spec.containers[name=nginx])
t, err := node.Pipe(yaml.Lookup(fieldPath...))
if err != nil {
return err
}
if t != nil {
// TODO (#3492): Use the field options to refine interpretation of the field
t.SetYNode(value.YNode())
}
}
return nil
}
func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, error) {
source, err := selectSourceNode(nodes, r.Source)
if err != nil {
return nil, err
}
if r.Source.FieldPath == "" {
r.Source.FieldPath = types.DefaultReplacementFieldPath
}
fieldPath := strings.Split(r.Source.FieldPath, ".")
// TODO (#3492): Add tests for map keys in the fieldPath (e.g. .spec.containers[name=nginx])
rn, err := source.Pipe(yaml.Lookup(fieldPath...))
if err != nil {
return nil, err
}
// TODO (#3492): Use the field options to refine interpretation of the field
return rn, nil
}
// selectSourceNode finds the node that matches the selector, returning
// an error if multiple or none are found
func selectSourceNode(nodes []*yaml.RNode, selector *types.SourceSelector) (*yaml.RNode, error) {
var matches []*yaml.RNode
for _, n := range nodes {
if selector.KrmId.Match(getKrmId(n)) {
if len(matches) > 0 {
return nil, fmt.Errorf("more than one match for source %v", selector)
}
matches = append(matches, n)
}
}
if len(matches) == 0 {
return nil, fmt.Errorf("found no matches for source %v", selector)
}
return matches[0], nil
}
func getKrmId(n *yaml.RNode) *types.KrmId {
ns, _ := n.GetNamespace()
apiVersion := yaml.GetValue(n.Field(yaml.APIVersionField).Value)
group, version := resid.ParseGroupVersion(apiVersion)
return &types.KrmId{
Gvk: resid.Gvk{Group: group, Version: version, Kind: n.GetKind()},
Name: n.GetName(),
Namespace: ns,
}
}

View File

@@ -0,0 +1,336 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package replacement
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/yaml"
)
func TestFilter(t *testing.T) {
testCases := map[string]struct {
input string
replacements string
expected string
expectedErr bool
}{
"simple": {
input: `apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
replacements: `replacements:
- source:
kind: Deployment
name: deploy2
fieldPath: spec.template.spec.containers.0.image
targets:
- select:
kind: Deployment
name: deploy1
fieldPaths:
- spec.template.spec.containers.1.image
`,
expected: `apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:1.7.9
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
},
"complex type": {
input: `apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy3
spec:
template:
spec:
containers: {}
`,
replacements: `replacements:
- source:
kind: Pod
name: pod
fieldPath: spec.containers
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers
`,
expected: `apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy3
spec:
template:
spec:
containers:
- image: busybox
name: myapp-container
`,
},
"from ConfigMap": {
input: `apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
labels:
foo: bar
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: command-demo-container
image: debian
command: ["printenv"]
args:
- HOSTNAME
- PORT
- name: busybox
image: busybox:latest
args:
- echo
- HOSTNAME
- PORT
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
data:
HOSTNAME: example.com
PORT: 8080
`,
replacements: `replacements:
- source:
kind: ConfigMap
name: cm
fieldPath: data.HOSTNAME
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.0.args.0
- spec.template.spec.containers.1.args.1
- source:
kind: ConfigMap
name: cm
fieldPath: data.PORT
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec.containers.0.args.1
- spec.template.spec.containers.1.args.2
`,
expected: `apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
labels:
foo: bar
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: command-demo-container
image: debian
command: ["printenv"]
args:
- example.com
- 8080
- name: busybox
image: busybox:latest
args:
- echo
- example.com
- 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
data:
HOSTNAME: example.com
PORT: 8080
`,
},
"multiple matches for source select": {
input: `apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
---
apiVersion: v1
kind: Deployment
metadata:
name: deploy3
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
replacements: `replacements:
- source:
kind: Deployment
targets:
- select:
kind: Deployment
`,
expectedErr: true,
},
"replacement has no source": {
input: `apiVersion: v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: postgres:1.8.0
name: postgresdb
`,
replacements: `replacements:
- targets:
- select:
kind: Deployment
`,
expectedErr: true,
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
f := Filter{}
err := yaml.Unmarshal([]byte(tc.replacements), &f)
assert.NoError(t, err)
actual, err := filtertest.RunFilterE(t, tc.input, f)
assert.Equal(t, tc.expectedErr, err != nil)
if !tc.expectedErr &&
!assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual)) {
t.FailNow()
}
})
}
}

View File

@@ -4,8 +4,9 @@ import (
"context" "context"
"log" "log"
"os" "os"
server "sigs.k8s.io/kustomize/api/internal/crawl/backend"
"strconv" "strconv"
server "sigs.k8s.io/kustomize/api/internal/crawl/backend"
) )
func main() { func main() {

View File

@@ -81,7 +81,7 @@ func (gc githubCrawler) Crawl(ctx context.Context,
output chan<- crawler.CrawledDocument, seen utils.SeenMap) error { output chan<- crawler.CrawledDocument, seen utils.SeenMap) error {
ranges := []RangeWithin{ ranges := []RangeWithin{
RangeWithin{ {
start: uint64(0), start: uint64(0),
end: githubMaxFileSize, end: githubMaxFileSize,
}, },

View File

@@ -96,6 +96,6 @@ func TestRangeSizes(t *testing.T) {
returnedResult := RangeSizes(s) returnedResult := RangeSizes(s)
expectedResult := RangeWithin{uint64(2365), uint64(10000)} expectedResult := RangeWithin{uint64(2365), uint64(10000)}
if !reflect.DeepEqual(returnedResult, expectedResult) { if !reflect.DeepEqual(returnedResult, expectedResult) {
t.Errorf("RangeSizes expected (%v), got (%v)",expectedResult, returnedResult) t.Errorf("RangeSizes expected (%v), got (%v)", expectedResult, returnedResult)
} }
} }

View File

@@ -61,13 +61,13 @@ func TestFindPatchTargets(t *testing.T) {
}{ }{
"select_01": { "select_01": {
target: types.Selector{ target: types.Selector{
Name: "name.*", KrmId: types.KrmId{Name: "name.*"},
}, },
count: 3, count: 3,
}, },
"select_02": { "select_02": {
target: types.Selector{ target: types.Selector{
Name: "name.*", KrmId: types.KrmId{Name: "name.*"},
AnnotationSelector: "foo=bar", AnnotationSelector: "foo=bar",
}, },
count: 2, count: 2,
@@ -80,98 +80,102 @@ func TestFindPatchTargets(t *testing.T) {
}, },
"select_04": { "select_04": {
target: types.Selector{ target: types.Selector{
KrmId: types.KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "Kind1", Kind: "Kind1",
}, },
Name: "name.*", Name: "name.*",
}, },
},
count: 2, count: 2,
}, },
"select_05": { "select_05": {
target: types.Selector{ target: types.Selector{
Name: "NotMatched", KrmId: types.KrmId{Name: "NotMatched"},
}, },
count: 0, count: 0,
}, },
"select_06": { "select_06": {
target: types.Selector{ target: types.Selector{
Name: "", KrmId: types.KrmId{Name: ""},
}, },
count: 4, count: 4,
}, },
"select_07": { "select_07": {
target: types.Selector{ target: types.Selector{
Namespace: "default", KrmId: types.KrmId{Namespace: "default"},
}, },
count: 2, count: 2,
}, },
"select_08": { "select_08": {
target: types.Selector{ target: types.Selector{
Namespace: "", KrmId: types.KrmId{Namespace: ""},
}, },
count: 4, count: 4,
}, },
"select_09": { "select_09": {
target: types.Selector{ target: types.Selector{
KrmId: types.KrmId{
Namespace: "default", Namespace: "default",
Name: "name.*", Name: "name.*",
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "Kind1", Kind: "Kind1",
}, },
}, },
},
count: 1, count: 1,
}, },
"select_10": { "select_10": {
target: types.Selector{ target: types.Selector{
Name: "^name.*", KrmId: types.KrmId{Name: "^name.*"},
}, },
count: 3, count: 3,
}, },
"select_11": { "select_11": {
target: types.Selector{ target: types.Selector{
Name: "name.*$", KrmId: types.KrmId{Name: "name.*$"},
}, },
count: 3, count: 3,
}, },
"select_12": { "select_12": {
target: types.Selector{ target: types.Selector{
Name: "^name.*$", KrmId: types.KrmId{Name: "^name.*$"},
}, },
count: 3, count: 3,
}, },
"select_13": { "select_13": {
target: types.Selector{ target: types.Selector{
Namespace: "^def.*", KrmId: types.KrmId{Namespace: "^def.*"},
}, },
count: 2, count: 2,
}, },
"select_14": { "select_14": {
target: types.Selector{ target: types.Selector{
Namespace: "def.*$", KrmId: types.KrmId{Namespace: "def.*$"},
}, },
count: 2, count: 2,
}, },
"select_15": { "select_15": {
target: types.Selector{ target: types.Selector{
Namespace: "^def.*$", KrmId: types.KrmId{Namespace: "^def.*$"},
}, },
count: 2, count: 2,
}, },
"select_16": { "select_16": {
target: types.Selector{ target: types.Selector{
Namespace: "default", KrmId: types.KrmId{Namespace: "default"},
}, },
count: 2, count: 2,
}, },
"select_17": { "select_17": {
target: types.Selector{ target: types.Selector{
Namespace: "NotMatched", KrmId: types.KrmId{Namespace: "NotMatched"},
}, },
count: 0, count: 0,
}, },
"select_18": { "select_18": {
target: types.Selector{ target: types.Selector{
Namespace: "ns1", KrmId: types.KrmId{Namespace: "ns1"},
}, },
count: 1, count: 1,
}, },

View File

@@ -9,6 +9,7 @@ import (
func TestPatchEquals(t *testing.T) { func TestPatchEquals(t *testing.T) {
selector := Selector{ selector := Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -16,6 +17,7 @@ func TestPatchEquals(t *testing.T) {
}, },
Name: "name", Name: "name",
Namespace: "namespace", Namespace: "namespace",
},
LabelSelector: "selector", LabelSelector: "selector",
AnnotationSelector: "selector", AnnotationSelector: "selector",
} }
@@ -38,6 +40,7 @@ func TestPatchEquals(t *testing.T) {
Path: "foo", Path: "foo",
Patch: "bar", Patch: "bar",
Target: &Selector{ Target: &Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -45,6 +48,7 @@ func TestPatchEquals(t *testing.T) {
}, },
Name: "name", Name: "name",
Namespace: "namespace", Namespace: "namespace",
},
LabelSelector: "selector", LabelSelector: "selector",
AnnotationSelector: "selector", AnnotationSelector: "selector",
}, },
@@ -53,6 +57,7 @@ func TestPatchEquals(t *testing.T) {
Path: "foo", Path: "foo",
Patch: "bar", Patch: "bar",
Target: &Selector{ Target: &Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -60,6 +65,7 @@ func TestPatchEquals(t *testing.T) {
}, },
Name: "name", Name: "name",
Namespace: "namespace", Namespace: "namespace",
},
LabelSelector: "selector", LabelSelector: "selector",
AnnotationSelector: "selector", AnnotationSelector: "selector",
}, },

View File

@@ -1,27 +1,62 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package types package types
const DefaultReplacementFieldPath = "metadata.name"
// Replacement defines how to perform a substitution // Replacement defines how to perform a substitution
// where it is from and where it is to. // where it is from and where it is to.
type Replacement struct { type Replacement struct {
Source *ReplSource `json:"source" yaml:"source"` // The source of the value.
Target *ReplTarget `json:"target" yaml:"target"` Source *SourceSelector `json:"source" yaml:"source"`
// The N fields to write the value to.
Targets []*TargetSelector `json:"targets" yaml:"targets"`
} }
// ReplSource defines where a substitution is from // SourceSelector is the source of the replacement transformer.
// It can from two different kinds of sources type SourceSelector struct {
// - from a field of one resource // A specific object to read it from.
// - from a string KrmId `json:",inline,omitempty" yaml:",inline,omitempty"`
type ReplSource struct {
ObjRef *Target `json:"objref,omitempty" yaml:"objref,omitempty"` // Structured field path expected in the allowed object.
FieldRef string `json:"fieldref,omitempty" yaml:"fiedldref,omitempty"` FieldPath string `json:"fieldPath" yaml:"fieldPath"`
Value string `json:"value,omitempty" yaml:"value,omitempty"`
// Used to refine the interpretation of the field
Options *FieldOptions `json:"options" yaml:"options"`
} }
// ReplTarget defines where a substitution is to. // TargetSelector specifies fields in one or more objects.
type ReplTarget struct { type TargetSelector struct {
ObjRef *Selector `json:"objref,omitempty" yaml:"objref,omitempty"` // Include objects that match this.
FieldRefs []string `json:"fieldrefs,omitempty" yaml:"fieldrefs,omitempty"` Select *Selector `json:"select" yaml:"select"`
// From the allowed set, remove objects that match this.
// TODO (#3492): Remove matches listed in the exclude field
// Currently this field is unused
Reject *Selector `json:"reject" yaml:"reject"`
// Structured field paths expected in each allowed object.
FieldPaths []string `json:"fieldPaths" yaml:"fieldPaths"`
// Used to refine the interpretation of the field
Options *FieldOptions `json:"options" yaml:"options"`
}
// FieldPath is a structured field path to the desired object
// TODO (#3492): Implement use of these options, they are
// currently used
type FieldOptions struct {
// Used to split/join the field.
Delimiter string `json:"delimiter" yaml:"delimiter"`
// Which position in the split to consider.
Index int `json:"index" yaml:"index"`
// None, Base64, URL, Hex, etc
Encoding string `json:"encoding" yaml:"index"`
// If field missing, add it
Create bool `json:"create" yaml:"create"`
} }

View File

@@ -13,9 +13,8 @@ import (
// Any resource that matches intersection of all conditions // Any resource that matches intersection of all conditions
// is included in this set. // is included in this set.
type Selector struct { type Selector struct {
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` // KrmId refers to a GVKN/Ns of a resource.
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` KrmId `json:",inline,omitempty" yaml:",inline,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// AnnotationSelector is a string that follows the label selection expression // AnnotationSelector is a string that follows the label selection expression
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
@@ -28,6 +27,23 @@ type Selector struct {
LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"` LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"`
} }
// KrmId refers to a GVKN/Ns of a resource.
type KrmId struct {
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}
// Match returns true if id selects other, i.e. id's fields
// either match other's or are empty
func (id *KrmId) Match(other *KrmId) bool {
return (id.Group == "" || id.Group == other.Group) &&
(id.Version == "" || id.Version == other.Version) &&
(id.Kind == "" || id.Kind == other.Kind) &&
(id.Name == "" || id.Name == other.Name) &&
(id.Namespace == "" || id.Namespace == other.Namespace)
}
// SelectorRegex is a Selector with regex in GVK // SelectorRegex is a Selector with regex in GVK
// Any resource that matches intersection of all conditions // Any resource that matches intersection of all conditions
// is included in this set. // is included in this set.

View File

@@ -15,12 +15,14 @@ func TestSelectorRegexMatchGvk(t *testing.T) {
}{ }{
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
Kind: "kind", Kind: "kind",
}, },
}, },
},
G: resid.Gvk{ G: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -30,12 +32,14 @@ func TestSelectorRegexMatchGvk(t *testing.T) {
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "", Version: "",
Kind: "", Kind: "",
}, },
}, },
},
G: resid.Gvk{ G: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -45,12 +49,14 @@ func TestSelectorRegexMatchGvk(t *testing.T) {
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
Kind: "kind", Kind: "kind",
}, },
}, },
},
G: resid.Gvk{ G: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -60,12 +66,14 @@ func TestSelectorRegexMatchGvk(t *testing.T) {
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
Kind: "kind", Kind: "kind",
}, },
}, },
},
G: resid.Gvk{ G: resid.Gvk{
Group: "group", Group: "group",
Version: "version", Version: "version",
@@ -75,12 +83,14 @@ func TestSelectorRegexMatchGvk(t *testing.T) {
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "g.*", Group: "g.*",
Version: "\\d+", Version: "\\d+",
Kind: ".{4}", Kind: ".{4}",
}, },
}, },
},
G: resid.Gvk{ G: resid.Gvk{
Group: "group", Group: "group",
Version: "123", Version: "123",
@@ -90,12 +100,14 @@ func TestSelectorRegexMatchGvk(t *testing.T) {
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Group: "g.*", Group: "g.*",
Version: "\\d+", Version: "\\d+",
Kind: ".{4}", Kind: ".{4}",
}, },
}, },
},
G: resid.Gvk{ G: resid.Gvk{
Group: "group", Group: "group",
Version: "123", Version: "123",
@@ -125,31 +137,39 @@ func TestSelectorRegexMatchName(t *testing.T) {
}{ }{
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Name: "foo", Name: "foo",
Namespace: "bar", Namespace: "bar",
}, },
},
Name: "foo", Name: "foo",
Expected: true, Expected: true,
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Name: "foo", Name: "foo",
Namespace: "bar", Namespace: "bar",
}, },
},
Name: "bar", Name: "bar",
Expected: false, Expected: false,
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Name: "f.*", Name: "f.*",
}, },
},
Name: "foo", Name: "foo",
Expected: true, Expected: true,
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Name: "b.*", Name: "b.*",
}, },
},
Name: "foo", Name: "foo",
Expected: false, Expected: false,
}, },
@@ -174,31 +194,39 @@ func TestSelectorRegexMatchNamespace(t *testing.T) {
}{ }{
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Name: "bar", Name: "bar",
Namespace: "foo", Namespace: "foo",
}, },
},
Namespace: "foo", Namespace: "foo",
Expected: true, Expected: true,
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Name: "foo", Name: "foo",
Namespace: "bar", Namespace: "bar",
}, },
},
Namespace: "foo", Namespace: "foo",
Expected: false, Expected: false,
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Namespace: "f.*", Namespace: "f.*",
}, },
},
Namespace: "foo", Namespace: "foo",
Expected: true, Expected: true,
}, },
{ {
S: Selector{ S: Selector{
KrmId: KrmId{
Namespace: "b.*", Namespace: "b.*",
}, },
},
Namespace: "foo", Namespace: "foo",
Expected: false, Expected: false,
}, },

View File

@@ -12,8 +12,6 @@ import (
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
) )
const defaultFieldPath = "metadata.name"
// Var represents a variable whose value will be sourced // Var represents a variable whose value will be sourced
// from a field in a Kubernetes object. // from a field in a Kubernetes object.
type Var struct { type Var struct {
@@ -71,7 +69,7 @@ type FieldSelector struct {
// defaulting sets reference to field used by default. // defaulting sets reference to field used by default.
func (v *Var) Defaulting() { func (v *Var) Defaulting() {
if v.FieldRef.FieldPath == "" { if v.FieldRef.FieldPath == "" {
v.FieldRef.FieldPath = defaultFieldPath v.FieldRef.FieldPath = DefaultReplacementFieldPath
} }
v.ObjRef.GVK() v.ObjRef.GVK()
} }

View File

@@ -69,9 +69,9 @@ func TestDefaulting(t *testing.T) {
}, },
} }
v.Defaulting() v.Defaulting()
if v.FieldRef.FieldPath != defaultFieldPath { if v.FieldRef.FieldPath != DefaultReplacementFieldPath {
t.Fatalf("expected %s, got %v", t.Fatalf("expected %s, got %v",
defaultFieldPath, v.FieldRef.FieldPath) DefaultReplacementFieldPath, v.FieldRef.FieldPath)
} }
} }
@@ -127,7 +127,7 @@ func TestVarSet(t *testing.T) {
t.Fatalf("expected var") t.Fatalf("expected var")
} }
// Confirm defaulting. // Confirm defaulting.
if v.FieldRef.FieldPath != defaultFieldPath { if v.FieldRef.FieldPath != DefaultReplacementFieldPath {
t.Fatalf("unexpected field path: %v", v.FieldRef.FieldPath) t.Fatalf("unexpected field path: %v", v.FieldRef.FieldPath)
} }
// Confirm sorting. // Confirm sorting.