mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Merge branch 'master' of https://github.com/kubernetes-sigs/kustomize
This commit is contained in:
@@ -29,10 +29,11 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
func(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
for _, k := range keys {
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: filtersutil.SetEntry(k, f.Annotations[k], yaml.StringTag),
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: filtersutil.SetEntry(
|
||||
k, f.Annotations[k], yaml.NodeTagString),
|
||||
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
|
||||
CreateTag: "!!map", // TODO: change to yaml.NodeTagMap
|
||||
CreateTag: yaml.NodeTagMap,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func (fltr Filter) field(obj *yaml.RNode) error {
|
||||
// create the field if it is missing: must be a mapping node
|
||||
lookupField = yaml.LookupCreate(yaml.MappingNode, fieldName)
|
||||
kind = yaml.MappingNode
|
||||
tag = "!!map" // TODO: change to yaml.NodeTagMap
|
||||
tag = yaml.NodeTagMap
|
||||
}
|
||||
|
||||
// locate (or maybe create) the field
|
||||
@@ -97,7 +97,7 @@ func (fltr Filter) field(obj *yaml.RNode) error {
|
||||
|
||||
// if the value exists, but is null, then change it to the creation type
|
||||
// TODO: update yaml.LookupCreate to support this
|
||||
if field.YNode().Tag == "!!null" { // TODO: change to yaml.NodeTagNull
|
||||
if field.YNode().Tag == yaml.NodeTagNull {
|
||||
field.YNode().Kind = kind
|
||||
field.YNode().Tag = tag
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func SetEntry(key, value, tag string) SetFn {
|
||||
Value: value,
|
||||
Tag: tag,
|
||||
}
|
||||
if tag == yaml.StringTag && yaml.IsYaml1_1NonString(n) {
|
||||
if tag == yaml.NodeTagString && yaml.IsYaml1_1NonString(n) {
|
||||
n.Style = yaml.DoubleQuotedStyle
|
||||
}
|
||||
return func(node *yaml.RNode) error {
|
||||
|
||||
@@ -30,10 +30,11 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
func(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
for _, k := range keys {
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: filtersutil.SetEntry(k, f.Labels[k], yaml.StringTag),
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: filtersutil.SetEntry(
|
||||
k, f.Labels[k], yaml.NodeTagString),
|
||||
CreateKind: yaml.MappingNode, // Labels are MappingNodes.
|
||||
CreateTag: "!!map", // TODO: change to yaml.NodeTagMap
|
||||
CreateTag: yaml.NodeTagMap,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
3
api/filters/nameref/doc.go
Normal file
3
api/filters/nameref/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package nameref contains a kio.Filter implementation of the kustomize
|
||||
// name reference transformer.
|
||||
package nameref
|
||||
214
api/filters/nameref/nameref.go
Normal file
214
api/filters/nameref/nameref.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package nameref
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter will update the name reference
|
||||
type Filter struct {
|
||||
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||
Referrer *resource.Resource
|
||||
Target resid.Gvk
|
||||
ReferralCandidates resmap.ResMap
|
||||
}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
err := node.PipeE(fieldspec.Filter{
|
||||
FieldSpec: f.FieldSpec,
|
||||
SetValue: f.set,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
func (f Filter) set(node *yaml.RNode) error {
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
return nil
|
||||
}
|
||||
switch node.YNode().Kind {
|
||||
case yaml.ScalarNode:
|
||||
return f.setScalar(node)
|
||||
case yaml.MappingNode:
|
||||
// Kind: ValidatingWebhookConfiguration
|
||||
// FieldSpec is webhooks/clientConfig/service
|
||||
return f.setMapping(node)
|
||||
case yaml.SequenceNode:
|
||||
return f.setSequence(node)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"node is expected to be either a string or a slice of string or a map of string")
|
||||
}
|
||||
}
|
||||
|
||||
func (f Filter) setSequence(node *yaml.RNode) error {
|
||||
return applyFilterToSeq(seqFilter{
|
||||
setScalarFn: f.setScalar,
|
||||
setMappingFn: f.setMapping,
|
||||
}, node)
|
||||
}
|
||||
|
||||
func (f Filter) setMapping(node *yaml.RNode) error {
|
||||
return setNameAndNs(
|
||||
node,
|
||||
f.Referrer,
|
||||
f.Target,
|
||||
f.ReferralCandidates,
|
||||
)
|
||||
}
|
||||
|
||||
func (f Filter) setScalar(node *yaml.RNode) error {
|
||||
newValue, err := getSimpleNameField(
|
||||
node.YNode().Value,
|
||||
f.Referrer,
|
||||
f.Target,
|
||||
f.ReferralCandidates,
|
||||
f.ReferralCandidates.Resources(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = filtersutil.SetScalar(newValue)(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// selectReferral picks the referral among a subset of candidates.
|
||||
// It returns the current name and namespace of the selected candidate.
|
||||
// Note that the content of the referricalCandidateSubset slice is most of the time
|
||||
// identical to the referralCandidates resmap. Still in some cases, such
|
||||
// as ClusterRoleBinding, the subset only contains the resources of a specific
|
||||
// namespace.
|
||||
func selectReferral(
|
||||
oldName string,
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap,
|
||||
referralCandidateSubset []*resource.Resource) (string, string, error) {
|
||||
|
||||
for _, res := range referralCandidateSubset {
|
||||
id := res.OrgId()
|
||||
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
|
||||
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
|
||||
// If there's more than one match, there's no way
|
||||
// to know which one to pick, so emit error.
|
||||
if len(matches) > 1 {
|
||||
return "", "", fmt.Errorf(
|
||||
"multiple matches for %s:\n %v",
|
||||
id, getIds(matches))
|
||||
}
|
||||
// In the resource, note that it is referenced
|
||||
// by the referrer.
|
||||
res.AppendRefBy(referrer.CurId())
|
||||
// Return transformed name of the object,
|
||||
// complete with prefixes, hashes, etc.
|
||||
return res.GetName(), res.GetNamespace(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return oldName, "", nil
|
||||
}
|
||||
|
||||
// utility function to replace a simple string by the new name
|
||||
func getSimpleNameField(
|
||||
oldName string,
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap,
|
||||
referralCandidateSubset []*resource.Resource) (string, error) {
|
||||
|
||||
newName, _, err := selectReferral(oldName, referrer, target,
|
||||
referralCandidates, referralCandidateSubset)
|
||||
|
||||
return newName, err
|
||||
}
|
||||
|
||||
func getIds(rs []*resource.Resource) []string {
|
||||
var result []string
|
||||
for _, r := range rs {
|
||||
result = append(result, r.CurId().String()+"\n")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// utility function to replace name field within a map RNode
|
||||
// and leverage the namespace field.
|
||||
func setNameAndNs(
|
||||
in *yaml.RNode,
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap) error {
|
||||
|
||||
if in.YNode().Kind != yaml.MappingNode {
|
||||
return fmt.Errorf("expect a mapping node")
|
||||
}
|
||||
|
||||
// Get name field
|
||||
nameNode, err := in.Pipe(yaml.FieldMatcher{
|
||||
Name: "name",
|
||||
})
|
||||
if err != nil || nameNode == nil {
|
||||
return fmt.Errorf("cannot find field 'name' in node")
|
||||
}
|
||||
|
||||
// Get namespace field
|
||||
namespaceNode, err := in.Pipe(yaml.FieldMatcher{
|
||||
Name: "namespace",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error when find field 'namespace'")
|
||||
}
|
||||
|
||||
// check is namespace matched
|
||||
// name will bot be updated if the namespace doesn't match
|
||||
subset := referralCandidates.Resources()
|
||||
if namespaceNode != nil {
|
||||
namespace := namespaceNode.YNode().Value
|
||||
bynamespace := referralCandidates.GroupedByOriginalNamespace()
|
||||
if _, ok := bynamespace[namespace]; !ok {
|
||||
return nil
|
||||
}
|
||||
subset = bynamespace[namespace]
|
||||
}
|
||||
|
||||
oldName := nameNode.YNode().Value
|
||||
newname, newnamespace, err := selectReferral(oldName, referrer, target,
|
||||
referralCandidates, subset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (newname == oldName) && (newnamespace == "") {
|
||||
// no candidate found.
|
||||
return nil
|
||||
}
|
||||
|
||||
// set name
|
||||
in.Pipe(yaml.FieldSetter{
|
||||
Name: "name",
|
||||
StringValue: newname,
|
||||
})
|
||||
if newnamespace != "" {
|
||||
// We don't want value "" to replace value "default" since
|
||||
// the empty string is handled as a wild card here not default namespace
|
||||
// by kubernetes.
|
||||
in.Pipe(yaml.FieldSetter{
|
||||
Name: "namespace",
|
||||
StringValue: newnamespace,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
333
api/filters/nameref/nameref_test.go
Normal file
333
api/filters/nameref/nameref_test.go
Normal file
@@ -0,0 +1,333 @@
|
||||
package nameref
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestNamerefFilter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
candidates string
|
||||
expected string
|
||||
filter Filter
|
||||
originalNames []string
|
||||
}{
|
||||
"simple scalar": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
ref:
|
||||
name: oldName
|
||||
`,
|
||||
candidates: `
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: NotSecret
|
||||
metadata:
|
||||
name: newName2
|
||||
`,
|
||||
originalNames: []string{"oldName", ""},
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
ref:
|
||||
name: newName
|
||||
`,
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
"sequence": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
seq:
|
||||
- oldName1
|
||||
- oldName2
|
||||
`,
|
||||
candidates: `
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: NotSecret
|
||||
metadata:
|
||||
name: newName2
|
||||
`,
|
||||
originalNames: []string{"oldName1", ""},
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
seq:
|
||||
- newName
|
||||
- oldName2
|
||||
`,
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "seq"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
"mapping": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
map:
|
||||
name: oldName
|
||||
`,
|
||||
candidates: `
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: NotSecret
|
||||
metadata:
|
||||
name: newName2
|
||||
`,
|
||||
originalNames: []string{"oldName", ""},
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
map:
|
||||
name: newName
|
||||
`,
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "map"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
"mapping with namespace": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
map:
|
||||
name: oldName
|
||||
namespace: oldNs
|
||||
`,
|
||||
candidates: `
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName
|
||||
namespace: oldNs
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: NotSecret
|
||||
metadata:
|
||||
name: newName2
|
||||
`,
|
||||
originalNames: []string{"oldName", ""},
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
map:
|
||||
name: newName
|
||||
namespace: oldNs
|
||||
`,
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "map"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
factory := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||
referrer, err := factory.FromBytes([]byte(tc.input))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tc.filter.Referrer = referrer
|
||||
|
||||
resMapFactory := resmap.NewFactory(factory, nil)
|
||||
candidatesRes, err := factory.SliceFromBytesWithNames(
|
||||
tc.originalNames, []byte(tc.candidates))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
candidates := resMapFactory.FromResourceSlice(candidatesRes)
|
||||
tc.filter.ReferralCandidates = candidates
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamerefFilterUnhappy(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
candidates string
|
||||
expected string
|
||||
filter Filter
|
||||
originalNames []string
|
||||
}{
|
||||
"invalid node type": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
ref:
|
||||
name: null
|
||||
`,
|
||||
candidates: "",
|
||||
originalNames: []string{},
|
||||
expected: "obj '' at path 'ref/name': node is expected to be either a string or a slice of string or a map of string",
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
"multiple match": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
ref:
|
||||
name: oldName
|
||||
`,
|
||||
candidates: `
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName2
|
||||
`,
|
||||
originalNames: []string{"oldName", "oldName"},
|
||||
expected: "",
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
"no name": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
ref:
|
||||
notName: oldName
|
||||
`,
|
||||
candidates: `
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: newName2
|
||||
`,
|
||||
originalNames: []string{"oldName", "oldName"},
|
||||
expected: "",
|
||||
filter: Filter{
|
||||
FieldSpec: types.FieldSpec{Path: "ref"},
|
||||
Target: resid.Gvk{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
factory := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||
referrer, err := factory.FromBytes([]byte(tc.input))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tc.filter.Referrer = referrer
|
||||
|
||||
resMapFactory := resmap.NewFactory(factory, nil)
|
||||
candidatesRes, err := factory.SliceFromBytesWithNames(
|
||||
tc.originalNames, []byte(tc.candidates))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
candidates := resMapFactory.FromResourceSlice(candidatesRes)
|
||||
tc.filter.ReferralCandidates = candidates
|
||||
|
||||
_, err = filtertest_test.RunFilterE(t, tc.input, tc.filter)
|
||||
if err == nil {
|
||||
t.Fatalf("expect an error")
|
||||
}
|
||||
if tc.expected != "" && !assert.EqualError(t, err, tc.expected) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
57
api/filters/nameref/seqfilter.go
Normal file
57
api/filters/nameref/seqfilter.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package nameref
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type setFn func(*yaml.RNode) error
|
||||
|
||||
type seqFilter struct {
|
||||
setScalarFn setFn
|
||||
setMappingFn setFn
|
||||
}
|
||||
|
||||
func (sf seqFilter) Filter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
return node, nil
|
||||
}
|
||||
switch node.YNode().Kind {
|
||||
case yaml.ScalarNode:
|
||||
// Kind: Role/ClusterRole
|
||||
// FieldSpec is rules.resourceNames
|
||||
err := sf.setScalarFn(node)
|
||||
return node, err
|
||||
case yaml.MappingNode:
|
||||
// Kind: RoleBinding/ClusterRoleBinding
|
||||
// FieldSpec is subjects
|
||||
// Note: The corresponding fieldSpec had been changed from
|
||||
// from path: subjects/name to just path: subjects. This is
|
||||
// what get mutatefield to request the mapping of the whole
|
||||
// map containing namespace and name instead of just a simple
|
||||
// string field containing the name
|
||||
err := sf.setMappingFn(node)
|
||||
return node, err
|
||||
default:
|
||||
return node, fmt.Errorf(
|
||||
"%#v is expected to be either a string or a map of string", node)
|
||||
}
|
||||
}
|
||||
|
||||
// applyFilterToSeq will apply the filter to each element in the sequence node
|
||||
func applyFilterToSeq(filter yaml.Filter, node *yaml.RNode) error {
|
||||
if node.YNode().Kind != yaml.SequenceNode {
|
||||
return fmt.Errorf("expect a sequence node but got %v", node.YNode().Kind)
|
||||
}
|
||||
|
||||
for _, elem := range node.Content() {
|
||||
rnode := yaml.NewRNode(elem)
|
||||
err := rnode.PipeE(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
80
api/filters/nameref/seqfilter_test.go
Normal file
80
api/filters/nameref/seqfilter_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package nameref
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func SeqFilter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
if node.YNode().Value == "aaa" {
|
||||
node.YNode().SetString("ccc")
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func TestApplyFilterToSeq(t *testing.T) {
|
||||
fltr := yaml.FilterFunc(SeqFilter)
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expect string
|
||||
}{
|
||||
"replace in seq": {
|
||||
input: `
|
||||
- aaa
|
||||
- bbb`,
|
||||
expect: `
|
||||
- ccc
|
||||
- bbb`,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
node, err := yaml.Parse(tc.input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = applyFilterToSeq(fltr, node)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expect),
|
||||
strings.TrimSpace(node.MustString())) {
|
||||
t.Fatalf("expect:\n%s\nactual:\n%s",
|
||||
strings.TrimSpace(tc.expect),
|
||||
strings.TrimSpace(node.MustString()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyFilterToSeqUnhappy(t *testing.T) {
|
||||
fltr := yaml.FilterFunc(SeqFilter)
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
}{
|
||||
"replace in seq": {
|
||||
input: `
|
||||
aaa`,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
node, err := yaml.Parse(tc.input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = applyFilterToSeq(fltr, node)
|
||||
if err == nil {
|
||||
t.Fatalf("expect an error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
FsSlice: ns.FsSlice,
|
||||
SetValue: filtersutil.SetScalar(ns.Namespace),
|
||||
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
||||
CreateTag: yaml.StringTag,
|
||||
CreateTag: yaml.NodeTagString,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (ns Filter) roleBindingHack(obj *yaml.RNode, meta yaml.ResourceMeta) error
|
||||
// Lookup the namespace field on all elements.
|
||||
// We should change the fieldspec so this isn't necessary.
|
||||
obj, err := obj.Pipe(yaml.Lookup(subjectsField))
|
||||
if err != nil || yaml.IsEmpty(obj) {
|
||||
if err != nil || yaml.IsMissingOrNull(obj) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func (ns Filter) roleBindingHack(obj *yaml.RNode, meta yaml.ResourceMeta) error
|
||||
name, err := o.Pipe(
|
||||
yaml.Lookup("name"), yaml.Match("default"),
|
||||
)
|
||||
if err != nil || yaml.IsEmpty(name) {
|
||||
if err != nil || yaml.IsMissingOrNull(name) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
FieldSpec: f.FieldSpec,
|
||||
SetValue: f.evaluateField,
|
||||
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
||||
CreateTag: yaml.StringTag,
|
||||
CreateTag: yaml.NodeTagString,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
@@ -66,8 +66,7 @@ func updateNodeValue(node *yaml.Node, newValue interface{}) {
|
||||
}
|
||||
|
||||
func (f Filter) setScalar(node *yaml.RNode) error {
|
||||
if node.YNode().Kind != yaml.ScalarNode || node.YNode().Tag != yaml.StringTag {
|
||||
// Only process string values
|
||||
if !yaml.IsYNodeString(node.YNode()) {
|
||||
return nil
|
||||
}
|
||||
v := expansion2.Expand(node.YNode().Value, f.MappingFunc)
|
||||
@@ -78,11 +77,10 @@ func (f Filter) setScalar(node *yaml.RNode) error {
|
||||
func (f Filter) setMap(node *yaml.RNode) error {
|
||||
contents := node.YNode().Content
|
||||
for i := 0; i < len(contents); i += 2 {
|
||||
if contents[i].Kind != yaml.ScalarNode || contents[i].Tag != yaml.StringTag {
|
||||
if !yaml.IsYNodeString(contents[i]) {
|
||||
return fmt.Errorf("invalid map key: %s, type: %s", contents[i].Value, contents[i].Tag)
|
||||
}
|
||||
if contents[i+1].Kind != yaml.ScalarNode || contents[i+1].Tag != yaml.StringTag {
|
||||
// value is not a string
|
||||
if !yaml.IsYNodeString(contents[i+1]) {
|
||||
continue
|
||||
}
|
||||
newValue := expansion2.Expand(contents[i+1].Value, f.MappingFunc)
|
||||
@@ -93,8 +91,7 @@ func (f Filter) setMap(node *yaml.RNode) error {
|
||||
|
||||
func (f Filter) setSeq(node *yaml.RNode) error {
|
||||
for _, item := range node.YNode().Content {
|
||||
if item.Kind != yaml.ScalarNode || item.Tag != yaml.StringTag {
|
||||
// value is not a string
|
||||
if !yaml.IsYNodeString(item) {
|
||||
return fmt.Errorf("invalid value type expect a string")
|
||||
}
|
||||
newValue := expansion2.Expand(item.Value, f.MappingFunc)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
@@ -251,7 +252,7 @@ metadata:
|
||||
config.kubernetes.io/index: '0'
|
||||
data:
|
||||
1: str
|
||||
' at path 'data': invalid map key: 1, type: !!int`,
|
||||
' at path 'data': invalid map key: 1, type: ` + yaml.NodeTagInt,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
|
||||
@@ -27,7 +27,7 @@ func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
FieldSpec: rc.FieldSpec,
|
||||
SetValue: rc.set,
|
||||
CreateKind: yaml.ScalarNode, // replicas is a ScalarNode
|
||||
CreateTag: yaml.IntTag, // yaml.NodeTagInt
|
||||
CreateTag: yaml.NodeTagInt,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ require (
|
||||
k8s.io/apimachinery v0.17.0
|
||||
k8s.io/client-go v0.17.0
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml v0.4.2 => ../kyaml
|
||||
replace sigs.k8s.io/kustomize/kyaml v0.5.0 => ../kyaml
|
||||
|
||||
@@ -466,9 +466,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
@@ -558,8 +557,8 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// SortArrayAndComputeHash sorts a string array and
|
||||
@@ -50,3 +52,105 @@ func Encode(hex string) (string, error) {
|
||||
func Hash(data string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
|
||||
}
|
||||
|
||||
// HashRNode returns the hash value of input RNode
|
||||
func HashRNode(node *yaml.RNode) (string, error) {
|
||||
// get node kind
|
||||
kindNode, err := node.Pipe(yaml.FieldMatcher{Name: "kind"})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
kind := kindNode.YNode().Value
|
||||
|
||||
// calculate hash for different kinds
|
||||
encoded := ""
|
||||
switch kind {
|
||||
case "ConfigMap":
|
||||
encoded, err = encodeConfigMap(node)
|
||||
case "Secret":
|
||||
encoded, err = encodeSecret(node)
|
||||
default:
|
||||
var encodedBytes []byte
|
||||
encodedBytes, err = json.Marshal(node.YNode())
|
||||
encoded = string(encodedBytes)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return Encode(Hash(encoded))
|
||||
}
|
||||
|
||||
func getNodeValues(node *yaml.RNode, paths []string) (map[string]interface{}, error) {
|
||||
values := make(map[string]interface{})
|
||||
for _, p := range paths {
|
||||
vn, err := node.Pipe(yaml.Lookup(p))
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
if vn == nil {
|
||||
values[p] = ""
|
||||
continue
|
||||
}
|
||||
if vn.YNode().Kind != yaml.ScalarNode {
|
||||
vs, err := vn.MarshalJSON()
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
// data, binaryData and stringData are all maps
|
||||
var v map[string]interface{}
|
||||
json.Unmarshal(vs, &v)
|
||||
values[p] = v
|
||||
} else {
|
||||
values[p] = vn.YNode().Value
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// encodeConfigMap encodes a ConfigMap.
|
||||
// Data, Kind, and Name are taken into account.
|
||||
// BinaryData is included if it's not empty to avoid useless key in output.
|
||||
func encodeConfigMap(node *yaml.RNode) (string, error) {
|
||||
// get fields
|
||||
paths := []string{"metadata/name", "data", "binaryData"}
|
||||
values, err := getNodeValues(node, paths)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m := map[string]interface{}{"kind": "ConfigMap", "name": values["metadata/name"],
|
||||
"data": values["data"]}
|
||||
if _, ok := values["binaryData"].(map[string]interface{}); ok {
|
||||
m["binaryData"] = values["binaryData"]
|
||||
}
|
||||
|
||||
// json.Marshal sorts the keys in a stable order in the encoding
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// encodeSecret encodes a Secret.
|
||||
// Data, Kind, Name, and Type are taken into account.
|
||||
// StringData is included if it's not empty to avoid useless key in output.
|
||||
func encodeSecret(node *yaml.RNode) (string, error) {
|
||||
// get fields
|
||||
paths := []string{"type", "metadata/name", "data", "stringData"}
|
||||
values, err := getNodeValues(node, paths)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m := map[string]interface{}{"kind": "Secret", "type": values["type"],
|
||||
"name": values["metadata/name"], "data": values["data"]}
|
||||
if _, ok := values["stringData"].(map[string]interface{}); ok {
|
||||
m["stringData"] = values["stringData"]
|
||||
}
|
||||
|
||||
// json.Marshal sorts the keys in a stable order in the encoding
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package hasher_test
|
||||
package hasher
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/api/hasher"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestSortArrayAndComputeHash(t *testing.T) {
|
||||
@@ -39,3 +40,314 @@ func TestHash(t *testing.T) {
|
||||
t.Errorf("expected hash %q but got %q", expect, sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigMapHash(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
cmYaml string
|
||||
hash string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap`, "6ct58987ht", ""},
|
||||
// one key
|
||||
{"one key", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
one: ""`, "9g67k2htb6", ""},
|
||||
// three keys (tests sorting order)
|
||||
{"three keys", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
two: 2
|
||||
one: ""
|
||||
three: 3`, "7757f9kkct", ""},
|
||||
// empty binary data map
|
||||
{"empty binary data", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap`, "6ct58987ht", ""},
|
||||
// one key with binary data
|
||||
{"one key with binary data", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
binaryData:
|
||||
one: ""`, "6mtk2m274t", ""},
|
||||
// three keys with binary data (tests sorting order)
|
||||
{"three keys with binary data", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
binaryData:
|
||||
two: 2
|
||||
one: ""
|
||||
three: 3`, "9th7kc28dg", ""},
|
||||
// two keys, one with string and another with binary data
|
||||
{"two keys with one each", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
one: ""
|
||||
binaryData:
|
||||
two: ""`, "698h7c7t9m", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
node, err := yaml.Parse(c.cmYaml)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h, err := HashRNode(node)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.hash != h {
|
||||
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretHash(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
secretYaml string
|
||||
hash string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type`, "5gmgkf8578", ""},
|
||||
// one key
|
||||
{"one key", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
one: ""`, "74bd68bm66", ""},
|
||||
// three keys (tests sorting order)
|
||||
{"three keys", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
two: 2
|
||||
one: ""
|
||||
three: 3`, "4gf75c7476", ""},
|
||||
// with stringdata
|
||||
{"stringdata", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
one: ""
|
||||
stringData:
|
||||
two: 2`, "c4h4264gdb", ""},
|
||||
// empty stringdata
|
||||
{"empty stringdata", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
one: ""`, "74bd68bm66", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
node, err := yaml.Parse(c.secretYaml)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h, err := HashRNode(node)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.hash != h {
|
||||
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnstructuredHash(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
unstructured string
|
||||
hash string
|
||||
err string
|
||||
}{
|
||||
{"minimal", `
|
||||
apiVersion: test/v1
|
||||
kind: TestResource
|
||||
metadata:
|
||||
name: my-resource`, "244782mkb7", ""},
|
||||
{"with spec", `
|
||||
apiVersion: test/v1
|
||||
kind: TestResource
|
||||
metadata:
|
||||
name: my-resource
|
||||
spec:
|
||||
foo: 1
|
||||
bar: abc`, "59m2mdccg4", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
node, err := yaml.Parse(c.unstructured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h, err := HashRNode(node)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.hash != h {
|
||||
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeConfigMap(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
cmYaml string
|
||||
expect string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap`, `{"data":"","kind":"ConfigMap","name":""}`, ""},
|
||||
// one key
|
||||
{"one key", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
one: ""`, `{"data":{"one":""},"kind":"ConfigMap","name":""}`, ""},
|
||||
// three keys (tests sorting order)
|
||||
{"three keys", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
two: 2
|
||||
one: ""
|
||||
three: 3`, `{"data":{"one":"","three":3,"two":2},"kind":"ConfigMap","name":""}`, ""},
|
||||
// empty binary map
|
||||
{"empty data", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap`, `{"data":"","kind":"ConfigMap","name":""}`, ""},
|
||||
// one key with binary data
|
||||
{"one key", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
binaryData:
|
||||
one: ""`, `{"binaryData":{"one":""},"data":"","kind":"ConfigMap","name":""}`, ""},
|
||||
// three keys with binary data (tests sorting order)
|
||||
{"three keys", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
binaryData:
|
||||
two: 2
|
||||
one: ""
|
||||
three: 3`, `{"binaryData":{"one":"","three":3,"two":2},"data":"","kind":"ConfigMap","name":""}`, ""},
|
||||
// two keys, one string and one binary values
|
||||
{"two keys with one each", `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
data:
|
||||
one: ""
|
||||
binaryData:
|
||||
two: ""`, `{"binaryData":{"two":""},"data":{"one":""},"kind":"ConfigMap","name":""}`, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
node, err := yaml.Parse(c.cmYaml)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := encodeConfigMap(node)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if s != c.expect {
|
||||
t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.cmYaml)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeSecret(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
secretYaml string
|
||||
expect string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type`, `{"data":"","kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
// one key
|
||||
{"one key", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
one: ""`, `{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
// three keys (tests sorting order) - note json.Marshal base64 encodes the values because they come in as []byte
|
||||
{"three keys", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
two: 2
|
||||
one: ""
|
||||
three: 3`, `{"data":{"one":"","three":3,"two":2},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
// with stringdata
|
||||
{"stringdata", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
one: ""
|
||||
stringData:
|
||||
two: 2`, `{"data":{"one":""},"kind":"Secret","name":"","stringData":{"two":2},"type":"my-type"}`, ""},
|
||||
// empty stringdata
|
||||
{"empty stringdata", `
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: my-type
|
||||
data:
|
||||
one: ""`, `{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
node, err := yaml.Parse(c.secretYaml)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := encodeSecret(node)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if s != c.expect {
|
||||
t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.secretYaml)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SkipRest returns true if there was a non-nil error or if we expected an error that didn't happen,
|
||||
// and logs the appropriate error on the test object.
|
||||
// The return value indicates whether we should skip the rest of the test case due to the error result.
|
||||
func SkipRest(t *testing.T, desc string, err error, contains string) bool {
|
||||
if err != nil {
|
||||
if len(contains) == 0 {
|
||||
t.Errorf("case %q, expect nil error but got %q", desc, err.Error())
|
||||
} else if !strings.Contains(err.Error(), contains) {
|
||||
t.Errorf("case %q, expect error to contain %q but got %q", desc, contains, err.Error())
|
||||
}
|
||||
return true
|
||||
} else if len(contains) > 0 {
|
||||
t.Errorf("case %q, expect error to contain %q but got nil error", desc, contains)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
package accumulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/nameref"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/transform"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
)
|
||||
|
||||
type nameReferenceTransformer struct {
|
||||
@@ -86,16 +84,12 @@ func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
|
||||
if candidates == nil {
|
||||
candidates = m.SubsetThatCouldBeReferencedByResource(referrer)
|
||||
}
|
||||
err := transform.MutateField(
|
||||
referrer.Map(),
|
||||
fSpec.PathSlice(),
|
||||
fSpec.CreateIfNotPresent,
|
||||
o.getNewNameFunc(
|
||||
// referrer could be an HPA instance,
|
||||
// target could be Gvk for Deployment,
|
||||
// candidate a list of resources "reachable"
|
||||
// from the HPA.
|
||||
referrer, target.Gvk, candidates))
|
||||
err := filtersutil.ApplyToJSON(nameref.Filter{
|
||||
FieldSpec: fSpec,
|
||||
Referrer: referrer,
|
||||
Target: target.Gvk,
|
||||
ReferralCandidates: candidates,
|
||||
}, referrer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -105,165 +99,3 @@ func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// selectReferral picks the referral among a subset of candidates.
|
||||
// It returns the current name and namespace of the selected candidate.
|
||||
// Note that the content of the referricalCandidateSubset slice is most of the time
|
||||
// identical to the referralCandidates resmap. Still in some cases, such
|
||||
// as ClusterRoleBinding, the subset only contains the resources of a specific
|
||||
// namespace.
|
||||
func (o *nameReferenceTransformer) selectReferral(
|
||||
oldName string,
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap,
|
||||
referralCandidateSubset []*resource.Resource) (interface{}, interface{}, error) {
|
||||
|
||||
for _, res := range referralCandidateSubset {
|
||||
id := res.OrgId()
|
||||
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
|
||||
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
|
||||
// If there's more than one match, there's no way
|
||||
// to know which one to pick, so emit error.
|
||||
if len(matches) > 1 {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"multiple matches for %s:\n %v",
|
||||
id, getIds(matches))
|
||||
}
|
||||
// In the resource, note that it is referenced
|
||||
// by the referrer.
|
||||
res.AppendRefBy(referrer.CurId())
|
||||
// Return transformed name of the object,
|
||||
// complete with prefixes, hashes, etc.
|
||||
return res.GetName(), res.GetNamespace(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return oldName, nil, nil
|
||||
}
|
||||
|
||||
// utility function to replace a simple string by the new name
|
||||
func (o *nameReferenceTransformer) getSimpleNameField(
|
||||
oldName string,
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap,
|
||||
referralCandidateSubset []*resource.Resource) (interface{}, error) {
|
||||
|
||||
newName, _, err := o.selectReferral(oldName, referrer, target,
|
||||
referralCandidates, referralCandidateSubset)
|
||||
|
||||
return newName, err
|
||||
}
|
||||
|
||||
// utility function to replace name field within a map[string]interface{}
|
||||
// and leverage the namespace field.
|
||||
func (o *nameReferenceTransformer) getNameAndNsStruct(
|
||||
inMap map[string]interface{},
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap) (interface{}, error) {
|
||||
|
||||
// Example:
|
||||
if _, ok := inMap["name"]; !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"%#v is expected to contain a name field", inMap)
|
||||
}
|
||||
oldName, ok := inMap["name"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"%#v is expected to contain a name field of type string", oldName)
|
||||
}
|
||||
|
||||
subset := referralCandidates.Resources()
|
||||
if namespacevalue, ok := inMap["namespace"]; ok {
|
||||
namespace := namespacevalue.(string)
|
||||
bynamespace := referralCandidates.GroupedByOriginalNamespace()
|
||||
if _, ok := bynamespace[namespace]; !ok {
|
||||
return inMap, nil
|
||||
}
|
||||
subset = bynamespace[namespace]
|
||||
}
|
||||
|
||||
newname, newnamespace, err := o.selectReferral(oldName, referrer, target,
|
||||
referralCandidates, subset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if (newname == oldName) && (newnamespace == nil) {
|
||||
// no candidate found.
|
||||
return inMap, nil
|
||||
}
|
||||
|
||||
inMap["name"] = newname
|
||||
if newnamespace != "" {
|
||||
// We don't want value "" to replace value "default" since
|
||||
// the empty string is handled as a wild card here not default namespace
|
||||
// by kubernetes.
|
||||
inMap["namespace"] = newnamespace
|
||||
}
|
||||
return inMap, nil
|
||||
|
||||
}
|
||||
|
||||
func (o *nameReferenceTransformer) getNewNameFunc(
|
||||
referrer *resource.Resource,
|
||||
target resid.Gvk,
|
||||
referralCandidates resmap.ResMap) func(in interface{}) (interface{}, error) {
|
||||
return func(in interface{}) (interface{}, error) {
|
||||
switch thing := in.(type) {
|
||||
case string:
|
||||
return o.getSimpleNameField(thing, referrer, target,
|
||||
referralCandidates, referralCandidates.Resources())
|
||||
case map[string]interface{}:
|
||||
// Kind: ValidatingWebhookConfiguration
|
||||
// FieldSpec is webhooks/clientConfig/service
|
||||
return o.getNameAndNsStruct(thing, referrer, target,
|
||||
referralCandidates)
|
||||
case []interface{}:
|
||||
for idx, item := range thing {
|
||||
switch value := item.(type) {
|
||||
case string:
|
||||
// Kind: Role/ClusterRole
|
||||
// FieldSpec is rules.resourceNames
|
||||
newName, err := o.getSimpleNameField(value, referrer, target,
|
||||
referralCandidates, referralCandidates.Resources())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thing[idx] = newName
|
||||
case map[string]interface{}:
|
||||
// Kind: RoleBinding/ClusterRoleBinding
|
||||
// FieldSpec is subjects
|
||||
// Note: The corresponding fieldSpec had been changed from
|
||||
// from path: subjects/name to just path: subjects. This is
|
||||
// what get mutatefield to request the mapping of the whole
|
||||
// map containing namespace and name instead of just a simple
|
||||
// string field containing the name
|
||||
newMap, err := o.getNameAndNsStruct(value, referrer, target,
|
||||
referralCandidates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thing[idx] = newMap
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"%#v is expected to be either a []string or a []map[string]interface{}", in)
|
||||
}
|
||||
}
|
||||
return in, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"%#v is expected to be either a string or a []interface{}", in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getIds(rs []*resource.Resource) []string {
|
||||
var result []string
|
||||
for _, r := range rs {
|
||||
result = append(result, r.CurId().String()+"\n")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -520,7 +520,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}).ResMap(),
|
||||
expectedErr: "is expected to contain a name field"},
|
||||
expectedErr: "cannot find field 'name' in node"},
|
||||
}
|
||||
|
||||
nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
|
||||
|
||||
@@ -16,8 +16,10 @@ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmU
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
@@ -72,6 +74,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||
@@ -91,11 +94,13 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
@@ -110,6 +115,7 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
@@ -120,6 +126,7 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
@@ -242,6 +249,7 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@@ -353,6 +361,7 @@ github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOV
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM=
|
||||
@@ -404,8 +413,6 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -494,7 +501,8 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
@@ -515,7 +523,8 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -201,7 +201,7 @@ metadata:
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-literalConfigMap-bar-8d2dkb8k24",
|
||||
"name": "foo-literalConfigMap-bar-g5f6t456f5",
|
||||
"namespace": "ns1",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
@@ -220,7 +220,7 @@ metadata:
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-secret-bar-9btc7bt4kb",
|
||||
"name": "foo-secret-bar-82c2g5f8f6",
|
||||
"namespace": "ns1",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
package kunstruct
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/kustomize/api/hasher"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
)
|
||||
|
||||
// kustHash computes a hash of an unstructured object.
|
||||
@@ -22,119 +19,9 @@ func NewKustHash() *kustHash {
|
||||
|
||||
// Hash returns a hash of the given object
|
||||
func (h *kustHash) Hash(m ifc.Kunstructured) (string, error) {
|
||||
u := unstructured.Unstructured{
|
||||
Object: m.Map(),
|
||||
}
|
||||
kind := u.GetKind()
|
||||
switch kind {
|
||||
case "ConfigMap":
|
||||
cm, err := unstructuredToConfigmap(u)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return configMapHash(cm)
|
||||
case "Secret":
|
||||
sec, err := unstructuredToSecret(u)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return secretHash(sec)
|
||||
default:
|
||||
return unstructuredHash(&u)
|
||||
}
|
||||
}
|
||||
|
||||
// configMapHash returns a hash of the ConfigMap.
|
||||
// The Data, Kind, and Name are taken into account.
|
||||
func configMapHash(cm *corev1.ConfigMap) (string, error) {
|
||||
encoded, err := encodeConfigMap(cm)
|
||||
node, err := filtersutil.GetRNode(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := hasher.Encode(hasher.Hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// SecretHash returns a hash of the Secret.
|
||||
// The Data, Kind, Name, and Type are taken into account.
|
||||
func secretHash(sec *corev1.Secret) (string, error) {
|
||||
encoded, err := encodeSecret(sec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := hasher.Encode(hasher.Hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// unstructuredHash creates a hash for an arbitrary type.
|
||||
// All fields of the object are taken into account when generating the hash.
|
||||
// This is a fallback for when a specalised hash for the type is unavailable.
|
||||
func unstructuredHash(u *unstructured.Unstructured) (string, error) {
|
||||
encoded, err := json.Marshal(u.Object)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := hasher.Encode(hasher.Hash(string(encoded)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// encodeConfigMap encodes a ConfigMap.
|
||||
// Data, Kind, and Name are taken into account.
|
||||
// BinaryData is included if it's not empty to avoid useless key in output.
|
||||
func encodeConfigMap(cm *corev1.ConfigMap) (string, error) {
|
||||
// json.Marshal sorts the keys in a stable order in the encoding
|
||||
m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data}
|
||||
if len(cm.BinaryData) > 0 {
|
||||
m["binaryData"] = cm.BinaryData
|
||||
}
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// encodeSecret encodes a Secret.
|
||||
// Data, Kind, Name, and Type are taken into account.
|
||||
// StringData is included if it's not empty to avoid useless key in output.
|
||||
func encodeSecret(sec *corev1.Secret) (string, error) {
|
||||
// json.Marshal sorts the keys in a stable order in the encoding
|
||||
m := map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data}
|
||||
if len(sec.StringData) > 0 {
|
||||
m["stringData"] = sec.StringData
|
||||
}
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func unstructuredToConfigmap(u unstructured.Unstructured) (*corev1.ConfigMap, error) {
|
||||
marshaled, err := json.Marshal(u.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out corev1.ConfigMap
|
||||
err = json.Unmarshal(marshaled, &out)
|
||||
return &out, err
|
||||
}
|
||||
|
||||
func unstructuredToSecret(u unstructured.Unstructured) (*corev1.Secret, error) {
|
||||
marshaled, err := json.Marshal(u.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out corev1.Secret
|
||||
err = json.Unmarshal(marshaled, &out)
|
||||
return &out, err
|
||||
return hasher.HashRNode(node)
|
||||
}
|
||||
|
||||
@@ -1,224 +1,34 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kunstruct
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func TestConfigMapHash(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
cm *corev1.ConfigMap
|
||||
hash string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", &corev1.ConfigMap{Data: map[string]string{}, BinaryData: map[string][]byte{}}, "42745tchd9", ""},
|
||||
// one key
|
||||
{"one key", &corev1.ConfigMap{Data: map[string]string{"one": ""}}, "9g67k2htb6", ""},
|
||||
// three keys (tests sorting order)
|
||||
{"three keys", &corev1.ConfigMap{Data: map[string]string{"two": "2", "one": "", "three": "3"}}, "f5h7t85m9b", ""},
|
||||
// empty binary data map
|
||||
{"empty binary data", &corev1.ConfigMap{BinaryData: map[string][]byte{}}, "dk855m5d49", ""},
|
||||
// one key with binary data
|
||||
{"one key with binary data", &corev1.ConfigMap{BinaryData: map[string][]byte{"one": []byte("")}}, "mk79584b8c", ""},
|
||||
// three keys with binary data (tests sorting order)
|
||||
{"three keys with binary data", &corev1.ConfigMap{BinaryData: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}}, "t458mc6db2", ""},
|
||||
// two keys, one with string and another with binary data
|
||||
{"two keys with one each", &corev1.ConfigMap{Data: map[string]string{"one": ""}, BinaryData: map[string][]byte{"two": []byte("")}}, "698h7c7t9m", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
h, err := configMapHash(c.cm)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.hash != h {
|
||||
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretHash(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
secret *corev1.Secret
|
||||
hash string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", &corev1.Secret{Type: "my-type", Data: map[string][]byte{}}, "t75bgf6ctb", ""},
|
||||
// one key
|
||||
{"one key", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}}, "74bd68bm66", ""},
|
||||
// three keys (tests sorting order)
|
||||
{"three keys", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}}, "dgcb6h9tmk", ""},
|
||||
// with stringdata
|
||||
{"stringdata", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}, StringData: map[string]string{"two": "2"}}, "ckm7f798g2", ""},
|
||||
// empty stringdata
|
||||
{"empty stringdata", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}, StringData: map[string]string{}}, "74bd68bm66", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
h, err := secretHash(c.secret)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.hash != h {
|
||||
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnstructuredHash(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
unstructured *unstructured.Unstructured
|
||||
hash string
|
||||
err string
|
||||
}{
|
||||
{"minimal", &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "test/v1",
|
||||
"kind": "TestResource",
|
||||
"metadata": map[string]string{"name": "my-resource"}},
|
||||
}, "2tt46d7f79", ""},
|
||||
{"with spec", &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "test/v1",
|
||||
"kind": "TestResource",
|
||||
"metadata": map[string]string{"name": "my-resource"},
|
||||
"spec": map[string]interface{}{"foo": 1, "bar": "abc"}},
|
||||
}, "6gc62g4m6k", ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
h, err := unstructuredHash(c.unstructured)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.hash != h {
|
||||
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeConfigMap(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
cm *corev1.ConfigMap
|
||||
expect string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", &corev1.ConfigMap{Data: map[string]string{}}, `{"data":{},"kind":"ConfigMap","name":""}`, ""},
|
||||
// one key
|
||||
{"one key", &corev1.ConfigMap{Data: map[string]string{"one": ""}}, `{"data":{"one":""},"kind":"ConfigMap","name":""}`, ""},
|
||||
// three keys (tests sorting order)
|
||||
{"three keys", &corev1.ConfigMap{Data: map[string]string{"two": "2", "one": "", "three": "3"}},
|
||||
`{"data":{"one":"","three":"3","two":"2"},"kind":"ConfigMap","name":""}`, ""},
|
||||
// empty binary map
|
||||
{"empty data", &corev1.ConfigMap{BinaryData: map[string][]byte{}}, `{"data":null,"kind":"ConfigMap","name":""}`, ""},
|
||||
// one key with binary data
|
||||
{"one key", &corev1.ConfigMap{BinaryData: map[string][]byte{"one": []byte("")}},
|
||||
`{"binaryData":{"one":""},"data":null,"kind":"ConfigMap","name":""}`, ""},
|
||||
// three keys with binary data (tests sorting order)
|
||||
{"three keys", &corev1.ConfigMap{BinaryData: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}},
|
||||
`{"binaryData":{"one":"","three":"Mw==","two":"Mg=="},"data":null,"kind":"ConfigMap","name":""}`, ""},
|
||||
// two keys, one string and one binary values
|
||||
{"two keys with one each", &corev1.ConfigMap{Data: map[string]string{"one": ""}, BinaryData: map[string][]byte{"two": []byte("")}},
|
||||
`{"binaryData":{"two":""},"data":{"one":""},"kind":"ConfigMap","name":""}`, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
s, err := encodeConfigMap(c.cm)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if s != c.expect {
|
||||
t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.cm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeSecret(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
secret *corev1.Secret
|
||||
expect string
|
||||
err string
|
||||
}{
|
||||
// empty map
|
||||
{"empty data", &corev1.Secret{Type: "my-type", Data: map[string][]byte{}}, `{"data":{},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
// one key
|
||||
{"one key", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}}, `{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
// three keys (tests sorting order) - note json.Marshal base64 encodes the values because they come in as []byte
|
||||
{"three keys", &corev1.Secret{
|
||||
Type: "my-type",
|
||||
Data: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")},
|
||||
},
|
||||
`{"data":{"one":"","three":"Mw==","two":"Mg=="},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
// with stringdata
|
||||
{"stringdata", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}, StringData: map[string]string{"two": "2"}},
|
||||
`{"data":{"one":""},"kind":"Secret","name":"","stringData":{"two":"2"},"type":"my-type"}`, ""},
|
||||
// empty stringdata
|
||||
{"empty stringdata", &corev1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}, StringData: map[string]string{}},
|
||||
`{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
s, err := encodeSecret(c.secret)
|
||||
if SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if s != c.expect {
|
||||
t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.secret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// warn devs who change types that they might have to update a hash function
|
||||
// not perfect, as it only checks the number of top-level fields
|
||||
func TestTypeStability(t *testing.T) {
|
||||
errfmt := `case %q, expected %d fields but got %d
|
||||
Depending on the field(s) you added, you may need to modify the hash function for this type.
|
||||
To guide you: the hash function targets fields that comprise the contents of objects,
|
||||
not their metadata (e.g. the Data of a ConfigMap, but nothing in ObjectMeta).
|
||||
func TestHasher(t *testing.T) {
|
||||
input := `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: foo
|
||||
data:
|
||||
one: ""
|
||||
binaryData:
|
||||
two: ""
|
||||
`
|
||||
cases := []struct {
|
||||
typeName string
|
||||
obj interface{}
|
||||
expect int
|
||||
}{
|
||||
{"ConfigMap", corev1.ConfigMap{}, 4},
|
||||
{"Secret", corev1.Secret{}, 5},
|
||||
}
|
||||
for _, c := range cases {
|
||||
val := reflect.ValueOf(c.obj)
|
||||
if num := val.NumField(); c.expect != num {
|
||||
t.Errorf(errfmt, c.typeName, c.expect, num)
|
||||
}
|
||||
}
|
||||
}
|
||||
expect := "698h7c7t9m"
|
||||
|
||||
// SkipRest returns true if there was a non-nil error or if we expected an error that didn't happen,
|
||||
// and logs the appropriate error on the test object.
|
||||
// The return value indicates whether we should skip the rest of the test case due to the error result.
|
||||
func SkipRest(t *testing.T, desc string, err error, contains string) bool {
|
||||
factory := NewKunstructuredFactoryImpl()
|
||||
k, err := factory.SliceFromBytes([]byte(input))
|
||||
if err != nil {
|
||||
if len(contains) == 0 {
|
||||
t.Errorf("case %q, expect nil error but got %q", desc, err.Error())
|
||||
} else if !strings.Contains(err.Error(), contains) {
|
||||
t.Errorf("case %q, expect error to contain %q but got %q", desc, contains, err.Error())
|
||||
}
|
||||
return true
|
||||
} else if len(contains) > 0 {
|
||||
t.Errorf("case %q, expect error to contain %q but got nil error", desc, contains)
|
||||
return true
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hasher := NewKustHash()
|
||||
result, err := hasher.Hash(k[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result != expect {
|
||||
t.Fatalf("expect %s but got %s", expect, result)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ spec:
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: somekey
|
||||
name: test-infra-app-env-ffmd9b969m
|
||||
name: test-infra-app-env-8h5mh7f7ch
|
||||
image: nginx:1.8.0
|
||||
name: nginx
|
||||
ports:
|
||||
@@ -240,7 +240,7 @@ spec:
|
||||
- configMapRef:
|
||||
name: someConfigMap
|
||||
- configMapRef:
|
||||
name: test-infra-app-env-ffmd9b969m
|
||||
name: test-infra-app-env-8h5mh7f7ch
|
||||
image: busybox
|
||||
name: busybox
|
||||
volumeMounts:
|
||||
@@ -248,7 +248,7 @@ spec:
|
||||
name: app-env
|
||||
volumes:
|
||||
- configMap:
|
||||
name: test-infra-app-env-ffmd9b969m
|
||||
name: test-infra-app-env-8h5mh7f7ch
|
||||
name: app-env
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -288,7 +288,7 @@ metadata:
|
||||
app: mungebot
|
||||
org: kubernetes
|
||||
repo: test-infra
|
||||
name: test-infra-app-env-ffmd9b969m
|
||||
name: test-infra-app-env-8h5mh7f7ch
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -301,6 +301,6 @@ metadata:
|
||||
app: mungebot
|
||||
org: kubernetes
|
||||
repo: test-infra
|
||||
name: test-infra-app-config-f462h769f9
|
||||
name: test-infra-app-config-49d6f5h7b5
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: comp-my-configmap-ct5bgtbccd
|
||||
name: comp-my-configmap-kc6k2kmkh9
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -186,7 +186,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: comp-my-configmap-dgf97tmg6h
|
||||
name: comp-my-configmap-55249mf5kb
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -241,7 +241,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: comp-my-configmap-dgf97tmg6h
|
||||
name: comp-my-configmap-55249mf5kb
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -283,7 +283,7 @@ data:
|
||||
testValue: "1"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-configmap-7k9t4h74f8
|
||||
name: my-configmap-2g9c94mhb8
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -301,7 +301,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: comp-my-configmap-ct5bgtbccd
|
||||
name: comp-my-configmap-kc6k2kmkh9
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -349,7 +349,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: my-configmap-96dt22k28h
|
||||
name: my-configmap-kc6k2kmkh9
|
||||
`,
|
||||
},
|
||||
"missing-optional-component-api-version": {
|
||||
@@ -380,7 +380,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: my-configmap-72cfg2mg5d
|
||||
name: my-configmap-5g7gh5mgt5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -427,7 +427,7 @@ data:
|
||||
testValue: "1"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-configmap-a-b-tfb7c5t69m
|
||||
name: my-configmap-a-b-2g9c94mhb8
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
@@ -442,7 +442,7 @@ data:
|
||||
testValue: "1"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-configmap-b-8h7b8862bb
|
||||
name: my-configmap-b-2g9c94mhb8
|
||||
`,
|
||||
},
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ data:
|
||||
vegetable: broccoli
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blah-bob-k772g5db55
|
||||
name: blah-bob-d87t8m8tgm
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -89,7 +89,7 @@ data:
|
||||
v2: '[{"path": "var/druid/segment-cache"}]'
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blah-json-9gtcc2fgb4
|
||||
name: blah-json-5298bc8g99
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -101,7 +101,7 @@ data:
|
||||
vegetable: YnJvY2NvbGk=
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: blah-bob-gmc2824f4b
|
||||
name: blah-bob-ftht6hfgmb
|
||||
type: Opaque
|
||||
`)
|
||||
}
|
||||
@@ -155,7 +155,7 @@ data:
|
||||
vegetable: broccoli
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blah-bob-gfkcbk5ckf
|
||||
name: blah-bob-db529cg5bk
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: p1-com1-dhbbm922gd
|
||||
name: p1-com1-8tc62428t2
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -226,7 +226,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: p2-com2-c4b8md75k9
|
||||
name: p2-com2-87mcggf7d7
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: o1-cm-28g596k77k
|
||||
name: o1-cm-ft9mmdc8c6
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -284,6 +284,6 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: cm-o2-gfcc59fg5m
|
||||
name: cm-o2-5k95kd76ft
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ metadata:
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
if secret.GetName() != "foo-secret-bar-9btc7bt4kb" {
|
||||
if secret.GetName() != "foo-secret-bar-82c2g5f8f6" {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ secretGenerator:
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
if secret.GetName() != "yeshash-mcgcmdcm69" {
|
||||
if secret.GetName() != "yeshash-82c2g5f8f6" {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -232,7 +233,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: nginx-persistent-storage
|
||||
volumes:
|
||||
- name: nginx-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -258,7 +260,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -332,7 +335,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: nginx-persistent-storage
|
||||
volumes:
|
||||
- name: nginx-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -459,7 +463,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -559,7 +564,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -661,7 +667,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -762,7 +769,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -957,7 +965,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -1171,7 +1180,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: busybox-persistent-storage
|
||||
volumes:
|
||||
- name: busybox-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: busybox-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
|
||||
@@ -251,7 +251,7 @@ spec:
|
||||
- emptyDir: {}
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: team-foo-configmap-in-base-bbdmdh7m8t
|
||||
name: team-foo-configmap-in-base-798k5k7g9f
|
||||
name: configmap-in-base
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -283,7 +283,7 @@ metadata:
|
||||
app: mynginx
|
||||
org: example.com
|
||||
team: foo
|
||||
name: team-foo-configmap-in-base-bbdmdh7m8t
|
||||
name: team-foo-configmap-in-base-798k5k7g9f
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -297,7 +297,7 @@ metadata:
|
||||
app: mynginx
|
||||
org: example.com
|
||||
team: foo
|
||||
name: team-foo-secret-in-base-tkm7hhtf8d
|
||||
name: team-foo-secret-in-base-bgd6bkgdm2
|
||||
type: Opaque
|
||||
`)
|
||||
}
|
||||
@@ -386,10 +386,10 @@ spec:
|
||||
pdName: nginx-persistent-storage
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: staging-team-foo-configmap-in-base-gh9d7t85gb
|
||||
name: staging-team-foo-configmap-in-base-hc6g9dk6g9
|
||||
name: configmap-in-base
|
||||
- configMap:
|
||||
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||
name: staging-configmap-in-overlay-dc6fm46dhm
|
||||
name: configmap-in-overlay
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -424,7 +424,7 @@ metadata:
|
||||
env: staging
|
||||
org: example.com
|
||||
team: override-foo
|
||||
name: staging-team-foo-configmap-in-base-gh9d7t85gb
|
||||
name: staging-team-foo-configmap-in-base-hc6g9dk6g9
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -440,7 +440,7 @@ metadata:
|
||||
env: staging
|
||||
org: example.com
|
||||
team: override-foo
|
||||
name: staging-team-foo-secret-in-base-c8db7gk2m2
|
||||
name: staging-team-foo-secret-in-base-k2k4692t9g
|
||||
type: Opaque
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -451,7 +451,7 @@ metadata:
|
||||
labels:
|
||||
env: staging
|
||||
team: override-foo
|
||||
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||
name: staging-configmap-in-overlay-dc6fm46dhm
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ data:
|
||||
key: value
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-t5t4md8fdm
|
||||
name: test-t757gk2bmf
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -494,7 +494,7 @@ data:
|
||||
key: value
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-t5t4md8fdm
|
||||
name: test-t757gk2bmf
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -503,7 +503,7 @@ data:
|
||||
username: YWRtaW4=
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: test-h65t9hg6kc
|
||||
name: test-bgd6bkgdm2
|
||||
namespace: default
|
||||
type: Opaque
|
||||
---
|
||||
@@ -513,7 +513,7 @@ data:
|
||||
username: YWRtaW4=
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: test-h65t9hg6kc
|
||||
name: test-bgd6bkgdm2
|
||||
namespace: kube-system
|
||||
type: Opaque
|
||||
`)
|
||||
|
||||
@@ -40,7 +40,7 @@ data:
|
||||
passphrase: ZGF0IHBocmFzZQ==
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: bob-kf5c9fccbt
|
||||
name: bob-bh645k7tmg
|
||||
type: Opaque
|
||||
`)
|
||||
}
|
||||
@@ -91,6 +91,6 @@ kind: ConfigMap
|
||||
metadata:
|
||||
labels:
|
||||
fruit: apple
|
||||
name: shouldHaveHash-2k9hc848ff
|
||||
name: shouldHaveHash-c9867f8446
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: nginx-persistent-storage
|
||||
volumes:
|
||||
- name: nginx-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
@@ -222,7 +223,8 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: nginx-persistent-storage
|
||||
volumes:
|
||||
- name: nginx-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: configmap-in-base
|
||||
name: configmap-in-base
|
||||
|
||||
@@ -147,10 +147,10 @@ spec:
|
||||
pdName: nginx-persistent-storage
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: a-b-configmap-in-base-fm96mhk4dt
|
||||
name: a-b-configmap-in-base-798k5k7g9f
|
||||
name: configmap-in-base
|
||||
- configMap:
|
||||
name: a-configmap-in-overlay-ffm9hf78mc
|
||||
name: a-configmap-in-overlay-dc6fm46dhm
|
||||
name: configmap-in-overlay
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -175,7 +175,7 @@ metadata:
|
||||
labels:
|
||||
env: staging
|
||||
team: foo
|
||||
name: a-b-configmap-in-base-fm96mhk4dt
|
||||
name: a-b-configmap-in-base-798k5k7g9f
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -184,7 +184,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
labels:
|
||||
env: staging
|
||||
name: a-configmap-in-overlay-ffm9hf78mc
|
||||
name: a-configmap-in-overlay-dc6fm46dhm
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -352,10 +352,10 @@ spec:
|
||||
pdName: nginx-persistent-storage
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||
name: staging-team-foo-configmap-in-base-798k5k7g9f
|
||||
name: configmap-in-base
|
||||
- configMap:
|
||||
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||
name: staging-configmap-in-overlay-dc6fm46dhm
|
||||
name: configmap-in-overlay
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -390,7 +390,7 @@ metadata:
|
||||
env: staging
|
||||
org: example.com
|
||||
team: foo
|
||||
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||
name: staging-team-foo-configmap-in-base-798k5k7g9f
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -399,7 +399,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
labels:
|
||||
env: staging
|
||||
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||
name: staging-configmap-in-overlay-dc6fm46dhm
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -540,9 +540,10 @@ spec:
|
||||
- mountPath: /tmp/ps
|
||||
name: nginx-persistent-storage
|
||||
volumes:
|
||||
- name: nginx-persistent-storage
|
||||
- emptyDir: {}
|
||||
name: nginx-persistent-storage
|
||||
- configMap:
|
||||
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||
name: staging-team-foo-configmap-in-base-798k5k7g9f
|
||||
name: configmap-in-base
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -577,7 +578,7 @@ metadata:
|
||||
env: staging
|
||||
org: example.com
|
||||
team: foo
|
||||
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||
name: staging-team-foo-configmap-in-base-798k5k7g9f
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
@@ -586,7 +587,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
labels:
|
||||
env: staging
|
||||
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||
name: staging-configmap-in-overlay-dc6fm46dhm
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
44
api/krusty/namereference_test.go
Normal file
44
api/krusty/namereference_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package krusty_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
func TestEmptyFieldSpecValue(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/app", `
|
||||
generators:
|
||||
- generators.yaml
|
||||
configurations:
|
||||
- kustomizeconfig.yaml
|
||||
`)
|
||||
th.WriteF("/app/generators.yaml", `
|
||||
apiVersion: builtin
|
||||
kind: ConfigMapGenerator
|
||||
metadata:
|
||||
name: secret-example
|
||||
labels:
|
||||
app.kubernetes.io/name: secret-example
|
||||
literals:
|
||||
- this_is_a_secret_name=
|
||||
`)
|
||||
th.WriteF("/app/kustomizeconfig.yaml", `
|
||||
nameReference:
|
||||
- kind: Secret
|
||||
version: v1
|
||||
fieldSpecs:
|
||||
- path: data/this_is_a_secret_name
|
||||
kind: ConfigMap
|
||||
`)
|
||||
m := th.Run("/app", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: v1
|
||||
data:
|
||||
this_is_a_secret_name: ""
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: secret-example-7hf4fh868h
|
||||
`)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ data:
|
||||
enableRisky: "false"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: the-non-default-namespace-map-b6h49k7mt8
|
||||
name: the-non-default-namespace-map-64b2md8tth
|
||||
namespace: non-default
|
||||
---
|
||||
apiVersion: v1
|
||||
@@ -51,14 +51,14 @@ data:
|
||||
enableRisky: "false"
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: the-map-4959m5tm6c
|
||||
name: the-map-tg7t5hk8bk
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
password.txt: dmVyeVNlY3JldA==
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: the-non-default-namespace-secret-h8d9hkgtb9
|
||||
name: the-non-default-namespace-secret-8tc9gdd76t
|
||||
namespace: non-default
|
||||
type: Opaque
|
||||
---
|
||||
@@ -67,7 +67,7 @@ data:
|
||||
password.txt: YW5vdGhlclNlY3JldA==
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: the-secret-fgb45h45bh
|
||||
name: the-secret-6557m7fcg8
|
||||
type: Opaque
|
||||
`)
|
||||
}
|
||||
@@ -104,7 +104,7 @@ kind: ConfigMap
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels: {}
|
||||
name: testCase-4g75kbk6gm
|
||||
name: testCase-bcbmmg48hd
|
||||
namespace: overlay
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ data:
|
||||
FOO: foo
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-k4bkhftttd
|
||||
name: test-6bc28fff49
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
@@ -895,7 +895,7 @@ spec:
|
||||
- command:
|
||||
- echo
|
||||
- dev-base-cockroachdb
|
||||
- dev-base-test-config-map-b2g2dmd64b
|
||||
- dev-base-test-config-map-6b85g79g7g
|
||||
env:
|
||||
- name: CDB_PUBLIC_SVC
|
||||
value: dev-base-cockroachdb-public
|
||||
@@ -921,7 +921,7 @@ data:
|
||||
foo: bar
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: dev-base-test-config-map-b2g2dmd64b
|
||||
name: dev-base-test-config-map-6b85g79g7g
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,8 @@ var orderFirst = []string{
|
||||
"Service",
|
||||
"LimitRange",
|
||||
"PriorityClass",
|
||||
"PersistentVolume",
|
||||
"PersistentVolumeClaim",
|
||||
"Deployment",
|
||||
"StatefulSet",
|
||||
"CronJob",
|
||||
|
||||
@@ -40,6 +40,15 @@ func (rmF *Factory) FromResource(res *resource.Resource) ResMap {
|
||||
return m
|
||||
}
|
||||
|
||||
// FromResourceSlice returns a ResMap with a slice of resources.
|
||||
func (rmF *Factory) FromResourceSlice(ress []*resource.Resource) ResMap {
|
||||
m, err := newResMapFromResourceSlice(ress)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// FromFile returns a ResMap given a resource path.
|
||||
func (rmF *Factory) FromFile(
|
||||
loader ifc.Loader, path string) (ResMap, error) {
|
||||
|
||||
@@ -146,6 +146,22 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
|
||||
// name.
|
||||
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
|
||||
result, err := rf.SliceFromBytes(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(names) != len(result) {
|
||||
return nil, fmt.Errorf("number of names doesn't match number of resources")
|
||||
}
|
||||
for i, res := range result {
|
||||
res.originalName = names[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MakeConfigMap makes an instance of Resource for ConfigMap
|
||||
func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, args)
|
||||
|
||||
@@ -15,7 +15,5 @@ require (
|
||||
k8s.io/client-go v0.17.3
|
||||
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
|
||||
sigs.k8s.io/cli-utils v0.17.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
||||
|
||||
@@ -452,6 +452,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -623,8 +624,8 @@ sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZw
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.0/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||
|
||||
@@ -100,12 +100,16 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
if setterVersion == "" {
|
||||
if len(args) == 2 && r.Set.SetPartialField.Type == "array" && c.Flag("field").Changed {
|
||||
setterVersion = "v2"
|
||||
} else if len(args) < 2 || !c.Flag("value").Changed && len(args) < 3 {
|
||||
setterVersion = "v1"
|
||||
} else if err := initSetterVersion(c, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.Set.SetPartialField.Type != "array" && !c.Flag("value").Changed && len(args) < 3 {
|
||||
return errors.Errorf("setter name and value must be provided, " +
|
||||
"value can either be an argument or can be passed as a flag --value")
|
||||
}
|
||||
|
||||
if setterVersion == "v2" {
|
||||
var err error
|
||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||
@@ -130,6 +134,19 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
"substitution and setter can't have same name", r.CreateSetter.Name)
|
||||
}
|
||||
|
||||
// check if setter with same name exists and throw error
|
||||
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSetter.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
setter, _ := openapi.Resolve(&ref)
|
||||
// if setter already exists with the input setter name, throw error
|
||||
if setter != nil {
|
||||
return errors.Errorf("setter with name %s already exists, "+
|
||||
"if you want to modify it, please delete the existing setter and recreate it", r.CreateSetter.Name)
|
||||
}
|
||||
|
||||
r.CreateSetter.Description = r.Set.SetPartialField.Description
|
||||
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
|
||||
r.CreateSetter.Type = r.Set.SetPartialField.Type
|
||||
|
||||
@@ -125,6 +125,24 @@ openAPI:
|
||||
err: "substitution with name my-image already exists, substitution and setter can't have same name",
|
||||
},
|
||||
|
||||
{
|
||||
name: "error if setter with same name exists",
|
||||
args: []string{
|
||||
"my-image", "ubuntu"},
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.my-image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-image
|
||||
value: "nginx"
|
||||
`,
|
||||
err: "setter with name my-image already exists, if you want to modify it, please delete the existing setter and recreate it",
|
||||
},
|
||||
|
||||
{
|
||||
name: "add replicas with schema",
|
||||
args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"},
|
||||
@@ -166,6 +184,36 @@ spec:
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "add replicas not enough arguments",
|
||||
args: []string{"replicas", "--description", "hello world", "--set-by", "me"},
|
||||
err: `setter name and value must be provided, value can either be an argument or can be passed as a flag --value`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "list values with schema",
|
||||
args: []string{"list", "--description", "hello world", "--set-by", "me", "--type", "array", "--field", "spec.list"},
|
||||
|
||||
@@ -5,8 +5,6 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -14,7 +12,6 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
@@ -71,8 +68,20 @@ func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
// check if substitution with same name exists and throw error
|
||||
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + r.CreateSubstitution.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subst, _ := openapi.Resolve(&ref)
|
||||
// if substitution already exists with the input substitution name, throw error
|
||||
if subst != nil {
|
||||
return errors.Errorf("substitution with name %s already exists", r.CreateSubstitution.Name)
|
||||
}
|
||||
|
||||
// check if setter with same name exists and throw error
|
||||
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSubstitution.Name)
|
||||
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSubstitution.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -84,40 +93,5 @@ func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
|
||||
"substitution and setter can't have same name", r.CreateSubstitution.Name))
|
||||
}
|
||||
|
||||
// extract setter name tokens from pattern enclosed in ${}
|
||||
re := regexp.MustCompile(`\$\{([^}]*)\}`)
|
||||
markers := re.FindAllString(r.CreateSubstitution.Pattern, -1)
|
||||
if len(markers) == 0 {
|
||||
return errors.Errorf("unable to find setter or substitution names in pattern, " +
|
||||
"setter names must be enclosed in ${}")
|
||||
}
|
||||
|
||||
for _, marker := range markers {
|
||||
name := strings.TrimSuffix(strings.TrimPrefix(marker, "${"), "}")
|
||||
if name == r.CreateSubstitution.Name {
|
||||
return fmt.Errorf("setters must have different name than the substitution: %s", name)
|
||||
}
|
||||
|
||||
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var markerRef string
|
||||
subst, _ := openapi.Resolve(&ref)
|
||||
// check if the substitution exists with the marker name or fall back to creating setter
|
||||
// ref with the name
|
||||
if subst != nil {
|
||||
markerRef = fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + name
|
||||
} else {
|
||||
markerRef = fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + name
|
||||
}
|
||||
|
||||
r.CreateSubstitution.Values = append(
|
||||
r.CreateSubstitution.Values,
|
||||
setters2.Value{Marker: marker, Ref: markerRef},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -104,6 +104,27 @@ spec:
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "error if substitution with same name exists",
|
||||
args: []string{"my-image", "--field-value", "some:image", "--pattern", "some:${image}"},
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.substitutions.my-image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: my-image
|
||||
pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing
|
||||
values:
|
||||
- marker: ${my-image-setter}
|
||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
||||
- marker: ${my-tag-setter}
|
||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||
`,
|
||||
err: "substitution with name my-image already exists",
|
||||
},
|
||||
{
|
||||
name: "error if setter with same name exists",
|
||||
args: []string{
|
||||
@@ -289,125 +310,6 @@ spec:
|
||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "nested cyclic substitution",
|
||||
args: []string{"my-nested-subst", "--field-value", "something/nginx::1.7.9/nginxotherthing",
|
||||
"--pattern", "something/${my-image-subst}/${my-other-setter}"},
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.my-image-setter:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-image-setter
|
||||
value: nginx
|
||||
io.k8s.cli.setters.my-tag-setter:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-tag-setter
|
||||
value: 1.7.9
|
||||
io.k8s.cli.substitutions.my-image-subst:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: my-image-subst
|
||||
pattern: ${my-nested-subst}::${my-tag-setter}
|
||||
values:
|
||||
- marker: ${my-nested-subst}
|
||||
ref: '#/definitions/io.k8s.cli.substitutions.my-nested-subst'
|
||||
- marker: ${my-tag-setter}
|
||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||
io.k8s.cli.setters.my-other-setter:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-other-setter
|
||||
value: nginxotherthing
|
||||
io.k8s.cli.substitutions.my-nested-subst:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: my-nested-subst
|
||||
pattern: something/${my-image-subst}/${my-other-setter}
|
||||
values:
|
||||
- marker: ${my-image-subst}
|
||||
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
||||
- marker: ${my-other-setter}
|
||||
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
||||
- name: sidecar
|
||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.my-image-setter:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-image-setter
|
||||
value: nginx
|
||||
io.k8s.cli.setters.my-tag-setter:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-tag-setter
|
||||
value: 1.7.9
|
||||
io.k8s.cli.substitutions.my-image-subst:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: my-image-subst
|
||||
pattern: ${my-nested-subst}::${my-tag-setter}
|
||||
values:
|
||||
- marker: ${my-nested-subst}
|
||||
ref: '#/definitions/io.k8s.cli.substitutions.my-nested-subst'
|
||||
- marker: ${my-tag-setter}
|
||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||
io.k8s.cli.setters.my-other-setter:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: my-other-setter
|
||||
value: nginxotherthing
|
||||
io.k8s.cli.substitutions.my-nested-subst:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: my-nested-subst
|
||||
pattern: something/${my-image-subst}/${my-other-setter}
|
||||
values:
|
||||
- marker: ${my-image-subst}
|
||||
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
||||
- marker: ${my-other-setter}
|
||||
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
||||
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
||||
- name: sidecar
|
||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
||||
`,
|
||||
err: "cyclic substitution detected with name my-nested-subst",
|
||||
},
|
||||
{
|
||||
name: "substitution with non-existing setter with same name",
|
||||
args: []string{
|
||||
@@ -484,7 +386,7 @@ spec:
|
||||
if !assert.NotNil(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, err.Error(), test.err)
|
||||
assert.Equal(t, test.err, err.Error())
|
||||
return
|
||||
}
|
||||
if !assert.NoError(t, err) {
|
||||
|
||||
@@ -159,11 +159,11 @@ The configMap name is suffixed by _-v1_, per the
|
||||
|
||||
The suffix to the configMap name is generated from a
|
||||
hash of the maps content - in this case the name suffix
|
||||
is _k25m8k5k5m_:
|
||||
is _5276h4th55_:
|
||||
|
||||
<!-- @grepStagingHash @testAgainstLatestRelease -->
|
||||
<!-- @grepStagingHash -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging | grep k25m8k5k5m
|
||||
kustomize build $OVERLAYS/staging | grep 5276h4th55
|
||||
```
|
||||
|
||||
Now modify the map patch, to change the greeting
|
||||
@@ -190,20 +190,20 @@ kustomize build $OVERLAYS/staging |\
|
||||
```
|
||||
|
||||
Confirm that the change in configMap content resulted
|
||||
in three new names ending in _cd7kdh48fd_ - one in the
|
||||
in three new names ending in _c2g8fcbf88_ - one in the
|
||||
configMap name itself, and two in the deployment that
|
||||
uses the map:
|
||||
|
||||
<!-- @countHashes @testAgainstLatestRelease -->
|
||||
<!-- @countHashes -->
|
||||
```
|
||||
test 3 == \
|
||||
$(kustomize build $OVERLAYS/staging | grep cd7kdh48fd | wc -l); \
|
||||
$(kustomize build $OVERLAYS/staging | grep c2g8fcbf88 | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
Applying these resources to the cluster will result in
|
||||
a rolling update of the deployments pods, retargetting
|
||||
them from the _k25m8k5k5m_ maps to the _cd7kdh48fd_
|
||||
them from the _5276h4th55_ maps to the _c2g8fcbf88_
|
||||
maps. The system will later garbage collect the
|
||||
unused maps.
|
||||
|
||||
|
||||
@@ -139,11 +139,11 @@ kustomize build $OVERLAYS/staging |\
|
||||
|
||||
根据 `$OVERLAYS/staging/kustomization.yaml` 中的 `nameSuffix` 字段,configMap 名称以 _-v1_ 为后缀。
|
||||
|
||||
configMap 名称的后缀是由 map 内容的哈希生成的 - 在这种情况下,名称后缀是 _k25m8k5k5m_ :
|
||||
configMap 名称的后缀是由 map 内容的哈希生成的 - 在这种情况下,名称后缀是 _5276h4th55_ :
|
||||
|
||||
<!-- @grepStagingHash @testAgainstLatestRelease -->
|
||||
<!-- @grepStagingHash -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging | grep k25m8k5k5m
|
||||
kustomize build $OVERLAYS/staging | grep 5276h4th55
|
||||
```
|
||||
|
||||
现在修改 map patch ,更改该服务将使用的问候消息:
|
||||
@@ -168,16 +168,16 @@ kustomize build $OVERLAYS/staging |\
|
||||
grep -B 8 -A 1 staging-the-map
|
||||
```
|
||||
|
||||
确认 configMap 内容的更改将会生成以 _cd7kdh48fd_ 结尾的三个新名称 - 一个在 configMap 的名称中,另两个在使用 ConfigMap 的 deployment 中:
|
||||
确认 configMap 内容的更改将会生成以 _c2g8fcbf88_ 结尾的三个新名称 - 一个在 configMap 的名称中,另两个在使用 ConfigMap 的 deployment 中:
|
||||
|
||||
<!-- @countHashes @testAgainstLatestRelease -->
|
||||
<!-- @countHashes -->
|
||||
```
|
||||
test 3 == \
|
||||
$(kustomize build $OVERLAYS/staging | grep cd7kdh48fd | wc -l); \
|
||||
$(kustomize build $OVERLAYS/staging | grep c2g8fcbf88 | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
将这些资源应用于群集将导致 deployment pod 的滚动更新,将它们从 _k25m8k5k5m_ map 重新定位到 _cd7kdh48fd_ map 。系统稍后将垃圾收集未使用的 map。
|
||||
将这些资源应用于群集将导致 deployment pod 的滚动更新,将它们从 _5276h4th55_ map 重新定位到 _c2g8fcbf88_ map 。系统稍后将垃圾收集未使用的 map。
|
||||
|
||||
## 回滚
|
||||
|
||||
|
||||
@@ -5,6 +5,6 @@ go 1.14
|
||||
require (
|
||||
k8s.io/apimachinery v0.18.3
|
||||
sigs.k8s.io/application v0.8.2
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
@@ -406,9 +406,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
@@ -511,8 +510,8 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -559,8 +558,8 @@ sigs.k8s.io/application v0.8.2 h1:XB7C33f7eW+1MbCJXoZa0+nP+R+S/VbAvYfCd3ufP1I=
|
||||
sigs.k8s.io/application v0.8.2/go.mod h1:Mv+ht9RE/QNtITYCzRbt3XTIN6t6so6cInmiyg6wOIg=
|
||||
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
|
||||
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||
|
||||
@@ -2,4 +2,4 @@ module sigs.k8s.io/kustomize/functions/examples/injection-tshirt-sizes
|
||||
|
||||
go 1.14
|
||||
|
||||
require sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
require sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
|
||||
@@ -169,6 +169,8 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -212,9 +214,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -258,8 +259,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
|
||||
@@ -2,4 +2,4 @@ module sigs.k8s.io/kustomize/functions/examples/template-go-nginx
|
||||
|
||||
go 1.14
|
||||
|
||||
require sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
require sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
|
||||
@@ -170,6 +170,8 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -213,9 +215,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -259,8 +260,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
|
||||
@@ -4,5 +4,5 @@ go 1.14
|
||||
|
||||
require (
|
||||
github.com/instrumenta/kubeval v0.0.0-20190918223246-8d013ec9fc56
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
)
|
||||
|
||||
@@ -189,6 +189,8 @@ github.com/spf13/pflag v0.0.0-20180821114517-d929dcbb1086 h1:iU+nPfqRqK8ShQqnpZL
|
||||
github.com/spf13/pflag v0.0.0-20180821114517-d929dcbb1086/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -239,9 +241,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -289,10 +290,10 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -2,4 +2,4 @@ module sigs.k8s.io/kustomize/functions/examples/validator-resource-requests
|
||||
|
||||
go 1.14
|
||||
|
||||
require sigs.k8s.io/kustomize/kyaml v0.4.2
|
||||
require sigs.k8s.io/kustomize/kyaml v0.5.0
|
||||
|
||||
@@ -164,6 +164,7 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -207,9 +208,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -253,8 +253,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
|
||||
@@ -8,13 +8,17 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
k8s.io/client-go v0.17.3
|
||||
sigs.k8s.io/kustomize/api v0.5.1
|
||||
sigs.k8s.io/kustomize/cmd/config v0.2.0
|
||||
sigs.k8s.io/kustomize/cmd/config v0.5.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/api v0.5.1 => ../api
|
||||
replace (
|
||||
sigs.k8s.io/kustomize/api v0.5.1 => ../api
|
||||
sigs.k8s.io/kustomize/cmd/config v0.5.0 => ../cmd/config
|
||||
)
|
||||
|
||||
exclude (
|
||||
github.com/russross/blackfriday v2.0.0+incompatible
|
||||
sigs.k8s.io/kustomize/api v0.2.0
|
||||
sigs.k8s.io/kustomize/cmd/config v0.2.0
|
||||
)
|
||||
|
||||
@@ -670,7 +670,6 @@ gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
@@ -744,18 +743,15 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||
sigs.k8s.io/cli-utils v0.12.0 h1:+CvYwQAEtKvcx/NaUVF9rDKvY91VwJj+i7D2lWBMYc0=
|
||||
sigs.k8s.io/cli-utils v0.12.0/go.mod h1:H35YA5iJIM7EVNgqDTjX2dgt4wE23zmnXOTSTlyD+PE=
|
||||
sigs.k8s.io/cli-utils v0.17.0 h1:iQ0nhgU8DZiRphHTErI1IHcHYp2fZaULrrFN4NF3dCc=
|
||||
sigs.k8s.io/cli-utils v0.17.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY=
|
||||
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
|
||||
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.2.0 h1:VNAWKb1JLl7dFjMAD5MwdZpGjreN3qL63C8DKtsUYck=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.2.0/go.mod h1:oXzY7QJS6JlmWgusEjra2O3cW7GSIICXa59/3DvjBfE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.4/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.2.0/go.mod h1:72/rLkSi+L/pHM1oCjwrf3ClU+tH5kZQvvdLSqIHwWU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1 h1:NEqA/35upoAjb+I5vh1ODUqxoX4DOrezeQa9BhhG5Co=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.4.0/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||
|
||||
@@ -47,7 +47,7 @@ The argument can be a URL resolving to a directory
|
||||
with a kustomization.yaml file, e.g.
|
||||
|
||||
kustomize build \
|
||||
github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6
|
||||
github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
|
||||
|
||||
The URL should be formulated as described at
|
||||
https://github.com/hashicorp/go-getter#url-format
|
||||
|
||||
74
kyaml/fix/fixsetters/fieldmetav1.go
Normal file
74
kyaml/fix/fixsetters/fieldmetav1.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// FieldMeta contains metadata that may be attached to fields as comments
|
||||
type FieldMetaV1 struct {
|
||||
Schema spec.Schema
|
||||
|
||||
Extensions XKustomize
|
||||
}
|
||||
|
||||
type XKustomize struct {
|
||||
SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"`
|
||||
PartialFieldSetters []PartialFieldSetter `yaml:"partialSetters,omitempty" json:"partialSetters,omitempty"`
|
||||
FieldSetter *PartialFieldSetter `yaml:"setter,omitempty" json:"setter,omitempty"`
|
||||
}
|
||||
|
||||
// PartialFieldSetter defines how to set part of a field rather than the full field
|
||||
// value. e.g. the tag part of an image field
|
||||
type PartialFieldSetter struct {
|
||||
// Name is the name of this setter.
|
||||
Name string `yaml:"name" json:"name"`
|
||||
|
||||
// Value is the current value that has been set.
|
||||
Value string `yaml:"value" json:"value"`
|
||||
}
|
||||
|
||||
// UpgradeV1SetterComment reads the FieldMeta from a node and upgrade the
|
||||
// setters comment to latest
|
||||
func (fm *FieldMetaV1) UpgradeV1SetterComment(n *yaml.RNode) error {
|
||||
// check for metadata on head and line comments
|
||||
comments := []string{n.YNode().LineComment, n.YNode().HeadComment}
|
||||
for _, c := range comments {
|
||||
if c == "" {
|
||||
continue
|
||||
}
|
||||
c := strings.TrimLeft(c, "#")
|
||||
|
||||
if err := fm.Schema.UnmarshalJSON([]byte(c)); err != nil {
|
||||
// note: don't return an error if the comment isn't a fieldmeta struct
|
||||
return nil
|
||||
}
|
||||
|
||||
fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"]
|
||||
if fe == nil {
|
||||
return nil
|
||||
}
|
||||
b, err := json.Marshal(fe)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// delete line comment after parsing info into fieldmeta
|
||||
n.YNode().HeadComment = ""
|
||||
n.YNode().LineComment = ""
|
||||
err = json.Unmarshal(b, &fm.Extensions)
|
||||
if fm.Extensions.FieldSetter != nil {
|
||||
n.YNode().LineComment = fmt.Sprintf(`{"%s":"%s"}`, fieldmeta.ShortHandRef(), fm.Extensions.FieldSetter.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
141
kyaml/fix/fixsetters/fixsetters.go
Normal file
141
kyaml/fix/fixsetters/fixsetters.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// SetterFixer fixes setters in the input package
|
||||
type SetterFixer struct {
|
||||
// PkgPath is path to the resource package
|
||||
PkgPath string
|
||||
|
||||
// OpenAPIPath is path to the openAPI file in the package
|
||||
OpenAPIPath string
|
||||
|
||||
// DryRun only displays the actions without performing
|
||||
DryRun bool
|
||||
}
|
||||
|
||||
// SetterFixerV1Result holds the results of V1 setters fix
|
||||
type SetterFixerV1Result struct {
|
||||
// NeedFix indicates if the resource in pkgPath are on V1 version of setters
|
||||
// and need to be fixed
|
||||
NeedFix bool
|
||||
|
||||
// CreatedSetters are setters created as part of this fix
|
||||
CreatedSetters []string
|
||||
|
||||
// CreatedSubst are substitutions created as part of this fix
|
||||
CreatedSubst []string
|
||||
|
||||
// FailedSetters are setters failed to create from current V1 setters
|
||||
FailedSetters map[string]error
|
||||
|
||||
// FailedSubst are substitutions failed to be created from V1 partial setters
|
||||
FailedSubst map[string]error
|
||||
}
|
||||
|
||||
// FixSettersV1 reads the package and upgrades v1 version of setters
|
||||
// to latest
|
||||
func (f *SetterFixer) FixV1Setters() (SetterFixerV1Result, error) {
|
||||
sfr := SetterFixerV1Result{
|
||||
FailedSetters: make(map[string]error),
|
||||
FailedSubst: make(map[string]error),
|
||||
}
|
||||
// KrmFile need not exist for dryRun
|
||||
if !f.DryRun {
|
||||
_, err := os.Stat(f.OpenAPIPath)
|
||||
if err != nil {
|
||||
return sfr, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
// lookup for all setters and partial setters in v1 format
|
||||
// delete the v1 format comments after lookup
|
||||
l := UpgradeV1Setters{}
|
||||
if f.DryRun {
|
||||
err = applyReadFilter(&l, f.PkgPath)
|
||||
} else {
|
||||
err = applyWriteFilter(&l, f.PkgPath)
|
||||
}
|
||||
if err != nil {
|
||||
return sfr, err
|
||||
}
|
||||
if len(l.SetterCounts) > 0 {
|
||||
sfr.NeedFix = true
|
||||
} else {
|
||||
return sfr, nil
|
||||
}
|
||||
|
||||
// for each v1 setter create the equivalent in v2,
|
||||
for _, setter := range l.SetterCounts {
|
||||
sd := setters2.SetterDefinition{
|
||||
Name: setter.Name,
|
||||
Value: setter.Value,
|
||||
Description: setter.Description,
|
||||
SetBy: setter.SetBy,
|
||||
Type: setter.Type,
|
||||
}
|
||||
var err error
|
||||
if !f.DryRun {
|
||||
err = sd.AddToFile(f.OpenAPIPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
sfr.FailedSetters[setter.Name] = err
|
||||
} else {
|
||||
sfr.CreatedSetters = append(sfr.CreatedSetters, setter.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// for each group of partial setters, create equivalent substitution
|
||||
for _, subst := range l.Substitutions {
|
||||
sc := settersutil.SubstitutionCreator{
|
||||
Name: subst.Name,
|
||||
FieldValue: subst.FieldVale,
|
||||
Pattern: subst.Pattern,
|
||||
ResourcesPath: f.PkgPath,
|
||||
OpenAPIPath: f.OpenAPIPath,
|
||||
}
|
||||
var err error
|
||||
if !f.DryRun {
|
||||
err = applyWriteFilter(&sc, f.PkgPath)
|
||||
}
|
||||
if err != nil {
|
||||
sfr.FailedSubst[subst.Name] = err
|
||||
} else {
|
||||
sfr.CreatedSubst = append(sfr.CreatedSubst, subst.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return sfr, nil
|
||||
}
|
||||
|
||||
func applyWriteFilter(f kio.Filter, pkgPath string) error {
|
||||
rw := &kio.LocalPackageReadWriter{
|
||||
PackagePath: pkgPath,
|
||||
}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
Filters: []kio.Filter{f},
|
||||
Outputs: []kio.Writer{rw},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
func applyReadFilter(f kio.Filter, pkgPath string) error {
|
||||
rw := &kio.LocalPackageReader{
|
||||
PackagePath: pkgPath,
|
||||
}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
Filters: []kio.Filter{f},
|
||||
}.Execute()
|
||||
}
|
||||
466
kyaml/fix/fixsetters/fixsetters_test.go
Normal file
466
kyaml/fix/fixsetters/fixsetters_test.go
Normal file
@@ -0,0 +1,466 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFixSettersV1(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
input string
|
||||
err string
|
||||
dryRun bool
|
||||
openAPIFile string
|
||||
expectedOutput string
|
||||
expectedOpenAPI string
|
||||
needFix bool
|
||||
createdSetters []string
|
||||
createdSubst []string
|
||||
failedSetters map[string]error
|
||||
failedSubst map[string]error
|
||||
}{
|
||||
{
|
||||
name: "upgrade-delete-partial-setters",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"cluster", "profile", "project"},
|
||||
createdSubst: []string{"project-cluster-54235872"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"$openapi":"project-cluster-54235872"}
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profile"}
|
||||
cluster: "someproj/someclus" # {"$openapi":"project-cluster-54235872"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.cluster:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: cluster
|
||||
value: someclus
|
||||
io.k8s.cli.setters.profile:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profile
|
||||
value: asm
|
||||
io.k8s.cli.setters.project:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: project
|
||||
value: someproj
|
||||
io.k8s.cli.substitutions.project-cluster-54235872:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: project-cluster-54235872
|
||||
pattern: ${project}/${cluster}
|
||||
values:
|
||||
- marker: ${project}
|
||||
ref: '#/definitions/io.k8s.cli.setters.project'
|
||||
- marker: ${cluster}
|
||||
ref: '#/definitions/io.k8s.cli.setters.cluster'
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "upgrade-delete-partial-setters-dryRun",
|
||||
dryRun: true,
|
||||
input: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
`,
|
||||
needFix: true,
|
||||
createdSetters: []string{"cluster", "profile", "project"},
|
||||
createdSubst: []string{"project-cluster-54235872"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "partial-setters-same-value",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
team: asm # {"type":"string","x-kustomize":{"setter":{"name":"team","value":"asm"}}}
|
||||
profile-team: asm/asm # {"type":"string","x-kustomize":{"partialSetters":[{"name":"profile","value":"asm"},{"name":"team","value":"asm"}]}}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"profile", "team"},
|
||||
createdSubst: []string{"profile-team-1851878264"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profile"}
|
||||
team: asm # {"$openapi":"team"}
|
||||
profile-team: asm/asm # {"$openapi":"profile-team-1851878264"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profile:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profile
|
||||
value: asm
|
||||
io.k8s.cli.setters.team:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: team
|
||||
value: asm
|
||||
io.k8s.cli.substitutions.profile-team-1851878264:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: profile-team-1851878264
|
||||
pattern: ${profile}/${team}
|
||||
values:
|
||||
- marker: ${profile}
|
||||
ref: '#/definitions/io.k8s.cli.setters.profile'
|
||||
- marker: ${team}
|
||||
ref: '#/definitions/io.k8s.cli.setters.team'
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "partial-setters-suffix-subst",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm-profile # {"type":"string","x-kustomize":{"partialSetters":[{"name":"asm","value":"asm"}]}}
|
||||
team: asm-team # {"type":"string","x-kustomize":{"partialSetters":[{"name":"asm","value":"asm"}]}}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"asm"},
|
||||
createdSubst: []string{"asm-3472570278", "asm-3647054792"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm-profile # {"$openapi":"asm-3647054792"}
|
||||
team: asm-team # {"$openapi":"asm-3472570278"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.asm:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: asm
|
||||
value: asm
|
||||
io.k8s.cli.substitutions.asm-3472570278:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: asm-3472570278
|
||||
pattern: ${asm}-team
|
||||
values:
|
||||
- marker: ${asm}
|
||||
ref: '#/definitions/io.k8s.cli.setters.asm'
|
||||
io.k8s.cli.substitutions.asm-3647054792:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: asm-3647054792
|
||||
pattern: ${asm}-profile
|
||||
values:
|
||||
- marker: ${asm}
|
||||
ref: '#/definitions/io.k8s.cli.setters.asm'
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "upgrade-with-both-versions",
|
||||
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: gcr.io/asm-testing # {"$openapi":"hubsetter"}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.hubsetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: hubsetter
|
||||
value: gcr.io/asm-testing`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"profilesetter"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: gcr.io/asm-testing # {"$openapi":"hubsetter"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.hubsetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: hubsetter
|
||||
value: gcr.io/asm-testing
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "setter-already-exists",
|
||||
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: asm # {"$openapi":"profilesetter"}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"profilesetter"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: asm # {"$openapi":"profilesetter"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "do-not-delete-latest setters",
|
||||
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: gcr.io/asm-testing
|
||||
`,
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: gcr.io/asm-testing
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "no-openAPI-file-error",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: gcr.io/asm-testing
|
||||
`,
|
||||
|
||||
err: "Krmfile:",
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
openAPIFileName := "Krmfile"
|
||||
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "deploy.yaml"), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if test.openAPIFile != "" {
|
||||
err = ioutil.WriteFile(filepath.Join(dir, openAPIFileName), []byte(test.openAPIFile), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
sf := SetterFixer{
|
||||
PkgPath: dir,
|
||||
DryRun: test.dryRun,
|
||||
OpenAPIPath: filepath.Join(dir, "Krmfile"),
|
||||
}
|
||||
|
||||
sfr, err := sf.FixV1Setters()
|
||||
if test.err == "" {
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
if !assert.Contains(t, err.Error(), test.err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
actualOutput, err := ioutil.ReadFile(filepath.Join(dir, "deploy.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedOutput, string(actualOutput))
|
||||
|
||||
if test.expectedOpenAPI != "" {
|
||||
actualOpenAPI, err := ioutil.ReadFile(filepath.Join(dir, openAPIFileName))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedOpenAPI, string(actualOpenAPI))
|
||||
}
|
||||
assert.Equal(t, test.needFix, sfr.NeedFix)
|
||||
assert.Equal(t, test.createdSetters, sfr.CreatedSetters)
|
||||
assert.Equal(t, test.createdSubst, sfr.CreatedSubst)
|
||||
assert.Equal(t, test.failedSubst, sfr.FailedSubst)
|
||||
assert.Equal(t, test.failedSetters, sfr.FailedSetters)
|
||||
})
|
||||
}
|
||||
}
|
||||
134
kyaml/fix/fixsetters/lookupupgrade.go
Normal file
134
kyaml/fix/fixsetters/lookupupgrade.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var _ yaml.Filter = &upgradeV1Setters{}
|
||||
|
||||
// upgradeV1Setters looks up v1 setters in a Resource and upgrades
|
||||
// all the setters related comments
|
||||
type upgradeV1Setters struct {
|
||||
// Name of the setter to lookup. Optional
|
||||
Name string
|
||||
|
||||
// Setters is a list of setters that were found
|
||||
Setters []setter
|
||||
|
||||
// Substitutions is a list of substitutions that were found
|
||||
Substitutions []substitution
|
||||
}
|
||||
|
||||
type substitution struct {
|
||||
Name string
|
||||
FieldVale string
|
||||
Pattern string
|
||||
}
|
||||
|
||||
type setter struct {
|
||||
PartialFieldSetter
|
||||
Description string
|
||||
Type string
|
||||
SetBy string
|
||||
SubstName string
|
||||
}
|
||||
|
||||
func (ls *upgradeV1Setters) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
switch object.YNode().Kind {
|
||||
case yaml.DocumentNode:
|
||||
// skip the document node
|
||||
return ls.Filter(yaml.NewRNode(object.YNode().Content[0]))
|
||||
case yaml.MappingNode:
|
||||
return object, object.VisitFields(func(node *yaml.MapNode) error {
|
||||
return node.Value.PipeE(ls)
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
return object, object.VisitElements(func(node *yaml.RNode) error {
|
||||
return node.PipeE(ls)
|
||||
})
|
||||
case yaml.ScalarNode:
|
||||
return object, ls.lookupAndUpgrade(object)
|
||||
default:
|
||||
return object, nil
|
||||
}
|
||||
}
|
||||
|
||||
// lookupAndUpgrade finds any setters for a field and upgrades the setters comment
|
||||
func (ls *upgradeV1Setters) lookupAndUpgrade(field *yaml.RNode) error {
|
||||
// check if there is a substitution for this field
|
||||
var fm = FieldMetaV1{}
|
||||
if err := fm.UpgradeV1SetterComment(field); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fm.Extensions.FieldSetter != nil {
|
||||
if ls.Name != "" && ls.Name != fm.Extensions.FieldSetter.Name {
|
||||
// skip this setter, it doesn't match the specified setter
|
||||
return nil
|
||||
}
|
||||
// full setter
|
||||
ls.Setters = append(ls.Setters, setter{
|
||||
PartialFieldSetter: *fm.Extensions.FieldSetter,
|
||||
Description: fm.Schema.Description,
|
||||
Type: fm.Schema.Type[0],
|
||||
SetBy: fm.Extensions.SetBy,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(fm.Extensions.PartialFieldSetters) > 0 {
|
||||
fieldValue := field.YNode().Value
|
||||
pattern := fieldValue
|
||||
|
||||
var substName string
|
||||
// derive substitution pattern from partial setters
|
||||
for i := range fm.Extensions.PartialFieldSetters {
|
||||
substName += fm.Extensions.PartialFieldSetters[i].Name + "-"
|
||||
pattern = strings.Replace(pattern, fm.Extensions.PartialFieldSetters[i].Value, `${`+fm.Extensions.PartialFieldSetters[i].Name+"}", 1)
|
||||
}
|
||||
|
||||
fvHash, err := FNV32aHash(fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
substName += fvHash
|
||||
ls.Substitutions = append(ls.Substitutions, substitution{
|
||||
Name: substName,
|
||||
FieldVale: fieldValue,
|
||||
Pattern: pattern,
|
||||
})
|
||||
}
|
||||
|
||||
for i := range fm.Extensions.PartialFieldSetters {
|
||||
if ls.Name != "" && ls.Name != fm.Extensions.PartialFieldSetters[i].Name {
|
||||
// skip this setter
|
||||
continue
|
||||
}
|
||||
ls.Setters = append(ls.Setters, setter{
|
||||
PartialFieldSetter: fm.Extensions.PartialFieldSetters[i],
|
||||
Description: fm.Schema.Description,
|
||||
Type: fm.Schema.Type[0],
|
||||
SetBy: fm.Extensions.SetBy,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FNV32aHash generates 32-bit FNV-1a hash for input string
|
||||
func FNV32aHash(text string) (string, error) {
|
||||
algorithm := fnv.New32a()
|
||||
_, err := algorithm.Write([]byte(text))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err)
|
||||
}
|
||||
return fmt.Sprint(algorithm.Sum32()), nil
|
||||
}
|
||||
86
kyaml/fix/fixsetters/lookupupgradekio.go
Normal file
86
kyaml/fix/fixsetters/lookupupgradekio.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var _ kio.Filter = &UpgradeV1Setters{}
|
||||
|
||||
// UpgradeV1Setters identifies setters for a collection of Resources to upgrade
|
||||
type UpgradeV1Setters struct {
|
||||
// Name is the name of the setter to match. Optional.
|
||||
Name string
|
||||
|
||||
// SetterCounts is populated by Filter and contains the count of fields matching each setter.
|
||||
SetterCounts []setterCount
|
||||
|
||||
// Substitutions are groups of partial setters
|
||||
Substitutions []substitution
|
||||
}
|
||||
|
||||
// setterCount records the identified setters and number of fields matching those setters
|
||||
type setterCount struct {
|
||||
// Count is the number of substitutions possible to perform
|
||||
Count int
|
||||
|
||||
// setter is the substitution found
|
||||
setter
|
||||
}
|
||||
|
||||
// Filter implements kio.Filter
|
||||
func (l *UpgradeV1Setters) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
setters := map[string]*setterCount{}
|
||||
substitutions := map[string]*substitution{}
|
||||
|
||||
for i := range input {
|
||||
// lookup substitutions for this object
|
||||
ls := &upgradeV1Setters{Name: l.Name}
|
||||
if err := input[i].PipeE(ls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// aggregate counts for each setter by name. takes the description and value from
|
||||
// the first setter for each name encountered.
|
||||
for j := range ls.Setters {
|
||||
setter := ls.Setters[j]
|
||||
curr, found := setters[setter.Name]
|
||||
if !found {
|
||||
curr = &setterCount{setter: setter}
|
||||
setters[setter.Name] = curr
|
||||
}
|
||||
curr.Count++
|
||||
}
|
||||
|
||||
for j := range ls.Substitutions {
|
||||
subst := ls.Substitutions[j]
|
||||
_, found := substitutions[subst.Name]
|
||||
if !found {
|
||||
substitutions[subst.Name] = &subst
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pull out and sort the results by setter name
|
||||
for _, v := range setters {
|
||||
l.SetterCounts = append(l.SetterCounts, *v)
|
||||
}
|
||||
|
||||
for _, subst := range substitutions {
|
||||
l.Substitutions = append(l.Substitutions, *subst)
|
||||
}
|
||||
|
||||
sort.Slice(l.Substitutions, func(i, j int) bool {
|
||||
return l.Substitutions[i].Name < l.Substitutions[j].Name
|
||||
})
|
||||
|
||||
sort.Slice(l.SetterCounts, func(i, j int) bool {
|
||||
return l.SetterCounts[i].Name < l.SetterCounts[j].Name
|
||||
})
|
||||
return input, nil
|
||||
}
|
||||
@@ -162,6 +162,8 @@ func (c *Filter) setupExec() {
|
||||
c.Exec.Args = args
|
||||
}
|
||||
|
||||
var tmpDirEnvKey string = "TMPDIR"
|
||||
|
||||
// getArgs returns the command + args to run to spawn the container
|
||||
func (c *Filter) getCommand() (string, []string) {
|
||||
// run the container using docker. this is simpler than using the docker
|
||||
@@ -189,13 +191,15 @@ func (c *Filter) getCommand() (string, []string) {
|
||||
args = append(args, "--mount", storageMount.String())
|
||||
}
|
||||
|
||||
// TODO: put these env processes into a separate function and call it in the outside of
|
||||
// getCommand
|
||||
os.Setenv("LOG_TO_STDERR", "true")
|
||||
os.Setenv("STRUCTURED_RESULTS", "true")
|
||||
|
||||
// export the local environment vars to the container
|
||||
for _, pair := range os.Environ() {
|
||||
items := strings.Split(pair, "=")
|
||||
if items[0] == "" || items[1] == "" {
|
||||
if items[0] == "" || items[1] == "" || items[0] == tmpDirEnvKey {
|
||||
continue
|
||||
}
|
||||
args = append(args, "-e", items[0])
|
||||
|
||||
@@ -106,7 +106,7 @@ metadata:
|
||||
for _, e := range os.Environ() {
|
||||
// the process env
|
||||
parts := strings.Split(e, "=")
|
||||
if parts[0] == "" || parts[1] == "" {
|
||||
if parts[0] == "" || parts[1] == "" || parts[0] == tmpDirEnvKey {
|
||||
continue
|
||||
}
|
||||
tt.expectedArgs = append(tt.expectedArgs, "-e", parts[0])
|
||||
@@ -210,3 +210,15 @@ func TestFilter_ExitCode(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreEnv(t *testing.T) {
|
||||
os.Setenv(tmpDirEnvKey, "")
|
||||
|
||||
fltr := Filter{Image: "example.com:version"}
|
||||
_, args := fltr.getCommand()
|
||||
for _, arg := range args {
|
||||
if arg == tmpDirEnvKey {
|
||||
t.Fatalf("%s should not be exported to container", tmpDirEnvKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) *Funct
|
||||
}
|
||||
}
|
||||
n, err := n.Pipe(yaml.Lookup("metadata", "configFn"))
|
||||
if err != nil || yaml.IsEmpty(n) {
|
||||
if err != nil || yaml.IsMissingOrNull(n) {
|
||||
return nil
|
||||
}
|
||||
s, err := n.String()
|
||||
|
||||
@@ -11,11 +11,10 @@ require (
|
||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d
|
||||
github.com/sergi/go-diff v1.1.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/pflag v1.0.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
|
||||
)
|
||||
|
||||
@@ -180,6 +180,8 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -226,9 +228,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -270,6 +271,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -176,12 +176,6 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func isEmptyDocument(node *yaml.Node) bool {
|
||||
// node is a Document with no content -- e.g. "---\n---"
|
||||
return node.Kind == yaml.DocumentNode &&
|
||||
node.Content[0].Tag == yaml.NullNodeTag
|
||||
}
|
||||
|
||||
func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, error) {
|
||||
node := &yaml.Node{}
|
||||
err := decoder.Decode(node)
|
||||
@@ -192,7 +186,7 @@ func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, erro
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
if isEmptyDocument(node) {
|
||||
if yaml.IsYNodeEmptyDoc(node) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -238,10 +238,10 @@ func ownerToString(node *yaml.RNode) (string, error) {
|
||||
owner := elements[0]
|
||||
var kind, name string
|
||||
|
||||
if value := owner.Field("kind"); !yaml.IsFieldEmpty(value) {
|
||||
if value := owner.Field("kind"); !value.IsNilOrEmpty() {
|
||||
kind = value.Value.YNode().Value
|
||||
}
|
||||
if value := owner.Field("name"); !yaml.IsFieldEmpty(value) {
|
||||
if value := owner.Field("name"); !value.IsNilOrEmpty() {
|
||||
name = value.Value.YNode().Value
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ type ResourceSchema struct {
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the ResourceSchema is empty
|
||||
func (rs *ResourceSchema) IsEmpty() bool {
|
||||
func (rs *ResourceSchema) IsMissingOrNull() bool {
|
||||
if rs == nil || rs.Schema == nil {
|
||||
return true
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func AddSchemaFromFileUsingField(path, field string) error {
|
||||
if field != "" {
|
||||
// get the field containing the openAPI
|
||||
m := y.Field(field)
|
||||
if yaml.IsFieldEmpty(m) {
|
||||
if m.IsNilOrEmpty() {
|
||||
// doesn't contain openAPI definitions
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ func (a *Add) visitMapping(object *yaml.RNode, p string, _ *openapi.ResourceSche
|
||||
|
||||
// visitScalar implements visitor
|
||||
// visitScalar will set the field metadata on each scalar field whose name + value match
|
||||
func (a *Add) visitScalar(object *yaml.RNode, p string, _ *openapi.ResourceSchema) error {
|
||||
func (a *Add) visitScalar(object *yaml.RNode, p string, _, _ *openapi.ResourceSchema) error {
|
||||
// check if the field matches
|
||||
if a.Type == "array" {
|
||||
return nil
|
||||
|
||||
@@ -45,7 +45,7 @@ func (d *Delete) visitMapping(_ *yaml.RNode, _ string, _ *openapi.ResourceSchema
|
||||
|
||||
// visitScalar implements visitor
|
||||
// visitScalar will remove the reference on each scalar field whose name matches.
|
||||
func (d *Delete) visitScalar(object *yaml.RNode, p string, _ *openapi.ResourceSchema) error {
|
||||
func (d *Delete) visitScalar(object *yaml.RNode, p string, _, _ *openapi.ResourceSchema) error {
|
||||
// check if the field matches
|
||||
if d.FieldName != "" && !strings.HasSuffix(p, d.FieldName) {
|
||||
return nil
|
||||
|
||||
@@ -53,7 +53,7 @@ func (l *List) listSetters(object *yaml.RNode, resourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(def) {
|
||||
if yaml.IsMissingOrNull(def) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (l *List) listSetters(object *yaml.RNode, resourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(setterNode) {
|
||||
if yaml.IsMissingOrNull(setterNode) {
|
||||
// has the setter prefix, but missing the setter extension
|
||||
return errors.Errorf("missing x-k8s-cli.setter for %s", key)
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func (l *List) listSubst(object *yaml.RNode) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(def) {
|
||||
if yaml.IsMissingOrNull(def) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (l *List) listSubst(object *yaml.RNode) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(substNode) {
|
||||
if yaml.IsMissingOrNull(substNode) {
|
||||
// has the substitution prefix, but missing the setter extension
|
||||
return errors.Errorf("missing x-k8s-cli.substitution for %s", key)
|
||||
}
|
||||
|
||||
@@ -81,9 +81,9 @@ func (s *Set) visitSequence(object *yaml.RNode, p string, schema *openapi.Resour
|
||||
}
|
||||
|
||||
// visitScalar
|
||||
func (s *Set) visitScalar(object *yaml.RNode, p string, schema *openapi.ResourceSchema) error {
|
||||
func (s *Set) visitScalar(object *yaml.RNode, p string, oa, setterSchema *openapi.ResourceSchema) error {
|
||||
// get the openAPI for this field describing how to apply the setter
|
||||
ext, err := getExtFromComment(schema)
|
||||
ext, err := getExtFromComment(setterSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -91,8 +91,13 @@ func (s *Set) visitScalar(object *yaml.RNode, p string, schema *openapi.Resource
|
||||
return nil
|
||||
}
|
||||
|
||||
var k8sSchema *spec.Schema
|
||||
if oa != nil {
|
||||
k8sSchema = oa.Schema
|
||||
}
|
||||
|
||||
// perform a direct set of the field if it matches
|
||||
ok, err := s.set(object, ext, schema.Schema)
|
||||
ok, err := s.set(object, ext, k8sSchema, setterSchema.Schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -214,7 +219,7 @@ func (s *Set) substituteUtil(ext *CliExtension, visited sets.String, nameMatch *
|
||||
}
|
||||
|
||||
// set applies the value from ext to field if its name matches s.Name
|
||||
func (s *Set) set(field *yaml.RNode, ext *CliExtension, sch *spec.Schema) (bool, error) {
|
||||
func (s *Set) set(field *yaml.RNode, ext *CliExtension, k8sSch, sch *spec.Schema) (bool, error) {
|
||||
// check full setter
|
||||
if ext.Setter == nil || !s.isMatch(ext.Setter.Name) {
|
||||
return false, nil
|
||||
@@ -234,14 +239,21 @@ func (s *Set) set(field *yaml.RNode, ext *CliExtension, sch *spec.Schema) (bool,
|
||||
// this has a full setter, set its value
|
||||
field.YNode().Value = ext.Setter.Value
|
||||
|
||||
// format the node so it is quoted if it is a string
|
||||
yaml.FormatNonStringStyle(field.YNode(), *sch)
|
||||
// format the node so it is quoted if it is a string. If there is
|
||||
// type information on the setter schema, we use it. Otherwise we
|
||||
// fall back to the field schema if it exists.
|
||||
if len(sch.Type) > 0 {
|
||||
yaml.FormatNonStringStyle(field.YNode(), *sch)
|
||||
} else if k8sSch != nil {
|
||||
yaml.FormatNonStringStyle(field.YNode(), *k8sSch)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// validateAgainstSchema validates the input setter value against user provided
|
||||
// openAI schema
|
||||
func validateAgainstSchema(ext *CliExtension, sch *spec.Schema) error {
|
||||
fixSchemaTypes(sch)
|
||||
sc := spec.Schema{}
|
||||
sc.Properties = map[string]spec.Schema{}
|
||||
sc.Properties[ext.Setter.Name] = *sch
|
||||
@@ -301,6 +313,33 @@ func validateAgainstSchema(ext *CliExtension, sch *spec.Schema) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixSchemaTypes traverses the schema and checks for some common
|
||||
// errors for the type field. This currently involves users using
|
||||
// 'int' instead of 'integer' and 'bool' instead of 'boolean'. Early versions
|
||||
// of setters didn't validate this, so there are users that have invalid
|
||||
// types in their packages.
|
||||
func fixSchemaTypes(sc *spec.Schema) {
|
||||
for i := range sc.Type {
|
||||
currentType := sc.Type[i]
|
||||
if currentType == "int" {
|
||||
sc.Type[i] = "integer"
|
||||
}
|
||||
if currentType == "bool" {
|
||||
sc.Type[i] = "boolean"
|
||||
}
|
||||
}
|
||||
|
||||
if items := sc.Items; items != nil {
|
||||
if items.Schema != nil {
|
||||
fixSchemaTypes(items.Schema)
|
||||
}
|
||||
for i := range items.Schemas {
|
||||
schema := items.Schemas[i]
|
||||
fixSchemaTypes(&schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetOpenAPI updates a setter value
|
||||
type SetOpenAPI struct {
|
||||
// Name is the name of the setter to add
|
||||
|
||||
@@ -126,7 +126,7 @@ metadata:
|
||||
},
|
||||
{
|
||||
name: "set-foo-no-type",
|
||||
description: "if a type is not specified for a setter, keep the existing quoting",
|
||||
description: "if a type is not specified for a setter or k8s schema, keep existing quoting",
|
||||
setter: "foo",
|
||||
openapi: `
|
||||
openAPI:
|
||||
@@ -138,16 +138,16 @@ openAPI:
|
||||
value: "4"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
apiVersion: custom/v1
|
||||
kind: Example
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.foo"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
apiVersion: custom/v1
|
||||
kind: Example
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
@@ -666,6 +666,68 @@ spec:
|
||||
- "1"
|
||||
- "2"
|
||||
- "3"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-with-invalid-type-int",
|
||||
description: "if a type is set to int instead of integer, we accept it",
|
||||
setter: "foo",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.foo:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: foo
|
||||
value: "4"
|
||||
type: int
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.foo"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.foo"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-with-invalid-type-bool",
|
||||
description: "if a type is set to bool instead of boolean, we accept it",
|
||||
setter: "foo",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.foo:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: foo
|
||||
value: "true"
|
||||
type: bool
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: false # {"$ref": "#/definitions/io.k8s.cli.setters.foo"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: true # {"$ref": "#/definitions/io.k8s.cli.setters.foo"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// SetterCreator creates or updates a setter in the OpenAPI definitions, and inserts references
|
||||
@@ -45,6 +46,16 @@ type SetterCreator struct {
|
||||
// live apply/preview. This field is added to the setter definition to record
|
||||
// the package publisher's intent to make the setter required to be set.
|
||||
Required bool
|
||||
|
||||
// Path to openAPI file
|
||||
OpenAPIPath string
|
||||
|
||||
// Path to resources folder
|
||||
ResourcesPath string
|
||||
}
|
||||
|
||||
func (c *SetterCreator) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nil, c.Create(c.OpenAPIPath, c.ResourcesPath)
|
||||
}
|
||||
|
||||
func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
@@ -42,9 +43,24 @@ type SubstitutionCreator struct {
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
|
||||
// Path to openAPI file
|
||||
OpenAPIPath string
|
||||
|
||||
// Path to resources folder
|
||||
ResourcesPath string
|
||||
}
|
||||
|
||||
func (c *SubstitutionCreator) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nil, c.Create(c.OpenAPIPath, c.ResourcesPath)
|
||||
}
|
||||
|
||||
func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
values, err := markersAndRefs(c.Name, c.Pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Values = values
|
||||
d := setters2.SubstitutionDefinition{
|
||||
Name: c.Name,
|
||||
Values: c.Values,
|
||||
@@ -121,6 +137,47 @@ func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
// createMarkersAndRefs takes the input pattern, creates setter/substitution markers
|
||||
// and corresponding openAPI refs
|
||||
func markersAndRefs(substName, pattern string) ([]setters2.Value, error) {
|
||||
var values []setters2.Value
|
||||
// extract setter name tokens from pattern enclosed in ${}
|
||||
re := regexp.MustCompile(`\$\{([^}]*)\}`)
|
||||
markers := re.FindAllString(pattern, -1)
|
||||
if len(markers) == 0 {
|
||||
return nil, errors.Errorf("unable to find setter or substitution names in pattern, " +
|
||||
"setter names must be enclosed in ${}")
|
||||
}
|
||||
|
||||
for _, marker := range markers {
|
||||
name := strings.TrimSuffix(strings.TrimPrefix(marker, "${"), "}")
|
||||
if name == substName {
|
||||
return nil, fmt.Errorf("setters must have different name than the substitution: %s", name)
|
||||
}
|
||||
|
||||
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var markerRef string
|
||||
subst, _ := openapi.Resolve(&ref)
|
||||
// check if the substitution exists with the marker name or fall back to creating setter
|
||||
// ref with the name
|
||||
if subst != nil {
|
||||
markerRef = fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + name
|
||||
} else {
|
||||
markerRef = fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + name
|
||||
}
|
||||
|
||||
values = append(
|
||||
values,
|
||||
setters2.Value{Marker: marker, Ref: markerRef},
|
||||
)
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// CreateSettersForSubstitution creates the setters for all the references in the substitution
|
||||
// values if they don't already exist in openAPIPath file.
|
||||
func (c SubstitutionCreator) CreateSettersForSubstitution(openAPIPath string) error {
|
||||
|
||||
@@ -16,7 +16,7 @@ type visitor interface {
|
||||
// node is the scalar field value
|
||||
// path is the path to the field; path elements are separated by '.'
|
||||
// oa is the OpenAPI schema for the field
|
||||
visitScalar(node *yaml.RNode, path string, oa *openapi.ResourceSchema) error
|
||||
visitScalar(node *yaml.RNode, path string, oa *openapi.ResourceSchema, fieldOA *openapi.ResourceSchema) error
|
||||
|
||||
// visitSequence is called for each sequence field value on a resource
|
||||
// node is the sequence field value
|
||||
@@ -50,9 +50,9 @@ func acceptImpl(v visitor, object *yaml.RNode, p string, oa *openapi.ResourceSch
|
||||
}
|
||||
return object.VisitFields(func(node *yaml.MapNode) error {
|
||||
// get the schema for the field and propagate it
|
||||
oa = getSchema(node.Key, oa, node.Key.YNode().Value)
|
||||
fieldSchema := getSchema(node.Key, oa, node.Key.YNode().Value)
|
||||
// Traverse each field value
|
||||
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value, oa)
|
||||
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value, fieldSchema)
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
// get the schema for the sequence node, use the schema provided if not present
|
||||
@@ -61,15 +61,15 @@ func acceptImpl(v visitor, object *yaml.RNode, p string, oa *openapi.ResourceSch
|
||||
return err
|
||||
}
|
||||
// get the schema for the elements
|
||||
oa = getSchema(object, oa, "")
|
||||
schema := getSchema(object, oa, "")
|
||||
return object.VisitElements(func(node *yaml.RNode) error {
|
||||
// Traverse each list element
|
||||
return acceptImpl(v, node, p, oa)
|
||||
return acceptImpl(v, node, p, schema)
|
||||
})
|
||||
case yaml.ScalarNode:
|
||||
// Visit the scalar field
|
||||
oa = getSchema(object, oa, "")
|
||||
return v.visitScalar(object, p, oa)
|
||||
fieldSchema := getSchema(object, oa, "")
|
||||
return v.visitScalar(object, p, oa, fieldSchema)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func getSchema(r *yaml.RNode, s *openapi.ResourceSchema, field string) *openapi.
|
||||
return s
|
||||
}
|
||||
|
||||
if yaml.IsEmpty(r) {
|
||||
if yaml.IsMissingOrNull(r) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -67,14 +67,15 @@ func (e ElementSetter) Filter(rn *RNode) (*RNode, error) {
|
||||
matchingElementFound := false
|
||||
for i := range rn.YNode().Content {
|
||||
elem := rn.Content()[i]
|
||||
newNode := NewRNode(elem)
|
||||
|
||||
// empty elements are not valid -- they at least need an associative key
|
||||
if IsEmpty(NewRNode(elem)) {
|
||||
if IsMissingOrNull(newNode) || IsEmptyMap(newNode) {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if this is the element we are matching
|
||||
val, err := NewRNode(elem).Pipe(FieldMatcher{Name: e.Key, StringValue: e.Value})
|
||||
val, err := newNode.Pipe(FieldMatcher{Name: e.Key, StringValue: e.Value})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -467,6 +468,9 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
|
||||
if err := ErrorIfInvalid(rn, yaml.ScalarNode); err != nil {
|
||||
return rn, err
|
||||
}
|
||||
if IsMissingOrNull(s.Value) {
|
||||
return rn, nil
|
||||
}
|
||||
// only apply the style if there is not an existing style
|
||||
// or we want to override it
|
||||
if !s.OverrideStyle || s.Value.YNode().Style == 0 {
|
||||
@@ -478,7 +482,7 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
|
||||
}
|
||||
|
||||
// Clear the field if it is empty, or explicitly null
|
||||
if s.Value == nil || IsNull(s.Value) {
|
||||
if s.Value == nil || s.Value.IsTaggedNull() {
|
||||
return rn.Pipe(Clear(s.Name))
|
||||
}
|
||||
|
||||
@@ -545,7 +549,7 @@ func IsFoundOrError(rn *RNode, err error) bool {
|
||||
|
||||
func ErrorIfAnyInvalidAndNonNull(kind yaml.Kind, rn ...*RNode) error {
|
||||
for i := range rn {
|
||||
if IsEmpty(rn[i]) {
|
||||
if IsMissingOrNull(rn[i]) {
|
||||
continue
|
||||
}
|
||||
if err := ErrorIfInvalid(rn[i], kind); err != nil {
|
||||
@@ -564,7 +568,7 @@ var nodeTypeIndex = map[yaml.Kind]string{
|
||||
}
|
||||
|
||||
func ErrorIfInvalid(rn *RNode, kind yaml.Kind) error {
|
||||
if rn == nil || rn.YNode() == nil || IsNull(rn) {
|
||||
if IsMissingOrNull(rn) {
|
||||
// node has no type, pass validation
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -395,6 +395,19 @@ bar: buz
|
||||
assert.Equal(t, `baz
|
||||
`, assertNoErrorString(t)(k.String()))
|
||||
|
||||
// Empty value
|
||||
node, err = Parse(`
|
||||
foo
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
instance = FieldSetter{}
|
||||
k, err = instance.Filter(node)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `foo
|
||||
`, assertNoErrorString(t)(node.String()))
|
||||
assert.Equal(t, `foo
|
||||
`, assertNoErrorString(t)(k.String()))
|
||||
|
||||
// Encounter error
|
||||
node, err = Parse(`
|
||||
-a
|
||||
|
||||
@@ -302,7 +302,7 @@ kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers: {}
|
||||
containers: []
|
||||
`,
|
||||
dest: `
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -262,7 +262,7 @@ kind: Deployment
|
||||
{description: `remove list -- empty in src`,
|
||||
source: `
|
||||
kind: Deployment
|
||||
items: {}
|
||||
items: []
|
||||
`,
|
||||
dest: `
|
||||
kind: Deployment
|
||||
@@ -273,7 +273,7 @@ items:
|
||||
`,
|
||||
expected: `
|
||||
kind: Deployment
|
||||
items: {}
|
||||
items: []
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -167,8 +167,8 @@ spec: {}
|
||||
expected: `
|
||||
kind: Deployment
|
||||
spec:
|
||||
foo: bar1
|
||||
baz: buz
|
||||
foo: bar1
|
||||
`,
|
||||
},
|
||||
|
||||
|
||||
@@ -52,11 +52,11 @@ func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.R
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yaml.IsEmpty(nodes.Dest()) {
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
// Add
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
if yaml.IsNull(nodes.Origin()) {
|
||||
if nodes.Origin().IsTaggedNull() {
|
||||
// clear the value
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
@@ -108,11 +108,11 @@ func (m Merger) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind wa
|
||||
}
|
||||
|
||||
// Add
|
||||
if yaml.IsEmpty(nodes.Dest()) {
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Clear
|
||||
if yaml.IsNull(nodes.Origin()) {
|
||||
if nodes.Origin().IsTaggedNull() {
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
// Recursively Merge dest
|
||||
|
||||
@@ -91,7 +91,7 @@ kind: Deployment
|
||||
{description: `remove scalar -- empty in src`,
|
||||
source: `
|
||||
kind: Deployment
|
||||
field: {}
|
||||
field: null
|
||||
`,
|
||||
dest: `
|
||||
kind: Deployment
|
||||
@@ -99,7 +99,6 @@ field: value1
|
||||
`,
|
||||
expected: `
|
||||
kind: Deployment
|
||||
field: {}
|
||||
`,
|
||||
},
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ var elementTestCases = []testCase{
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add an element to an existing list`,
|
||||
{
|
||||
description: `Add an element to an existing list`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -57,7 +58,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add an element to a non-existing list`,
|
||||
{
|
||||
description: `Add an element to a non-existing list`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment`,
|
||||
@@ -86,7 +88,8 @@ spec:
|
||||
name: foo
|
||||
`},
|
||||
|
||||
{description: `Add an element to a non-existing list, existing in dest`,
|
||||
{
|
||||
description: `Add an element to a non-existing list, existing in dest`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment`,
|
||||
@@ -127,7 +130,8 @@ spec:
|
||||
// Test Case
|
||||
// TODO(pwittrock): Figure out if there is something better we can do here
|
||||
// This element is missing from the destination -- only the new fields are added
|
||||
{description: `Add a field to the element, element missing from dest`,
|
||||
{
|
||||
description: `Add a field to the element, element missing from dest`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -168,7 +172,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Update a field on the elem, element missing from the dest`,
|
||||
{
|
||||
description: `Update a field on the elem, element missing from the dest`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -212,7 +217,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Update a field on the elem, element present in the dest`,
|
||||
{
|
||||
description: `Update a field on the elem, element present in the dest`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -308,7 +314,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add a field on the elem, element and field present in the dest`,
|
||||
{
|
||||
description: `Add a field on the elem, element and field present in the dest`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -356,14 +363,15 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Ignore an element`,
|
||||
{
|
||||
description: `Ignore an element`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers: {}
|
||||
containers: null
|
||||
`,
|
||||
update: `
|
||||
apiVersion: apps/v1
|
||||
@@ -371,7 +379,7 @@ kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers: {}
|
||||
containers: null
|
||||
`,
|
||||
local: `
|
||||
apiVersion: apps/v1
|
||||
@@ -397,7 +405,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Leave deleted`,
|
||||
{
|
||||
description: `Leave deleted`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -424,7 +433,8 @@ kind: Deployment
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an element -- matching`,
|
||||
{
|
||||
description: `Remove an element -- matching`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -463,7 +473,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an element -- field missing from update`,
|
||||
{
|
||||
description: `Remove an element -- field missing from update`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -503,7 +514,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an element -- element missing`,
|
||||
{
|
||||
description: `Remove an element -- element missing`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -554,7 +566,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an element -- empty containers`,
|
||||
{
|
||||
description: `Remove an element -- empty containers`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -571,7 +584,7 @@ kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers: {}
|
||||
containers: null
|
||||
`,
|
||||
local: `
|
||||
apiVersion: apps/v1
|
||||
@@ -595,7 +608,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an element -- missing list field`,
|
||||
{
|
||||
description: `Remove an element -- missing list field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -635,7 +649,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `infer merge keys merge'`,
|
||||
{
|
||||
description: `infer merge keys merge'`,
|
||||
origin: `
|
||||
kind: Deployment
|
||||
spec:
|
||||
@@ -678,7 +693,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `no infer merge keys merge using schema`,
|
||||
{
|
||||
description: `no infer merge keys merge using schema`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -725,7 +741,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `no infer merge keys merge using explicit schema as line comment'`,
|
||||
{
|
||||
description: `no infer merge keys merge using explicit schema as line comment'`,
|
||||
origin: `
|
||||
apiVersion: custom
|
||||
kind: Deployment
|
||||
@@ -772,7 +789,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `no infer merge keys merge using explicit schema as head comment'`,
|
||||
{
|
||||
description: `no infer merge keys merge using explicit schema as head comment'`,
|
||||
origin: `
|
||||
apiVersion: custom
|
||||
kind: Deployment
|
||||
@@ -821,7 +839,8 @@ spec:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `no infer merge keys merge using explicit schema to parent field'`,
|
||||
{
|
||||
description: `no infer merge keys merge using explicit schema to parent field'`,
|
||||
origin: `
|
||||
apiVersion: custom
|
||||
kind: Deployment
|
||||
@@ -860,7 +879,8 @@ spec: # {"$ref":"#/definitions/io.k8s.api.core.v1.PodSpec"}
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `no infer merge keys merge using explicit schema to parent field header'`,
|
||||
{
|
||||
description: `no infer merge keys merge using explicit schema to parent field header'`,
|
||||
origin: `
|
||||
apiVersion: custom
|
||||
kind: Deployment
|
||||
|
||||
@@ -6,7 +6,8 @@ package merge3_test
|
||||
var kustomizationTestCases = []testCase{
|
||||
// Kustomization Test Cases
|
||||
|
||||
{description: `ConfigMapGenerator merge`,
|
||||
{
|
||||
description: `ConfigMapGenerator merge`,
|
||||
origin: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
@@ -48,7 +49,8 @@ configMapGenerator:
|
||||
- configkey=configs/another_configfile2
|
||||
name: a-configmap2`},
|
||||
|
||||
{description: `SecretGenerator merge`,
|
||||
{
|
||||
description: `SecretGenerator merge`,
|
||||
origin: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
@@ -9,7 +9,8 @@ var listTestCases = []testCase{
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Replace list`,
|
||||
{
|
||||
description: `Replace list`,
|
||||
origin: `
|
||||
list:
|
||||
- 1
|
||||
@@ -34,7 +35,8 @@ list:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add an updated list`,
|
||||
{
|
||||
description: `Add an updated list`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
list: # old value
|
||||
@@ -62,7 +64,8 @@ list:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add keep an omitted field`,
|
||||
{
|
||||
description: `Add keep an omitted field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment`,
|
||||
@@ -89,7 +92,8 @@ kind: StatefulSet
|
||||
// Test Case
|
||||
//
|
||||
// TODO(#36): consider making this an error
|
||||
{description: `Change an updated field`,
|
||||
{
|
||||
description: `Change an updated field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
list: # old value
|
||||
@@ -119,7 +123,8 @@ list: # conflicting value
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Ignore a field -- set`,
|
||||
{
|
||||
description: `Ignore a field -- set`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
list: # ignore value
|
||||
@@ -151,7 +156,8 @@ list:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Ignore a field -- empty`,
|
||||
{
|
||||
description: `Ignore a field -- empty`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
list: # ignore value
|
||||
@@ -174,7 +180,8 @@ apiVersion: apps/v1
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Explicitly clear a field`,
|
||||
{
|
||||
description: `Explicitly clear a field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1`,
|
||||
update: `
|
||||
@@ -192,7 +199,8 @@ apiVersion: apps/v1`},
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Implicitly clear a field`,
|
||||
{
|
||||
description: `Implicitly clear a field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
list: # clear value
|
||||
@@ -214,7 +222,8 @@ apiVersion: apps/v1`},
|
||||
// Test Case
|
||||
//
|
||||
// TODO(#36): consider making this an error
|
||||
{description: `Implicitly clear a changed field`,
|
||||
{
|
||||
description: `Implicitly clear a changed field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
list: # old value
|
||||
|
||||
@@ -7,7 +7,8 @@ var mapTestCases = []testCase{
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add the annotations map field`,
|
||||
{
|
||||
description: `Add the annotations map field`,
|
||||
origin: `
|
||||
kind: Deployment`,
|
||||
update: `
|
||||
@@ -27,7 +28,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add an annotation to the field`,
|
||||
{
|
||||
description: `Add an annotation to the field`,
|
||||
origin: `
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -54,7 +56,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add an annotation to the field, field missing from dest`,
|
||||
{
|
||||
description: `Add an annotation to the field, field missing from dest`,
|
||||
origin: `
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -64,7 +67,7 @@ metadata:
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
a: b # ignore because unchanged
|
||||
a: b # ignored because unchanged
|
||||
d: e`,
|
||||
local: `
|
||||
kind: Deployment`,
|
||||
@@ -77,7 +80,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Update an annotation on the field, field messing rom the dest`,
|
||||
{
|
||||
description: `Update an annotation on the field, field messing rom the dest`,
|
||||
origin: `
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -105,7 +109,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Add an annotation to the field, field missing from dest`,
|
||||
{
|
||||
description: `Add an annotation to the field, field missing from dest`,
|
||||
origin: `
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -115,7 +120,7 @@ metadata:
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
a: b # ignore because unchanged
|
||||
a: b # ignored because unchanged
|
||||
d: e`,
|
||||
local: `
|
||||
kind: Deployment`,
|
||||
@@ -128,7 +133,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an annotation`,
|
||||
{
|
||||
description: `Remove an annotation`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -158,7 +164,8 @@ metadata:
|
||||
// Test Case
|
||||
//
|
||||
// TODO(#36) support ~annotations~: {} deletion
|
||||
{description: `Specify a field as empty that isn't present in the source`,
|
||||
{
|
||||
description: `Specify a field as empty that isn't present in the source`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -186,7 +193,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove an annotation`,
|
||||
{
|
||||
description: `Remove an annotation`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -213,7 +221,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove annotations field`,
|
||||
{
|
||||
description: `Remove annotations field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -238,7 +247,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove annotations field, but keep in dest`,
|
||||
{
|
||||
description: `Remove annotations field, but keep in dest`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -266,7 +276,8 @@ metadata:
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Remove annotations, but they are already empty`,
|
||||
{
|
||||
description: `Remove annotations, but they are already empty`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
||||
@@ -8,13 +8,15 @@ var scalarTestCases = []testCase{
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Set and updated a field`,
|
||||
origin: `kind: Deployment`,
|
||||
update: `kind: StatefulSet`,
|
||||
local: `kind: Deployment`,
|
||||
expected: `kind: StatefulSet`},
|
||||
{
|
||||
description: `Set and updated a field`,
|
||||
origin: `kind: Deployment`,
|
||||
update: `kind: StatefulSet`,
|
||||
local: `kind: Deployment`,
|
||||
expected: `kind: StatefulSet`},
|
||||
|
||||
{description: `Add an updated field`,
|
||||
{
|
||||
description: `Add an updated field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment # old value`,
|
||||
@@ -27,7 +29,109 @@ apiVersion: apps/v1`,
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet # new value`},
|
||||
|
||||
{description: `Add keep an omitted field`,
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{
|
||||
description: `Ensure comments are updated`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'dest'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 3`,
|
||||
update: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'updated'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}`,
|
||||
local: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'original'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'updated'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`},
|
||||
|
||||
{
|
||||
description: `Ensure deleted comments are not updated`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'dest'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}`,
|
||||
update: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'updated'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}`,
|
||||
local: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'original'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 4
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/merge-source: 'updated'
|
||||
config.kubernetes.io/path: 'temp.yaml'
|
||||
spec:
|
||||
replicas: 4
|
||||
`},
|
||||
|
||||
{
|
||||
description: `Add keep an omitted field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment`,
|
||||
@@ -48,7 +152,8 @@ kind: StatefulSet
|
||||
// Test Case
|
||||
//
|
||||
// TODO(#36): consider making this an error
|
||||
{description: `Change an updated field`,
|
||||
{
|
||||
description: `Change an updated field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment # old value`,
|
||||
@@ -62,7 +167,8 @@ kind: Service # conflicting value`,
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet # new value`},
|
||||
|
||||
{description: `Ignore a field`,
|
||||
{
|
||||
description: `Ignore a field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment # ignore this field`,
|
||||
@@ -74,7 +180,8 @@ apiVersion: apps/v1`,
|
||||
expected: `
|
||||
apiVersion: apps/v1`},
|
||||
|
||||
{description: `Explicitly clear a field`,
|
||||
{
|
||||
description: `Explicitly clear a field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1`,
|
||||
update: `
|
||||
@@ -102,7 +209,8 @@ apiVersion: apps/v1`},
|
||||
// Test Case
|
||||
//
|
||||
// TODO(#36): consider making this an error
|
||||
{description: `Implicitly clear a changed field`,
|
||||
{
|
||||
description: `Implicitly clear a changed field`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment`,
|
||||
@@ -117,7 +225,8 @@ apiVersion: apps/v1`},
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{description: `Merge an empty scalar value`,
|
||||
{
|
||||
description: `Merge an empty scalar value`,
|
||||
origin: `
|
||||
apiVersion: apps/v1
|
||||
`,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package merge3
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
@@ -19,7 +20,7 @@ const (
|
||||
type Visitor struct{}
|
||||
|
||||
func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
@@ -38,11 +39,11 @@ func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.
|
||||
}
|
||||
|
||||
func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if yaml.IsEmpty(nodes.Updated()) && !yaml.IsEmpty(nodes.Origin()) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && !yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// implicitly cleared from update -- element was deleted
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
if yaml.IsEmpty(nodes.Dest()) {
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
// not cleared, but missing from the dest
|
||||
// initialize a new value that can be recursively merged
|
||||
return yaml.NewRNode(&yaml.Node{Kind: yaml.SequenceNode}), nil
|
||||
@@ -53,19 +54,33 @@ func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yam
|
||||
}
|
||||
|
||||
func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return nil, nil
|
||||
}
|
||||
if yaml.IsEmpty(nodes.Updated()) != yaml.IsEmpty(nodes.Origin()) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
if yaml.IsEmpty(nodes.Updated()) && yaml.IsEmpty(nodes.Origin()) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
updatedStr, err := nodes.Updated().String()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
originStr, err := nodes.Origin().String()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
if updatedStr != originStr {
|
||||
// change in update node
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
if nodes.Updated().YNode().Value != nodes.Origin().YNode().Value {
|
||||
// value changed in update
|
||||
return nodes.Updated(), nil
|
||||
@@ -76,16 +91,16 @@ func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*ya
|
||||
}
|
||||
|
||||
func (m Visitor) visitNAList(nodes walk.Sources) (*yaml.RNode, error) {
|
||||
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
if yaml.IsEmpty(nodes.Updated()) != yaml.IsEmpty(nodes.Origin()) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
if yaml.IsEmpty(nodes.Updated()) && yaml.IsEmpty(nodes.Origin()) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value not present in source or dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func IsAssociative(schema *openapi.ResourceSchema, nodes []*yaml.RNode, infer bo
|
||||
}
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
if yaml.IsEmpty(node) {
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
continue
|
||||
}
|
||||
if node.IsAssociative() {
|
||||
|
||||
@@ -26,70 +26,65 @@ const (
|
||||
NodeTagMap = "!!map"
|
||||
NodeTagSeq = "!!seq"
|
||||
NodeTagEmpty = ""
|
||||
|
||||
// TODO: deprecate these
|
||||
NullNodeTag = NodeTagNull
|
||||
StringTag = NodeTagString
|
||||
BoolTag = NodeTagBool
|
||||
IntTag = NodeTagInt
|
||||
)
|
||||
|
||||
// NullNode returns a RNode point represents a null; value
|
||||
func NullNode() *RNode {
|
||||
return NewRNode(&Node{Tag: NullNodeTag})
|
||||
// MakeNullNode returns an RNode that represents an empty document.
|
||||
func MakeNullNode() *RNode {
|
||||
return NewRNode(&Node{Tag: NodeTagNull})
|
||||
}
|
||||
|
||||
// IsMissingOrNull returns true if the RNode is nil or contains and explicitly null value.
|
||||
// IsMissingOrNull is true if the RNode is nil or explicitly tagged null.
|
||||
// TODO: make this a method on RNode.
|
||||
func IsMissingOrNull(node *RNode) bool {
|
||||
return node == nil || node.YNode() == nil || node.YNode().Tag == NullNodeTag
|
||||
return node.IsNil() || node.YNode().Tag == NodeTagNull
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the RNode is MissingOrNull, or is either a MappingNode with
|
||||
// no fields.
|
||||
func IsEmpty(node *RNode) bool {
|
||||
if IsMissingOrNull(node) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Empty sequence is a special case and temporarily not considered as empty here.
|
||||
// Some users may want to keep empty sequence for compatibility reason.
|
||||
// For example, use JSON 6902 patch.
|
||||
return node.YNode().Kind == yaml.MappingNode && len(node.YNode().Content) == 0
|
||||
}
|
||||
|
||||
func IsNull(node *RNode) bool {
|
||||
return node != nil && node.YNode() != nil && node.YNode().Tag == NullNodeTag
|
||||
// IsEmptyMap returns true if the RNode is an empty node or an empty map.
|
||||
// TODO: make this a method on RNode.
|
||||
func IsEmptyMap(node *RNode) bool {
|
||||
return IsMissingOrNull(node) || IsYNodeEmptyMap(node.YNode())
|
||||
}
|
||||
|
||||
func IsFieldEmpty(node *MapNode) bool {
|
||||
if node == nil || node.Value == nil || node.Value.YNode() == nil ||
|
||||
node.Value.YNode().Tag == NullNodeTag {
|
||||
node.Value.YNode().Tag == NodeTagNull {
|
||||
return true
|
||||
}
|
||||
return IsYNodeEmptyMap(node.Value.YNode()) ||
|
||||
IsYNodeEmptySeq(node.Value.YNode())
|
||||
}
|
||||
|
||||
if node.Value.YNode().Kind == yaml.MappingNode && len(node.Value.YNode().Content) == 0 {
|
||||
return true
|
||||
}
|
||||
if node.Value.YNode().Kind == yaml.SequenceNode && len(node.Value.YNode().Content) == 0 {
|
||||
return true
|
||||
}
|
||||
func IsYNodeEmptyMap(n *yaml.Node) bool {
|
||||
return n != nil && n.Kind == yaml.MappingNode && len(n.Content) == 0
|
||||
}
|
||||
|
||||
return false
|
||||
// IsYNodeTaggedNull returns true if the node is explicitly tagged Null.
|
||||
func IsYNodeTaggedNull(n *yaml.Node) bool {
|
||||
return n != nil && n.Tag == NodeTagNull
|
||||
}
|
||||
|
||||
func IsYNodeEmptySeq(n *yaml.Node) bool {
|
||||
return n != nil && n.Kind == yaml.SequenceNode && len(n.Content) == 0
|
||||
}
|
||||
|
||||
// IsYNodeEmptyDoc is true if the node is a Document with no content.
|
||||
// E.g.: "---\n---"
|
||||
func IsYNodeEmptyDoc(n *yaml.Node) bool {
|
||||
return n.Kind == yaml.DocumentNode && n.Content[0].Tag == NodeTagNull
|
||||
}
|
||||
|
||||
func IsYNodeString(n *yaml.Node) bool {
|
||||
return n.Kind == yaml.ScalarNode && n.Tag == NodeTagString
|
||||
}
|
||||
|
||||
// GetValue returns underlying yaml.Node Value field
|
||||
func GetValue(node *RNode) string {
|
||||
if IsEmpty(node) {
|
||||
if IsMissingOrNull(node) {
|
||||
return ""
|
||||
}
|
||||
return node.YNode().Value
|
||||
}
|
||||
|
||||
func IsFieldNull(node *MapNode) bool {
|
||||
return node != nil && node.Value != nil && node.Value.YNode() != nil &&
|
||||
node.Value.YNode().Tag == NullNodeTag
|
||||
}
|
||||
|
||||
// Parser parses values into configuration.
|
||||
type Parser struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
@@ -265,6 +260,12 @@ type MapNode struct {
|
||||
Value *RNode
|
||||
}
|
||||
|
||||
// IsNilOrEmpty returns true if the MapNode is nil,
|
||||
// has no value, or has a value that appears empty.
|
||||
func (mn *MapNode) IsNilOrEmpty() bool {
|
||||
return mn == nil || mn.Value.IsNilOrEmpty()
|
||||
}
|
||||
|
||||
type MapNodeSlice []*MapNode
|
||||
|
||||
func (m MapNodeSlice) Keys() []*RNode {
|
||||
@@ -369,9 +370,28 @@ const (
|
||||
LabelsField = "labels"
|
||||
)
|
||||
|
||||
// IsNil is true if the node is nil, or its underlying YNode is nil.
|
||||
func (rn *RNode) IsNil() bool {
|
||||
return rn == nil || rn.YNode() == nil
|
||||
}
|
||||
|
||||
// IsTaggedNull is true if a non-nil node is explicitly tagged Null.
|
||||
func (rn *RNode) IsTaggedNull() bool {
|
||||
return !rn.IsNil() && IsYNodeTaggedNull(rn.YNode())
|
||||
}
|
||||
|
||||
// IsNilOrEmpty is true if the node is nil,
|
||||
// has no YNode, or has YNode that appears empty.
|
||||
func (rn *RNode) IsNilOrEmpty() bool {
|
||||
return rn.IsNil() ||
|
||||
IsYNodeTaggedNull(rn.YNode()) ||
|
||||
IsYNodeEmptyMap(rn.YNode()) ||
|
||||
IsYNodeEmptySeq(rn.YNode())
|
||||
}
|
||||
|
||||
// GetMeta returns the ResourceMeta for an RNode
|
||||
func (rn *RNode) GetMeta() (ResourceMeta, error) {
|
||||
if IsEmpty(rn) {
|
||||
if IsMissingOrNull(rn) {
|
||||
return ResourceMeta{}, nil
|
||||
}
|
||||
missingMeta := true
|
||||
@@ -386,17 +406,17 @@ func (rn *RNode) GetMeta() (ResourceMeta, error) {
|
||||
m := ResourceMeta{}
|
||||
|
||||
// TODO: consider optimizing this parsing
|
||||
if f := n.Field(APIVersionField); !IsFieldEmpty(f) {
|
||||
if f := n.Field(APIVersionField); !f.IsNilOrEmpty() {
|
||||
m.APIVersion = GetValue(f.Value)
|
||||
missingMeta = false
|
||||
}
|
||||
if f := n.Field(KindField); !IsFieldEmpty(f) {
|
||||
if f := n.Field(KindField); !f.IsNilOrEmpty() {
|
||||
m.Kind = GetValue(f.Value)
|
||||
missingMeta = false
|
||||
}
|
||||
|
||||
mf := n.Field(MetadataField)
|
||||
if IsFieldEmpty(mf) {
|
||||
if mf.IsNilOrEmpty() {
|
||||
if missingMeta {
|
||||
return m, ErrMissingMetadata
|
||||
}
|
||||
@@ -404,16 +424,16 @@ func (rn *RNode) GetMeta() (ResourceMeta, error) {
|
||||
}
|
||||
meta := mf.Value
|
||||
|
||||
if f := meta.Field(NameField); !IsFieldEmpty(f) {
|
||||
if f := meta.Field(NameField); !f.IsNilOrEmpty() {
|
||||
m.Name = f.Value.YNode().Value
|
||||
missingMeta = false
|
||||
}
|
||||
if f := meta.Field(NamespaceField); !IsFieldEmpty(f) {
|
||||
if f := meta.Field(NamespaceField); !f.IsNilOrEmpty() {
|
||||
m.Namespace = GetValue(f.Value)
|
||||
missingMeta = false
|
||||
}
|
||||
|
||||
if f := meta.Field(LabelsField); !IsFieldEmpty(f) {
|
||||
if f := meta.Field(LabelsField); !f.IsNilOrEmpty() {
|
||||
m.Labels = map[string]string{}
|
||||
_ = f.Value.VisitFields(func(node *MapNode) error {
|
||||
m.Labels[GetValue(node.Key)] = GetValue(node.Value)
|
||||
@@ -421,7 +441,7 @@ func (rn *RNode) GetMeta() (ResourceMeta, error) {
|
||||
})
|
||||
missingMeta = false
|
||||
}
|
||||
if f := meta.Field(AnnotationsField); !IsFieldEmpty(f) {
|
||||
if f := meta.Field(AnnotationsField); !f.IsNilOrEmpty() {
|
||||
m.Annotations = map[string]string{}
|
||||
_ = f.Value.VisitFields(func(node *MapNode) error {
|
||||
m.Annotations[GetValue(node.Key)] = GetValue(node.Value)
|
||||
@@ -640,7 +660,7 @@ func (rn *RNode) ElementValues(key string) ([]string, error) {
|
||||
var elements []string
|
||||
for i := 0; i < len(rn.Content()); i++ {
|
||||
field := NewRNode(rn.Content()[i]).Field(key)
|
||||
if !IsFieldEmpty(field) {
|
||||
if !field.IsNilOrEmpty() {
|
||||
elements = append(elements, field.Value.YNode().Value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,61 @@ type: string
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestIsYNodeTaggedNull(t *testing.T) {
|
||||
if IsYNodeTaggedNull(nil) {
|
||||
t.Fatalf("nil cannot be tagged null")
|
||||
}
|
||||
if IsYNodeTaggedNull(&Node{}) {
|
||||
t.Fatalf("untagged node is not tagged")
|
||||
}
|
||||
if IsYNodeTaggedNull(&Node{Tag: NodeTagFloat}) {
|
||||
t.Fatalf("float tagged node is not tagged")
|
||||
}
|
||||
if !IsYNodeTaggedNull(&Node{Tag: NodeTagNull}) {
|
||||
t.Fatalf("tagged node is tagged")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsYNodeEmptyMap(t *testing.T) {
|
||||
if IsYNodeEmptyMap(nil) {
|
||||
t.Fatalf("nil cannot be a map")
|
||||
}
|
||||
if IsYNodeEmptyMap(&Node{}) {
|
||||
t.Fatalf("raw node is not a map")
|
||||
}
|
||||
if IsYNodeEmptyMap(&Node{Kind: SequenceNode}) {
|
||||
t.Fatalf("seq node is not a map")
|
||||
}
|
||||
n := &Node{Kind: MappingNode}
|
||||
if !IsYNodeEmptyMap(n) {
|
||||
t.Fatalf("empty mapping node is an empty mapping node")
|
||||
}
|
||||
n.Content = append(n.Content, &Node{Kind: SequenceNode})
|
||||
if IsYNodeEmptyMap(n) {
|
||||
t.Fatalf("a node with content isn't empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsYNodeEmptySeq(t *testing.T) {
|
||||
if IsYNodeEmptySeq(nil) {
|
||||
t.Fatalf("nil cannot be a map")
|
||||
}
|
||||
if IsYNodeEmptySeq(&Node{}) {
|
||||
t.Fatalf("raw node is not a map")
|
||||
}
|
||||
if IsYNodeEmptySeq(&Node{Kind: MappingNode}) {
|
||||
t.Fatalf("map node is not a sequence")
|
||||
}
|
||||
n := &Node{Kind: SequenceNode}
|
||||
if !IsYNodeEmptySeq(n) {
|
||||
t.Fatalf("empty sequence node is an empty sequence node")
|
||||
}
|
||||
n.Content = append(n.Content, &Node{Kind: MappingNode})
|
||||
if IsYNodeEmptySeq(n) {
|
||||
t.Fatalf("a node with content isn't empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsMissingOrNull(t *testing.T) {
|
||||
if !IsMissingOrNull(nil) {
|
||||
t.Fatalf("input: nil")
|
||||
@@ -180,60 +235,167 @@ func TestIsMissingOrNull(t *testing.T) {
|
||||
t.Fatalf("input: valid node")
|
||||
}
|
||||
// node with NullNodeTag
|
||||
if !IsMissingOrNull(NullNode()) {
|
||||
if !IsMissingOrNull(MakeNullNode()) {
|
||||
t.Fatalf("input: with NullNodeTag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmpty(t *testing.T) {
|
||||
if !IsEmpty(nil) {
|
||||
t.Fatalf("input: nil")
|
||||
}
|
||||
|
||||
// missing value or null value
|
||||
if !IsEmpty(NewRNode(nil)) {
|
||||
t.Fatalf("input: nil value")
|
||||
}
|
||||
// not array or map
|
||||
if IsEmpty(NewScalarRNode("foo")) {
|
||||
t.Fatalf("input: not array or map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmpty_Arrays(t *testing.T) {
|
||||
node := NewListRNode()
|
||||
// empty array. empty array is not expected as empty
|
||||
if IsEmpty(node) {
|
||||
if IsMissingOrNull(NewListRNode()) {
|
||||
t.Fatalf("input: empty array")
|
||||
}
|
||||
|
||||
// array with 1 item
|
||||
node = NewListRNode("foo")
|
||||
if IsEmpty(node) {
|
||||
node := NewListRNode("foo")
|
||||
if IsMissingOrNull(node) {
|
||||
t.Fatalf("input: array with 1 item")
|
||||
}
|
||||
|
||||
// delete the item in array
|
||||
node.value.Content = nil
|
||||
if IsEmpty(node) {
|
||||
if IsMissingOrNull(node) {
|
||||
t.Fatalf("input: empty array")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmpty_Maps(t *testing.T) {
|
||||
func TestIsEmptyMap(t *testing.T) {
|
||||
node := NewMapRNode(nil)
|
||||
// empty map
|
||||
if !IsEmpty(node) {
|
||||
if !IsEmptyMap(node) {
|
||||
t.Fatalf("input: empty map")
|
||||
}
|
||||
// map with 1 item
|
||||
node = NewMapRNode(&map[string]string{
|
||||
"foo": "bar",
|
||||
})
|
||||
if IsEmpty(node) {
|
||||
if IsEmptyMap(node) {
|
||||
t.Fatalf("input: map with 1 item")
|
||||
}
|
||||
// delete the item in map
|
||||
node.value.Content = nil
|
||||
if !IsEmpty(node) {
|
||||
if !IsEmptyMap(node) {
|
||||
t.Fatalf("input: empty map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNil(t *testing.T) {
|
||||
var rn *RNode
|
||||
|
||||
if !rn.IsNil() {
|
||||
t.Fatalf("uninitialized RNode should be nil")
|
||||
}
|
||||
|
||||
if !NewRNode(nil).IsNil() {
|
||||
t.Fatalf("missing value YNode should be nil")
|
||||
}
|
||||
|
||||
if MakeNullNode().IsNil() {
|
||||
t.Fatalf("value tagged null is not nil")
|
||||
}
|
||||
|
||||
if NewMapRNode(nil).IsNil() {
|
||||
t.Fatalf("empty map not nil")
|
||||
}
|
||||
|
||||
if NewListRNode().IsNil() {
|
||||
t.Fatalf("empty list not nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsTaggedNull(t *testing.T) {
|
||||
var rn *RNode
|
||||
|
||||
if rn.IsTaggedNull() {
|
||||
t.Fatalf("nil RNode cannot be tagged")
|
||||
}
|
||||
|
||||
if NewRNode(nil).IsTaggedNull() {
|
||||
t.Fatalf("bare RNode should not be tagged")
|
||||
}
|
||||
|
||||
if !MakeNullNode().IsTaggedNull() {
|
||||
t.Fatalf("a null node is tagged null by definition")
|
||||
}
|
||||
|
||||
if NewMapRNode(nil).IsTaggedNull() {
|
||||
t.Fatalf("empty map should not be tagged null")
|
||||
}
|
||||
|
||||
if NewListRNode().IsTaggedNull() {
|
||||
t.Fatalf("empty list should not be tagged null")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRNodeIsNilOrEmpty(t *testing.T) {
|
||||
var rn *RNode
|
||||
|
||||
if !rn.IsNilOrEmpty() {
|
||||
t.Fatalf("uninitialized RNode should be empty")
|
||||
}
|
||||
|
||||
if !NewRNode(nil).IsNilOrEmpty() {
|
||||
t.Fatalf("missing value YNode should be empty")
|
||||
}
|
||||
|
||||
if !MakeNullNode().IsNilOrEmpty() {
|
||||
t.Fatalf("value tagged null should be empty")
|
||||
}
|
||||
|
||||
if !NewMapRNode(nil).IsNilOrEmpty() {
|
||||
t.Fatalf("empty map should be empty")
|
||||
}
|
||||
|
||||
if NewMapRNode(&map[string]string{"foo": "bar"}).IsNilOrEmpty() {
|
||||
t.Fatalf("non-empty map should not be empty")
|
||||
}
|
||||
|
||||
if !NewListRNode().IsNilOrEmpty() {
|
||||
t.Fatalf("empty list should be empty")
|
||||
}
|
||||
|
||||
if NewListRNode("foo").IsNilOrEmpty() {
|
||||
t.Fatalf("non-empty list should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapNodeIsNilOrEmpty(t *testing.T) {
|
||||
var mn *MapNode
|
||||
|
||||
if !mn.IsNilOrEmpty() {
|
||||
t.Fatalf("nil should be empty")
|
||||
}
|
||||
|
||||
mn = &MapNode{Key: MakeNullNode()}
|
||||
if !mn.IsNilOrEmpty() {
|
||||
t.Fatalf("missing value should be empty")
|
||||
}
|
||||
|
||||
mn.Value = NewRNode(nil)
|
||||
if !mn.IsNilOrEmpty() {
|
||||
t.Fatalf("missing value YNode should be empty")
|
||||
}
|
||||
|
||||
mn.Value = MakeNullNode()
|
||||
if !mn.IsNilOrEmpty() {
|
||||
t.Fatalf("value tagged null should be empty")
|
||||
}
|
||||
|
||||
mn.Value = NewMapRNode(nil)
|
||||
if !mn.IsNilOrEmpty() {
|
||||
t.Fatalf("empty map should be empty")
|
||||
}
|
||||
|
||||
mn.Value = NewMapRNode(&map[string]string{"foo": "bar"})
|
||||
if mn.IsNilOrEmpty() {
|
||||
t.Fatalf("non-empty map should not be empty")
|
||||
}
|
||||
|
||||
mn.Value = NewListRNode()
|
||||
if !mn.IsNilOrEmpty() {
|
||||
t.Fatalf("empty list should be empty")
|
||||
}
|
||||
|
||||
mn.Value = NewListRNode("foo")
|
||||
if mn.IsNilOrEmpty() {
|
||||
t.Fatalf("non-empty list should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yaml.IsEmpty(val) {
|
||||
if yaml.IsMissingOrNull(val) || yaml.IsEmptyMap(val) {
|
||||
_, err = dest.Pipe(yaml.ElementSetter{Key: key, Value: value})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -74,7 +74,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
|
||||
}
|
||||
}
|
||||
// field is empty
|
||||
if yaml.IsEmpty(dest) {
|
||||
if yaml.IsMissingOrNull(dest) {
|
||||
return nil, nil
|
||||
}
|
||||
return dest, nil
|
||||
@@ -97,7 +97,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yaml.IsEmpty(val) {
|
||||
if yaml.IsMissingOrNull(val) {
|
||||
_, err = dest.Pipe(yaml.ElementSetter{Key: key, Value: value})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -121,7 +121,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
|
||||
}
|
||||
|
||||
// field is empty
|
||||
if yaml.IsEmpty(dest) {
|
||||
if yaml.IsMissingOrNull(dest) {
|
||||
return nil, nil
|
||||
}
|
||||
return dest, nil
|
||||
|
||||
@@ -39,7 +39,7 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
|
||||
continue
|
||||
}
|
||||
field := l.Sources[i].Field(key)
|
||||
if field == nil || yaml.IsEmpty(field.Key) {
|
||||
if field == nil || yaml.IsMissingOrNull(field.Key) {
|
||||
keys = append(keys, nil)
|
||||
continue
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func (l Walker) fieldValue(fieldName string) ([]*yaml.RNode, *openapi.ResourceSc
|
||||
field := l.Sources[i].Field(fieldName)
|
||||
f, s := l.valueIfPresent(field)
|
||||
fields = append(fields, f)
|
||||
if sch == nil && !s.IsEmpty() {
|
||||
if sch == nil && !s.IsMissingOrNull() {
|
||||
sch = s
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user