mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-27 00:38:16 +00:00
Compare commits
95 Commits
cmd/config
...
drop-link-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88c318bf46 | ||
|
|
7ee75c33a9 | ||
|
|
2b328eeb36 | ||
|
|
616363ee73 | ||
|
|
0e13eadd7a | ||
|
|
0c37388135 | ||
|
|
5d1352882b | ||
|
|
c469e80cad | ||
|
|
5559601ecb | ||
|
|
01b34c8ea0 | ||
|
|
eba0ffdde2 | ||
|
|
31c59bd7f2 | ||
|
|
2da8959198 | ||
|
|
dc591f0a10 | ||
|
|
c94f164e66 | ||
|
|
fb216d8af8 | ||
|
|
72207bfa04 | ||
|
|
fe3321d710 | ||
|
|
35b5890e46 | ||
|
|
ca807019f0 | ||
|
|
6420fc4911 | ||
|
|
bd8262630e | ||
|
|
cf5b26db8a | ||
|
|
4b4049e646 | ||
|
|
f211841035 | ||
|
|
efa4587f92 | ||
|
|
873c8c1d17 | ||
|
|
686e97f2fe | ||
|
|
669ae59982 | ||
|
|
868a226e4e | ||
|
|
a2693d0249 | ||
|
|
b7d913b58c | ||
|
|
f199b747e9 | ||
|
|
5a9fbf7da3 | ||
|
|
c18c803d3f | ||
|
|
d59d0401f4 | ||
|
|
83a70f7830 | ||
|
|
6afabf26ae | ||
|
|
2bcece5f1e | ||
|
|
e605391895 | ||
|
|
9de2c6b58e | ||
|
|
fc70e3181f | ||
|
|
8cd7c13fad | ||
|
|
740ec39dd8 | ||
|
|
e6927a2fdf | ||
|
|
083dccfe91 | ||
|
|
b61553e584 | ||
|
|
8cdc97a0dd | ||
|
|
f205641498 | ||
|
|
bfaca2122a | ||
|
|
9482c571f0 | ||
|
|
1ad49de087 | ||
|
|
5e89565930 | ||
|
|
ef713e33ce | ||
|
|
2f7241f4c3 | ||
|
|
34e0ade3e7 | ||
|
|
436c688bd0 | ||
|
|
9d8fbd9f04 | ||
|
|
bb6fb703a0 | ||
|
|
c99bc47c8d | ||
|
|
60422c8090 | ||
|
|
fc83477ec8 | ||
|
|
c9e8631399 | ||
|
|
974e3847dd | ||
|
|
8f4e7e8072 | ||
|
|
4e74947731 | ||
|
|
5c433ead5e | ||
|
|
feeaa994b7 | ||
|
|
17f935452f | ||
|
|
6faff2d031 | ||
|
|
18a86bd7d6 | ||
|
|
7eac250cf4 | ||
|
|
52083c6e49 | ||
|
|
6f63cf7238 | ||
|
|
de0c8dedc4 | ||
|
|
c23004df79 | ||
|
|
1a44c3c543 | ||
|
|
a9d1182322 | ||
|
|
a3d5628133 | ||
|
|
a563169461 | ||
|
|
729544b9f4 | ||
|
|
69d497ccdd | ||
|
|
c58c142849 | ||
|
|
9ba04e3f7d | ||
|
|
3f842e5e92 | ||
|
|
9fdb3e1e9e | ||
|
|
462dbcb999 | ||
|
|
3e0448f1b7 | ||
|
|
3c3f9a26f6 | ||
|
|
7d7c889285 | ||
|
|
4fe2f9dd5b | ||
|
|
0cb852b98a | ||
|
|
bf5656b02b | ||
|
|
af057a95c5 | ||
|
|
2ee4eec791 |
13
README.md
13
README.md
@@ -9,18 +9,15 @@ patch [kubernetes style] API objects. It's like
|
||||
[`make`], in that what it does is declared in a file,
|
||||
and it's like [`sed`], in that it emits edited text.
|
||||
|
||||
This tool is sponsored by [sig-cli] ([KEP]), and
|
||||
inspired by [DAM].
|
||||
This tool is sponsored by [sig-cli] ([KEP]).
|
||||
|
||||
- [Installation instructions](https://kubernetes-sigs.github.io/kustomize/installation)
|
||||
- [General documentation](https://kubernetes-sigs.github.io/kustomize)
|
||||
- [Examples](examples)
|
||||
|
||||
[](https://prow.k8s.io/job-history/kubernetes-jenkins/pr-logs/directory/kustomize-presubmit-master)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
||||
|
||||
Download a binary from the [release page], or see
|
||||
these [instructions](https://kubernetes-sigs.github.io/kustomize/installation/).
|
||||
|
||||
Browse the [docs](https://kubernetes-sigs.github.io/kustomize/) or jump right into the
|
||||
tested [examples](examples).
|
||||
|
||||
## kubectl integration
|
||||
|
||||
Since [v1.14][kubectl announcement] the kustomize build system has been included in kubectl.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
||||
if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil {
|
||||
return obj, errors.Wrap(err)
|
||||
}
|
||||
fltr.path = strings.Split(fltr.FieldSpec.Path, "/")
|
||||
fltr.path = splitPath(fltr.FieldSpec.Path)
|
||||
if err := fltr.filter(obj); err != nil {
|
||||
s, _ := obj.String()
|
||||
return nil, errors.WrapPrefixf(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
|
||||
}
|
||||
@@ -159,3 +159,18 @@ func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) (bool, error) {
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func splitPath(path string) []string {
|
||||
ps := strings.Split(path, "/")
|
||||
var res []string
|
||||
res = append(res, ps[0])
|
||||
for i := 1; i < len(ps); i++ {
|
||||
lastIndex := len(res) - 1
|
||||
if strings.HasSuffix(res[lastIndex], "\\") {
|
||||
res[lastIndex] = strings.TrimSuffix(res[lastIndex], "\\") + "/" + ps[i]
|
||||
} else {
|
||||
res = append(res, ps[i])
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -435,6 +435,60 @@ spec:
|
||||
},
|
||||
error: "obj '' at path 'spec/containers/image': expected sequence or mapping node",
|
||||
},
|
||||
{
|
||||
name: "filedname with slash '/'",
|
||||
fieldSpec: `
|
||||
path: a/b\/c/d
|
||||
version: v1
|
||||
kind: Bar
|
||||
`,
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: Bar
|
||||
a:
|
||||
b/c:
|
||||
d: foo
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: Bar
|
||||
a:
|
||||
b/c:
|
||||
d: bar
|
||||
`,
|
||||
filter: fieldspec.Filter{
|
||||
SetValue: filtersutil.SetScalar("bar"),
|
||||
CreateKind: yaml.ScalarNode,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filedname with multiple '/'",
|
||||
fieldSpec: `
|
||||
path: a/b\/c/d\/e/f
|
||||
version: v1
|
||||
kind: Bar
|
||||
`,
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: Bar
|
||||
a:
|
||||
b/c:
|
||||
d/e:
|
||||
f: foo
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: Bar
|
||||
a:
|
||||
b/c:
|
||||
d/e:
|
||||
f: bar
|
||||
`,
|
||||
filter: fieldspec.Filter{
|
||||
SetValue: filtersutil.SetScalar("bar"),
|
||||
CreateKind: yaml.ScalarNode,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestFilter_Filter(t *testing.T) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
3
api/filters/refvar/doc.go
Normal file
3
api/filters/refvar/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package refvar contains a kio.Filter implementation of the kustomize
|
||||
// refvar transformer.
|
||||
package refvar
|
||||
101
api/filters/refvar/refvar.go
Normal file
101
api/filters/refvar/refvar.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package refvar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
|
||||
)
|
||||
|
||||
// Filter updates $(VAR) style variables with values.
|
||||
// The fieldSpecs are the places to look for occurrences of $(VAR).
|
||||
type Filter struct {
|
||||
MappingFunc func(string) interface{} `json:"mappingFunc,omitempty" yaml:"mappingFunc,omitempty"`
|
||||
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||
}
|
||||
|
||||
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:
|
||||
return f.setMap(node)
|
||||
case yaml.SequenceNode:
|
||||
return f.setSeq(node)
|
||||
default:
|
||||
return fmt.Errorf("invalid type encountered %v", node.YNode().Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func updateNodeValue(node *yaml.Node, newValue interface{}) {
|
||||
switch newValue := newValue.(type) {
|
||||
case int64:
|
||||
node.Value = strconv.FormatInt(newValue, 10)
|
||||
node.Tag = yaml.NodeTagInt
|
||||
case bool:
|
||||
node.SetString(strconv.FormatBool(newValue))
|
||||
node.Tag = yaml.NodeTagBool
|
||||
case float64:
|
||||
node.SetString(strconv.FormatFloat(newValue, 'f', -1, 64))
|
||||
node.Tag = yaml.NodeTagFloat
|
||||
default:
|
||||
node.SetString(newValue.(string))
|
||||
node.Tag = yaml.NodeTagString
|
||||
}
|
||||
node.Style = 0
|
||||
}
|
||||
|
||||
func (f Filter) setScalar(node *yaml.RNode) error {
|
||||
if !yaml.IsYNodeString(node.YNode()) {
|
||||
return nil
|
||||
}
|
||||
v := expansion2.Expand(node.YNode().Value, f.MappingFunc)
|
||||
updateNodeValue(node.YNode(), v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f Filter) setMap(node *yaml.RNode) error {
|
||||
contents := node.YNode().Content
|
||||
for i := 0; i < len(contents); i += 2 {
|
||||
if !yaml.IsYNodeString(contents[i]) {
|
||||
return fmt.Errorf("invalid map key: %s, type: %s", contents[i].Value, contents[i].Tag)
|
||||
}
|
||||
if !yaml.IsYNodeString(contents[i+1]) {
|
||||
continue
|
||||
}
|
||||
newValue := expansion2.Expand(contents[i+1].Value, f.MappingFunc)
|
||||
updateNodeValue(contents[i+1], newValue)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f Filter) setSeq(node *yaml.RNode) error {
|
||||
for _, item := range node.YNode().Content {
|
||||
if !yaml.IsYNodeString(item) {
|
||||
return fmt.Errorf("invalid value type expect a string")
|
||||
}
|
||||
newValue := expansion2.Expand(item.Value, f.MappingFunc)
|
||||
updateNodeValue(item, newValue)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
287
api/filters/refvar/refvar_test.go
Normal file
287
api/filters/refvar/refvar_test.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package refvar
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
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) {
|
||||
replacementCounts := make(map[string]int)
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expected string
|
||||
filter Filter
|
||||
}{
|
||||
"simple scalar": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: $(VAR)`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 5`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "spec/replicas"},
|
||||
},
|
||||
},
|
||||
"non-string scalar": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 1`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 1`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "spec/replicas"},
|
||||
},
|
||||
},
|
||||
"wrong path": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 1`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 1`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "a/b/c"},
|
||||
},
|
||||
},
|
||||
"sequence": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
- $(FOO)
|
||||
- $(BAR)
|
||||
- $(BAZ)
|
||||
- $(FOO)+$(BAR)
|
||||
- $(BOOL)
|
||||
- $(FLOAT)`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
- foo
|
||||
- bar
|
||||
- $(BAZ)
|
||||
- foo+bar
|
||||
- false
|
||||
- 1.23`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"FOO": "foo",
|
||||
"BAR": "bar",
|
||||
"BOOL": false,
|
||||
"FLOAT": 1.23,
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "data"},
|
||||
},
|
||||
},
|
||||
"maps": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
FOO: $(FOO)
|
||||
BAR: $(BAR)
|
||||
BAZ: $(BAZ)
|
||||
PLUS: $(FOO)+$(BAR)`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
FOO: foo
|
||||
BAR: bar
|
||||
BAZ: $(BAZ)
|
||||
PLUS: foo+bar`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"FOO": "foo",
|
||||
"BAR": "bar",
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "data"},
|
||||
},
|
||||
},
|
||||
"complicated case": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
slice1:
|
||||
- $(FOO)
|
||||
slice2:
|
||||
FOO: $(FOO)
|
||||
BAR: $(BAR)
|
||||
BOOL: false
|
||||
INT: 0
|
||||
SLICE:
|
||||
- $(FOO)`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
slice1:
|
||||
- $(FOO)
|
||||
slice2:
|
||||
FOO: foo
|
||||
BAR: bar
|
||||
BOOL: false
|
||||
INT: 0
|
||||
SLICE:
|
||||
- $(FOO)`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"FOO": "foo",
|
||||
"BAR": "bar",
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "data/slice2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterUnhappy(t *testing.T) {
|
||||
replacementCounts := make(map[string]int)
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedError string
|
||||
filter Filter
|
||||
}{
|
||||
"non-string in sequence": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
slice:
|
||||
- false`,
|
||||
expectedError: `obj 'apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
data:
|
||||
slice:
|
||||
- false
|
||||
' at path 'data/slice': invalid value type expect a string`,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "data/slice"},
|
||||
},
|
||||
},
|
||||
"invalid key in map": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
1: str`,
|
||||
expectedError: `obj 'apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
data:
|
||||
1: str
|
||||
' at path 'data': invalid map key: 1, type: ` + yaml.NodeTagInt,
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
}),
|
||||
FieldSpec: types.FieldSpec{Path: "data"},
|
||||
},
|
||||
},
|
||||
"null input": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
data:
|
||||
FOO: null`,
|
||||
expectedError: "obj '' at path 'data/FOO': invalid type encountered 0",
|
||||
filter: Filter{
|
||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}),
|
||||
FieldSpec: types.FieldSpec{Path: "data/FOO"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
_, err := filtertest_test.RunFilterE(t, tc.input, tc.filter)
|
||||
if !assert.EqualError(t, err, tc.expectedError) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
12
api/go.sum
12
api/go.sum
@@ -239,6 +239,7 @@ github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoD
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
@@ -438,6 +439,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -464,11 +466,11 @@ 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=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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=
|
||||
@@ -499,6 +501,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -554,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=
|
||||
@@ -573,6 +576,7 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
package accumulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/refvar"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/transform"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
)
|
||||
|
||||
type refVarTransformer struct {
|
||||
@@ -31,59 +30,6 @@ func newRefVarTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
// replaceVars accepts as 'in' a string, or string array, which can have
|
||||
// embedded instances of $VAR style variables, e.g. a container command string.
|
||||
// The function returns the string with the variables expanded to their final
|
||||
// values.
|
||||
func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
|
||||
switch vt := in.(type) {
|
||||
case []interface{}:
|
||||
var xs []interface{}
|
||||
for _, a := range in.([]interface{}) {
|
||||
x, ok := a.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected array of strings, found %v", in)
|
||||
}
|
||||
xs = append(xs, expansion2.Expand(x, rv.mappingFunc))
|
||||
}
|
||||
return xs, nil
|
||||
case map[string]interface{}:
|
||||
inMap := in.(map[string]interface{})
|
||||
xs := make(map[string]interface{}, len(inMap))
|
||||
for k, v := range inMap {
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
// This field can not contain a $(VAR) since it is not
|
||||
// of string type. For instance .spec.replicas: 3 in
|
||||
// a Deployment object
|
||||
xs[k] = v
|
||||
} else {
|
||||
// This field can potentially contains a $(VAR) since it is
|
||||
// of string type. For instance .spec.replicas: $(REPLICAS)
|
||||
// in a Deployment object
|
||||
xs[k] = expansion2.Expand(s, rv.mappingFunc)
|
||||
}
|
||||
}
|
||||
return xs, nil
|
||||
case interface{}:
|
||||
s, ok := in.(string)
|
||||
if !ok {
|
||||
// This field can not contain a $(VAR) since it is not of string type.
|
||||
return in, nil
|
||||
}
|
||||
// This field can potentially contain a $(VAR) since it is
|
||||
// of string type.
|
||||
return expansion2.Expand(s, rv.mappingFunc), nil
|
||||
// staticcheck erroneously claims that `case nil`
|
||||
// is unreachable here, so suppressing it.
|
||||
//nolint:staticcheck
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
return "", fmt.Errorf("invalid type encountered %T", vt)
|
||||
}
|
||||
}
|
||||
|
||||
// UnusedVars returns slice of Var names that were unused
|
||||
// after a Transform run.
|
||||
func (rv *refVarTransformer) UnusedVars() []string {
|
||||
@@ -104,12 +50,12 @@ func (rv *refVarTransformer) Transform(m resmap.ResMap) error {
|
||||
rv.replacementCounts, rv.varMap)
|
||||
for _, res := range m.Resources() {
|
||||
for _, fieldSpec := range rv.fieldSpecs {
|
||||
if res.OrgId().IsSelected(&fieldSpec.Gvk) {
|
||||
if err := transform.MutateField(
|
||||
res.Map(), fieldSpec.PathSlice(),
|
||||
false, rv.replaceVars); err != nil {
|
||||
return err
|
||||
}
|
||||
err := filtersutil.ApplyToJSON(refvar.Filter{
|
||||
MappingFunc: rv.mappingFunc,
|
||||
FieldSpec: fieldSpec,
|
||||
}, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/map"},
|
||||
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"},
|
||||
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/interface"},
|
||||
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/nil"},
|
||||
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/num"},
|
||||
},
|
||||
res: resmaptest_test.NewRmBuilder(
|
||||
@@ -63,7 +62,7 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
"item4": "$(BAZ)+$(BAZ)",
|
||||
"item5": "$(BOO)",
|
||||
"item6": "if $(BOO)",
|
||||
"item7": 2019,
|
||||
"item7": int64(2019),
|
||||
},
|
||||
"slice": []interface{}{
|
||||
"$(FOO)",
|
||||
@@ -74,8 +73,7 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
"if $(BOO)",
|
||||
},
|
||||
"interface": "$(FOO)",
|
||||
"nil": nil,
|
||||
"num": 2019,
|
||||
"num": int64(2019),
|
||||
}}).ResMap(),
|
||||
},
|
||||
expected: expected{
|
||||
@@ -95,7 +93,7 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
"item4": "5+5",
|
||||
"item5": true,
|
||||
"item6": "if true",
|
||||
"item7": 2019,
|
||||
"item7": int64(2019),
|
||||
},
|
||||
"slice": []interface{}{
|
||||
"replacementForFoo",
|
||||
@@ -106,8 +104,7 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
"if true",
|
||||
},
|
||||
"interface": "replacementForFoo",
|
||||
"nil": nil,
|
||||
"num": 2019,
|
||||
"num": int64(2019),
|
||||
}}).ResMap(),
|
||||
unused: []string{"BAR"},
|
||||
},
|
||||
@@ -131,7 +128,29 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
"slice": []interface{}{5}, // noticeably *not* a []string
|
||||
}}).ResMap(),
|
||||
},
|
||||
errMessage: "expected array of strings, found [5]",
|
||||
errMessage: `obj '{"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}}
|
||||
' at path 'data/slice': invalid value type expect a string`,
|
||||
},
|
||||
{
|
||||
description: "var replacement panic in nil",
|
||||
given: given{
|
||||
varMap: map[string]interface{}{},
|
||||
fs: []types.FieldSpec{
|
||||
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/nil"},
|
||||
},
|
||||
res: resmaptest_test.NewRmBuilder(
|
||||
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
|
||||
Add(map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
"data": map[string]interface{}{
|
||||
"nil": nil, // noticeably *not* a []string
|
||||
}}).ResMap(),
|
||||
},
|
||||
errMessage: `obj '' at path 'data/nil': invalid type encountered 0`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func RunFilter(t *testing.T, input string, f kio.Filter) string {
|
||||
func run(input string, f kio.Filter) (string, error) {
|
||||
var out bytes.Buffer
|
||||
rw := kio.ByteReadWriter{
|
||||
Reader: bytes.NewBufferString(input),
|
||||
@@ -23,8 +23,26 @@ func RunFilter(t *testing.T, input string, f kio.Filter) string {
|
||||
Filters: []kio.Filter{f},
|
||||
Outputs: []kio.Writer{&rw},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
// RunFilter runs filter and panic if there is error
|
||||
func RunFilter(t *testing.T, input string, f kio.Filter) string {
|
||||
output, err := run(input, f)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return out.String()
|
||||
return output
|
||||
}
|
||||
|
||||
// RunFilterE runs filter and return error if there is
|
||||
func RunFilterE(t *testing.T, input string, f kio.Filter) (string, error) {
|
||||
output, err := run(input, f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@@ -15,5 +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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
@@ -52,7 +56,7 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
||||
"use this version of the setter format")
|
||||
set.Flags().BoolVar(&r.CreateSetter.Required, "required", false,
|
||||
"indicates that this setter must be set by package consumer before live apply/preview")
|
||||
set.Flags().StringVar(&r.CreateSetter.SchemaPath, "schema-path", "",
|
||||
set.Flags().StringVar(&r.SchemaPath, "schema-path", "",
|
||||
`openAPI schema file path for setter constraints -- file content `+
|
||||
`e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`)
|
||||
set.Flags().MarkHidden("version")
|
||||
@@ -70,6 +74,7 @@ type CreateSetterRunner struct {
|
||||
Set setters.CreateSetter
|
||||
CreateSetter settersutil.SetterCreator
|
||||
OpenAPIFile string
|
||||
SchemaPath string
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||
@@ -95,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)
|
||||
@@ -125,10 +134,28 @@ 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
|
||||
|
||||
err = r.processSchema()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.CreateSetter.Type == "array" {
|
||||
if !c.Flag("field").Changed {
|
||||
return errors.Errorf("field flag must be set for array type setters")
|
||||
@@ -138,6 +165,51 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) processSchema() error {
|
||||
sc, err := schemaFromFile(r.SchemaPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flagType := r.CreateSetter.Type
|
||||
var schemaType string
|
||||
switch {
|
||||
// json schema allows more than one type to be specified, but openapi
|
||||
// only allows one. So we follow the openapi convention.
|
||||
case len(sc.Type) > 1:
|
||||
return errors.Errorf("only one type is supported: %s",
|
||||
strings.Join(sc.Type, ", "))
|
||||
case len(sc.Type) == 1:
|
||||
schemaType = sc.Type[0]
|
||||
}
|
||||
|
||||
// Since type can be set both through the schema file and through the
|
||||
// --type flag, we make sure the same value is set in both places. If they
|
||||
// are both set with different values, we return an error.
|
||||
switch {
|
||||
case flagType == "" && schemaType != "":
|
||||
r.CreateSetter.Type = schemaType
|
||||
case flagType != "" && schemaType == "":
|
||||
sc.Type = []string{flagType}
|
||||
case flagType != "" && schemaType != "":
|
||||
if flagType != schemaType {
|
||||
return errors.Errorf("type provided in type flag (%s) and in schema (%s) doesn't match",
|
||||
r.CreateSetter.Type, sc.Type[0])
|
||||
}
|
||||
}
|
||||
|
||||
// Only marshal the properties in SchemaProps. This means any fields in
|
||||
// the schema file that isn't recognized will be dropped.
|
||||
// TODO: Consider if we should return an error here instead of just dropping
|
||||
// the unknown fields.
|
||||
b, err := json.Marshal(sc.SchemaProps)
|
||||
if err != nil {
|
||||
return errors.Errorf("error marshalling schema: %v", err)
|
||||
}
|
||||
r.CreateSetter.Schema = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
|
||||
if setterVersion == "v2" {
|
||||
return r.CreateSetter.Create(r.OpenAPIFile, args[0])
|
||||
@@ -153,3 +225,25 @@ func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// schemaFromFile reads the contents from schemaPath and returns schema
|
||||
func schemaFromFile(schemaPath string) (*spec.Schema, error) {
|
||||
sc := &spec.Schema{}
|
||||
if schemaPath == "" {
|
||||
return sc, nil
|
||||
}
|
||||
sch, err := ioutil.ReadFile(schemaPath)
|
||||
if err != nil {
|
||||
return sc, err
|
||||
}
|
||||
|
||||
if len(sch) == 0 {
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
err = sc.UnmarshalJSON(sch)
|
||||
if err != nil {
|
||||
return sc, errors.Errorf("unable to parse schema: %v", err)
|
||||
}
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
@@ -461,6 +509,229 @@ metadata:
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: my-hub # {"type":"","x-kustomize":{"setter":{"name":"hubsetter","value":"my-hub"}}}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "provide different type values in schema and with flag",
|
||||
args: []string{"replicas", "3", "--description", "hello world", "--type", "string"},
|
||||
schema: `{"type": "integer"}`,
|
||||
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
|
||||
`,
|
||||
err: `type provided in type flag (string) and in schema (integer) doesn't match`,
|
||||
},
|
||||
{
|
||||
name: "invalid json in schema",
|
||||
args: []string{"replicas", "3"},
|
||||
schema: `{"foo": bar`,
|
||||
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
|
||||
`,
|
||||
err: "unable to parse schema: invalid character 'b' looking for beginning of value",
|
||||
},
|
||||
{
|
||||
name: "unknown fields in schema are dropped",
|
||||
args: []string{"replicas", "3"},
|
||||
schema: `{"foo": "bar"}`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "unknown types are not accepted",
|
||||
args: []string{"replicas", "3"},
|
||||
schema: `{"type": "int"}`,
|
||||
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
|
||||
`,
|
||||
err: `invalid schema: type "int" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`,
|
||||
},
|
||||
{
|
||||
name: "unknown types are not accepted in nested structures",
|
||||
args: []string{"replicas", "3", "--field", "replicas"},
|
||||
schema: `{"maxItems": 3, "type": "array", "items": {"type": "foo"}}`,
|
||||
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
|
||||
`,
|
||||
err: `invalid schema: type "foo" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`,
|
||||
},
|
||||
{
|
||||
name: "unknown types are not accepted in --type flag",
|
||||
args: []string{"replicas", "3", "--type", "bar"},
|
||||
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
|
||||
`,
|
||||
err: `invalid schema: type "bar" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`,
|
||||
},
|
||||
{
|
||||
name: "unknown properties in schema are dropped",
|
||||
args: []string{"replicas", "3", "--type", "integer"},
|
||||
schema: `{"maximum": 3, "unknown": 42}`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
maximum: 3
|
||||
type: integer
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
|
||||
)
|
||||
@@ -98,7 +99,7 @@ func runCreate(opts createFlags, fSys filesys.FileSystem, uf ifc.KunstructuredFa
|
||||
var resources []string
|
||||
var err error
|
||||
if opts.resources != "" {
|
||||
resources, err = util.GlobPatterns(fSys, strings.Split(opts.resources, ","))
|
||||
resources, err = util.GlobPatternsWithLoader(fSys, loader.NewFileLoaderAtCwd(fSys), strings.Split(opts.resources, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
79
kustomize/internal/commands/edit/add/addcomponent.go
Normal file
79
kustomize/internal/commands/edit/add/addcomponent.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package add
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
|
||||
)
|
||||
|
||||
type addComponentOptions struct {
|
||||
componentFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddComponent adds the name of a file containing a component to the kustomization file.
|
||||
func newCmdAddComponent(fSys filesys.FileSystem) *cobra.Command {
|
||||
var o addComponentOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "component",
|
||||
Short: "Add the name of a file containing a component to the kustomization file.",
|
||||
Example: `
|
||||
add component {filepath}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunAddComponent(fSys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates addComponent command.
|
||||
func (o *addComponentOptions) Validate(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("must specify a component file")
|
||||
}
|
||||
o.componentFilePaths = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAddComponent runs addComponent command (do real work).
|
||||
func (o *addComponentOptions) RunAddComponent(fSys filesys.FileSystem) error {
|
||||
components, err := util.GlobPatternsWithLoader(fSys, loader.NewFileLoaderAtCwd(fSys), o.componentFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(components) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mf, err := kustfile.NewKustomizationFile(fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := mf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, component := range components {
|
||||
if kustfile.StringInSlice(component, m.Components) {
|
||||
log.Printf("component %s already in kustomization file", component)
|
||||
continue
|
||||
}
|
||||
m.Components = append(m.Components, component)
|
||||
}
|
||||
|
||||
return mf.Write(m)
|
||||
}
|
||||
76
kustomize/internal/commands/edit/add/addcomponent_test.go
Normal file
76
kustomize/internal/commands/edit/add/addcomponent_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package add
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/testutils"
|
||||
)
|
||||
|
||||
const (
|
||||
componentFileName = "myWonderfulComponent.yaml"
|
||||
componentFileContent = `
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
`
|
||||
)
|
||||
|
||||
func TestAddComponentHappyPath(t *testing.T) {
|
||||
fSys := filesys.MakeEmptyDirInMemory()
|
||||
fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
||||
fSys.WriteFile(componentFileName+"another", []byte(componentFileContent))
|
||||
testutils_test.WriteTestKustomization(fSys)
|
||||
|
||||
cmd := newCmdAddComponent(fSys)
|
||||
args := []string{componentFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := testutils_test.ReadTestKustomization(fSys)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(content), componentFileName) {
|
||||
t.Errorf("expected component name in kustomization")
|
||||
}
|
||||
if !strings.Contains(string(content), componentFileName+"another") {
|
||||
t.Errorf("expected component name in kustomization")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddComponentAlreadyThere(t *testing.T) {
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
||||
testutils_test.WriteTestKustomization(fSys)
|
||||
|
||||
cmd := newCmdAddComponent(fSys)
|
||||
args := []string{componentFileName}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
|
||||
// adding an existing component doesn't return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error :%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddComponentNoArgs(t *testing.T) {
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
|
||||
cmd := newCmdAddComponent(fSys)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify a component file" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
|
||||
)
|
||||
@@ -48,7 +49,7 @@ func (o *addResourceOptions) Validate(args []string) error {
|
||||
|
||||
// RunAddResource runs addResource command (do real work).
|
||||
func (o *addResourceOptions) RunAddResource(fSys filesys.FileSystem) error {
|
||||
resources, err := util.GlobPatterns(fSys, o.resourceFilePaths)
|
||||
resources, err := util.GlobPatternsWithLoader(fSys, loader.NewFileLoaderAtCwd(fSys), o.resourceFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ func NewCmdAdd(
|
||||
# Adds a patch to the kustomization
|
||||
kustomize edit add patch <filepath>
|
||||
|
||||
# Adds a component to the kustomization
|
||||
kustomize edit add component <filepath>
|
||||
|
||||
# Adds one or more base directories to the kustomization
|
||||
kustomize edit add base <filepath>
|
||||
kustomize edit add base <filepath1>,<filepath2>,<filepath3>
|
||||
@@ -46,6 +49,7 @@ func NewCmdAdd(
|
||||
c.AddCommand(
|
||||
newCmdAddResource(fSys),
|
||||
newCmdAddPatch(fSys),
|
||||
newCmdAddComponent(fSys),
|
||||
newCmdAddSecret(fSys, ldr, kf),
|
||||
newCmdAddConfigMap(fSys, ldr, kf),
|
||||
newCmdAddBase(fSys),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
)
|
||||
|
||||
// GlobPatterns accepts a slice of glob strings and returns the set of
|
||||
@@ -29,6 +30,32 @@ func GlobPatterns(fSys filesys.FileSystem, patterns []string) ([]string, error)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GlobPatterns accepts a slice of glob strings and returns the set of
|
||||
// matching file paths. If files are not found, will try load from remote.
|
||||
func GlobPatternsWithLoader(fSys filesys.FileSystem, ldr ifc.Loader, patterns []string) ([]string, error) {
|
||||
var result []string
|
||||
for _, pattern := range patterns {
|
||||
files, err := fSys.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
loader, err := ldr.New(pattern)
|
||||
if err != nil {
|
||||
log.Printf("%s has no match", pattern)
|
||||
} else {
|
||||
result = append(result, pattern)
|
||||
if loader != nil {
|
||||
loader.Cleanup()
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
result = append(result, files...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ConvertToMap converts a slice of strings in the form of
|
||||
// `key:value` into a map.
|
||||
func ConvertToMap(input string, kind string) (map[string]string, error) {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
)
|
||||
|
||||
func TestConvertToMap(t *testing.T) {
|
||||
@@ -35,3 +39,59 @@ func TestConvertToMapError(t *testing.T) {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobPatternsWithLoaderRemoteFile(t *testing.T) {
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
fSys.Create("test.yml")
|
||||
httpPath := "https://example.com/example.yaml"
|
||||
ldr := fakeLoader{
|
||||
path: httpPath,
|
||||
}
|
||||
|
||||
// test load remote file
|
||||
resources, err := GlobPatternsWithLoader(fSys, ldr, []string{httpPath})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error: %v", err)
|
||||
}
|
||||
if len(resources) != 1 || resources[0] != httpPath {
|
||||
t.Fatalf("incorrect resources: %v", resources)
|
||||
}
|
||||
|
||||
// test load local and remote file
|
||||
resources, err = GlobPatternsWithLoader(fSys, ldr, []string{httpPath, "/test.yml"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error: %v", err)
|
||||
}
|
||||
if len(resources) != 2 || resources[0] != httpPath || resources[1] != "/test.yml" {
|
||||
t.Fatalf("incorrect resources: %v", resources)
|
||||
}
|
||||
|
||||
// test load invalid file
|
||||
resources, err = GlobPatternsWithLoader(fSys, ldr, []string{"http://invalid"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error: %v", err)
|
||||
}
|
||||
if len(resources) > 0 {
|
||||
t.Fatalf("incorrect resources: %v", resources)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeLoader struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (l fakeLoader) Root() string {
|
||||
return ""
|
||||
}
|
||||
func (l fakeLoader) New(newRoot string) (ifc.Loader, error) {
|
||||
if newRoot == l.path {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s not exist", newRoot)
|
||||
}
|
||||
func (l fakeLoader) Load(location string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (l fakeLoader) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
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"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ package settersutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"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
|
||||
@@ -25,7 +28,7 @@ type SetterCreator struct {
|
||||
|
||||
Type string
|
||||
|
||||
SchemaPath string
|
||||
Schema string
|
||||
|
||||
// FieldName if set will add the OpenAPI reference to fields with this name or path
|
||||
// FieldName may be the full name of the field, full path to the field, or the path suffix.
|
||||
@@ -43,12 +46,22 @@ 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 {
|
||||
schema, err := schemaFromFile(c.SchemaPath)
|
||||
err := validateSchema(c.Schema)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Errorf("invalid schema: %v", err)
|
||||
}
|
||||
|
||||
// Update the resources with the setter reference
|
||||
@@ -74,7 +87,7 @@ func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
// Update the OpenAPI definitions to hace the setter
|
||||
sd := setters2.SetterDefinition{
|
||||
Name: c.Name, Value: c.FieldValue, SetBy: c.SetBy, Description: c.Description,
|
||||
Type: c.Type, Schema: schema, Required: c.Required,
|
||||
Type: c.Type, Schema: c.Schema, Required: c.Required,
|
||||
}
|
||||
if err := sd.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
@@ -97,14 +110,57 @@ func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// schemaFromFile reads the contents from schemaPath and returns schema
|
||||
func schemaFromFile(schemaPath string) (string, error) {
|
||||
if schemaPath == "" {
|
||||
return "", nil
|
||||
}
|
||||
sch, err := ioutil.ReadFile(schemaPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(sch), nil
|
||||
// The types recognized by by the go openapi validation library:
|
||||
// https://github.com/go-openapi/validate/blob/master/helpers.go#L35
|
||||
var validTypeValues = []string{
|
||||
"object", "array", "string", "integer", "number", "boolean", "file", "null",
|
||||
}
|
||||
|
||||
// validateSchema parses the provided schema and validates it.
|
||||
func validateSchema(schema string) error {
|
||||
var sc spec.Schema
|
||||
err := sc.UnmarshalJSON([]byte(schema))
|
||||
if err != nil {
|
||||
return errors.Errorf("unable to parse schema: %v", err)
|
||||
}
|
||||
return validateSchemaTypes(&sc)
|
||||
}
|
||||
|
||||
// validateSchemaTypes traverses the schema and checks that only valid types
|
||||
// are used.
|
||||
func validateSchemaTypes(sc *spec.Schema) error {
|
||||
if len(sc.Type) > 1 {
|
||||
return errors.Errorf("only one type is supported: %s", strings.Join(sc.Type, ", "))
|
||||
}
|
||||
|
||||
if len(sc.Type) == 1 {
|
||||
t := sc.Type[0]
|
||||
var match bool
|
||||
for _, validType := range validTypeValues {
|
||||
if t == validType {
|
||||
match = true
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
return errors.Errorf("type %q is not supported. Must be one of: %s",
|
||||
t, strings.Join(validTypeValues, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
if items := sc.Items; items != nil {
|
||||
if items.Schema != nil {
|
||||
err := validateSchemaTypes(items.Schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range items.Schemas {
|
||||
schema := items.Schemas[i]
|
||||
err := validateSchemaTypes(&schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
`,
|
||||
},
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user