mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-25 15:58:18 +00:00
Compare commits
45 Commits
monopole-p
...
cmd/config
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c467f48bf2 | ||
|
|
054f56ceaf | ||
|
|
49ebb3f155 | ||
|
|
fa507f782f | ||
|
|
da548f65ea | ||
|
|
8991b193c6 | ||
|
|
3c776b3435 | ||
|
|
cf61a360e0 | ||
|
|
a588f498ea | ||
|
|
573d7b7234 | ||
|
|
1d94ee86df | ||
|
|
275bf05ae2 | ||
|
|
d70f3a7958 | ||
|
|
5707962df5 | ||
|
|
63c06eeaf1 | ||
|
|
b3ce3a7882 | ||
|
|
a8b5ec2c61 | ||
|
|
d190e1c18a | ||
|
|
1d988a0fd8 | ||
|
|
6f176b1507 | ||
|
|
d5a107074d | ||
|
|
d8bd6db880 | ||
|
|
d27135e3a3 | ||
|
|
282b1fa49a | ||
|
|
87b680e1c0 | ||
|
|
584eb236fd | ||
|
|
31a193f60f | ||
|
|
bf17b244e5 | ||
|
|
8338299529 | ||
|
|
7adf7eb271 | ||
|
|
06b091b175 | ||
|
|
bf03669e94 | ||
|
|
7f52c814a8 | ||
|
|
d5aac922d9 | ||
|
|
9a62d866f3 | ||
|
|
b2812838bf | ||
|
|
e59e477702 | ||
|
|
4264ad0ad4 | ||
|
|
2554cd00eb | ||
|
|
383244cd63 | ||
|
|
b9da33afd4 | ||
|
|
23e339b86c | ||
|
|
9555009df8 | ||
|
|
91a10c560c | ||
|
|
780cb19c4d |
43
.travis.yml
43
.travis.yml
@@ -1,43 +0,0 @@
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
# TODO: Speed up the slowness of the osx travis runs
|
||||
# Maybe cache brew installs?
|
||||
#
|
||||
# TODO: Uncomment when some gets the tests running on Windows.
|
||||
# - windows
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- tree
|
||||
homebrew:
|
||||
packages:
|
||||
- tree
|
||||
update: true
|
||||
|
||||
# Only clone the most recent commit.
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.13"
|
||||
|
||||
go_import_path: sigs.k8s.io/kustomize
|
||||
|
||||
before_install:
|
||||
- source ./travis/consider-early-travis-exit.sh
|
||||
|
||||
# Skip the install process; let pre-commit.sh do it.
|
||||
install: true
|
||||
|
||||
script:
|
||||
- make verify-kustomize
|
||||
- ./travis/kyaml-pre-commit.sh
|
||||
- ./travis/check-go-mod.sh
|
||||
|
||||
# TBD. Suppressing for now.
|
||||
notifications:
|
||||
email: false
|
||||
@@ -114,7 +114,7 @@ type ResMap interface {
|
||||
Append(*resource.Resource) error
|
||||
|
||||
// AppendAll appends another ResMap to self,
|
||||
// failing on any OrgId collision.
|
||||
// failing on any CurId collision.
|
||||
AppendAll(ResMap) error
|
||||
|
||||
// AbsorbAll appends, replaces or merges the contents
|
||||
|
||||
@@ -10,7 +10,5 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.4.0
|
||||
k8s.io/apimachinery v0.17.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.0
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml v0.0.0 => ../../kyaml
|
||||
|
||||
@@ -156,13 +156,17 @@ 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 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/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=
|
||||
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
|
||||
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.0 h1:bpdlb136YAqxeL6+ziESe34SXRSBBpiUvoTu2dQG5Ss=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.0/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -38,8 +38,7 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
||||
"kind of the Resource on which to create the setter.")
|
||||
set.Flags().MarkHidden("kind")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Type, "type", "",
|
||||
"valid OpenAPI field type -- e.g. integer,boolean,string.")
|
||||
set.Flags().MarkHidden("type")
|
||||
"OpenAPI field type for the setter -- e.g. integer,boolean,string.")
|
||||
set.Flags().BoolVar(&r.Set.SetPartialField.Partial, "partial", false,
|
||||
"create a partial setter for only part of the field value.")
|
||||
set.Flags().MarkHidden("partial")
|
||||
@@ -89,6 +88,7 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||
r.CreateSetter.Description = r.Set.SetPartialField.Description
|
||||
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
|
||||
r.CreateSetter.Type = r.Set.SetPartialField.Type
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -102,6 +102,72 @@ spec:
|
||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitution and create setters 1",
|
||||
args: []string{
|
||||
"image", "something/nginx::1.7.9/nginxotherthing", "--pattern", "something/IMAGE::TAG/nginxotherthing",
|
||||
"--value", "IMAGE=image", "--value", "TAG=tag"},
|
||||
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
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: nginx
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: 1.7.9
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: something/IMAGE::TAG/nginxotherthing
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
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 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -109,7 +109,6 @@ openAPI:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
setBy: me
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -9,6 +9,7 @@ require (
|
||||
github.com/sergi/go-diff v1.1.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.7
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2
|
||||
)
|
||||
|
||||
10
kyaml/go.sum
10
kyaml/go.sum
@@ -39,18 +39,20 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
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-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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/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=
|
||||
|
||||
@@ -104,6 +104,17 @@ type SetterDefinition struct {
|
||||
|
||||
// Count is the number of fields set by this setter.
|
||||
Count int `yaml:"count,omitempty"`
|
||||
|
||||
// Type is the type of the setter value.
|
||||
Type string `yaml:"type,omitempty"`
|
||||
|
||||
// EnumValues is a map of possible setter values to actual field values.
|
||||
// If EnumValues is specified, then the value set the by user 1) MUST
|
||||
// be present in the enumValues map as a key, and 2) the map entry value
|
||||
// MUST be used as the value to set in the configuration (rather than the key)
|
||||
// Example -- may be used for t-shirt sizing values by allowing cpu to be
|
||||
// set to small, medium or large, and then mapping these values to cpu values -- 0.5, 2, 8
|
||||
EnumValues map[string]string `yaml:"enumValues,omitempty"`
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) AddToFile(path string) error {
|
||||
@@ -127,6 +138,15 @@ func (sd SetterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
sd.Description = ""
|
||||
}
|
||||
|
||||
if sd.Type != "" {
|
||||
err = def.PipeE(yaml.FieldSetter{Name: "type", StringValue: sd.Type})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// don't write the type to the extension
|
||||
sd.Type = ""
|
||||
}
|
||||
|
||||
ext, err := def.Pipe(yaml.LookupCreate(yaml.MappingNode, K8sCliExtensionKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -30,7 +30,7 @@ func (s *Set) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
// visitScalar
|
||||
func (s *Set) visitScalar(object *yaml.RNode, p string) error {
|
||||
// get the openAPI for this field describing how to apply the setter
|
||||
ext, err := getExtFromComment(object)
|
||||
ext, sch, err := getExtFromComment(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -39,13 +39,13 @@ func (s *Set) visitScalar(object *yaml.RNode, p string) error {
|
||||
}
|
||||
|
||||
// perform a direct set of the field if it matches
|
||||
if s.set(object, ext) {
|
||||
if s.set(object, ext, sch) {
|
||||
s.Count++
|
||||
return nil
|
||||
}
|
||||
|
||||
// perform a substitution of the field if it matches
|
||||
sub, err := s.substitute(object, ext)
|
||||
sub, err := s.substitute(object, ext, sch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func (s *Set) visitScalar(object *yaml.RNode, p string) error {
|
||||
|
||||
// substitute updates the value of field from ext if ext contains a substitution that
|
||||
// depends on a setter whose name matches s.Name.
|
||||
func (s *Set) substitute(field *yaml.RNode, ext *cliExtension) (bool, error) {
|
||||
func (s *Set) substitute(field *yaml.RNode, ext *cliExtension, _ *spec.Schema) (bool, error) {
|
||||
nameMatch := false
|
||||
|
||||
// check partial setters to see if they contain the setter as part of a
|
||||
@@ -86,8 +86,15 @@ func (s *Set) substitute(field *yaml.RNode, ext *cliExtension) (bool, error) {
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
// substitute the setters current value into the substitution pattern
|
||||
p = strings.ReplaceAll(p, v.Marker, subSetter.Setter.Value)
|
||||
|
||||
if val, found := subSetter.Setter.EnumValues[subSetter.Setter.Value]; found {
|
||||
// the setter has an enum-map. we should replace the marker with the
|
||||
// enum value looked up from the map rather than the enum key
|
||||
p = strings.ReplaceAll(p, v.Marker, val)
|
||||
} else {
|
||||
// substitute the setters current value into the substitution pattern
|
||||
p = strings.ReplaceAll(p, v.Marker, subSetter.Setter.Value)
|
||||
}
|
||||
|
||||
if subSetter.Setter.Name == s.Name {
|
||||
// the substitution depends on the specified setter
|
||||
@@ -102,11 +109,15 @@ func (s *Set) substitute(field *yaml.RNode, ext *cliExtension) (bool, error) {
|
||||
// TODO(pwittrock): validate the field value
|
||||
|
||||
field.YNode().Value = p
|
||||
|
||||
// substitutions are always strings
|
||||
field.YNode().Tag = "!!str"
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// set applies the value from ext to field if its name matches s.Name
|
||||
func (s *Set) set(field *yaml.RNode, ext *cliExtension) bool {
|
||||
func (s *Set) set(field *yaml.RNode, ext *cliExtension, sch *spec.Schema) bool {
|
||||
// check full setter
|
||||
if ext.Setter == nil || ext.Setter.Name != s.Name {
|
||||
return false
|
||||
@@ -114,8 +125,18 @@ func (s *Set) set(field *yaml.RNode, ext *cliExtension) bool {
|
||||
|
||||
// TODO(pwittrock): validate the field value
|
||||
|
||||
if val, found := ext.Setter.EnumValues[ext.Setter.Value]; found {
|
||||
// the setter has an enum-map. we should replace the marker with the
|
||||
// enum value looked up from the map rather than the enum key
|
||||
field.YNode().Value = val
|
||||
return true
|
||||
}
|
||||
|
||||
// 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)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -146,14 +167,50 @@ func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
if def == nil {
|
||||
return nil, errors.Errorf("no setter %s found", s.Name)
|
||||
}
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "value", StringValue: s.Value}); err != nil {
|
||||
|
||||
// if the setter contains an enumValues map, then ensure the set value appears
|
||||
// as a key in the map
|
||||
if values, err := def.Pipe(yaml.Lookup("enumValues")); err != nil {
|
||||
// error looking up the enumValues
|
||||
return nil, err
|
||||
} else if values != nil {
|
||||
// contains enumValues map -- validate the set value against the map entries
|
||||
|
||||
// get the enumValues keys
|
||||
fields, err := values.Fields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// search for the user provided value in the set of allowed values
|
||||
var match bool
|
||||
for i := range fields {
|
||||
if fields[i] == s.Value {
|
||||
// found a match, we are good
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
// no match found -- provide an informative error to the user
|
||||
return nil, errors.Errorf("%s does not match the possible values for %s: [%s]",
|
||||
s.Value, s.Name, strings.Join(fields, ","))
|
||||
}
|
||||
}
|
||||
|
||||
v := yaml.NewScalarRNode(s.Value)
|
||||
// values are always represented as strings the OpenAPI
|
||||
// since the are unmarshalled into strings. Use double quote style to
|
||||
// ensure this consistently.
|
||||
v.YNode().Tag = "!!str"
|
||||
v.YNode().Style = yaml.DoubleQuotedStyle
|
||||
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "value", Value: v}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.SetBy != "" {
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "setBy", StringValue: s.SetBy}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "setBy", StringValue: s.SetBy}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.Description != "" {
|
||||
|
||||
@@ -15,11 +15,12 @@ import (
|
||||
|
||||
func TestSet_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
setter string
|
||||
openapi string
|
||||
input string
|
||||
expected string
|
||||
name string
|
||||
description string
|
||||
setter string
|
||||
openapi string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "set-replicas",
|
||||
@@ -58,6 +59,184 @@ metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-foo-type",
|
||||
description: "if a type is specified for a setter, ensure the field is properly quoted",
|
||||
setter: "foo",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.foo:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: foo
|
||||
value: "4"
|
||||
type: string
|
||||
`,
|
||||
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-foo-type-wrong",
|
||||
description: "if a type is specified for a setter, for the field to the type",
|
||||
setter: "foo",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.foo:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: foo
|
||||
value: "4"
|
||||
type: boolean
|
||||
`,
|
||||
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: !!bool 4 # {"$ref": "#/definitions/io.k8s.cli.setters.foo"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-foo-no-type",
|
||||
description: "if a type is not specified for a setter, keep the existing quoting",
|
||||
setter: "foo",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.foo:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: foo
|
||||
value: "4"
|
||||
`,
|
||||
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-replicas-enum",
|
||||
setter: "replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "medium"
|
||||
enumValues:
|
||||
small: "1"
|
||||
medium: "5"
|
||||
large: "50"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 1 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 5 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-enum-large",
|
||||
setter: "replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "large"
|
||||
enumValues:
|
||||
small: "1"
|
||||
medium: "5"
|
||||
large: "50"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 1 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 50 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -155,6 +334,61 @@ spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitute-image-name-enum",
|
||||
setter: "image-tag",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image-name:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-name
|
||||
value: "helloworld"
|
||||
enumValues:
|
||||
nginx: gcr.io/nginx
|
||||
helloworld: us.gcr.io/helloworld
|
||||
io.k8s.cli.setters.image-tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-tag
|
||||
value: "1.8.1"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG
|
||||
values:
|
||||
- marker: "IMAGE_NAME"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
||||
- marker: "IMAGE_TAG"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: us.gcr.io/helloworld:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -491,6 +725,29 @@ openAPI:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-annotation-quoted",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: 4
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -583,6 +840,137 @@ openAPI:
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-set-by-empty",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
setBy: "package-default"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
setBy: "package-default"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
setBy: "package-default"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
setBy: "package-default"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
setBy: "package-default"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-with-enum",
|
||||
setter: "replicas",
|
||||
value: "baz",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
setBy: "package-default"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "foo"
|
||||
enumValues:
|
||||
foo: bar
|
||||
baz: biz
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
setBy: "package-default"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
setBy: "package-default"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "baz"
|
||||
enumValues:
|
||||
foo: bar
|
||||
baz: biz
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
setBy: "package-default"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-fail",
|
||||
setter: "replicas",
|
||||
value: "hello",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
setBy: "package-default"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "foo"
|
||||
enumValues:
|
||||
foo: bar
|
||||
baz: biz
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
setBy: "package-default"
|
||||
`,
|
||||
err: "hello does not match the possible values for replicas: [foo,baz]",
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
setter: "replicas",
|
||||
|
||||
@@ -19,6 +19,8 @@ type SetterCreator struct {
|
||||
|
||||
SetBy string
|
||||
|
||||
Type 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.
|
||||
// e.g. all of the following would match spec.template.spec.containers.image --
|
||||
@@ -35,7 +37,9 @@ type SetterCreator struct {
|
||||
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, Description: c.Description, SetBy: c.SetBy}
|
||||
Name: c.Name, Value: c.FieldValue, Description: c.Description, SetBy: c.SetBy,
|
||||
Type: c.Type,
|
||||
}
|
||||
if err := sd.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// SubstitutionCreator creates or updates a substitution in the OpenAPI definitions, and
|
||||
@@ -40,6 +44,12 @@ func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
Values: c.Values,
|
||||
Pattern: c.Pattern,
|
||||
}
|
||||
|
||||
err := c.CreateSettersForSubstitution(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -62,3 +72,187 @@ func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
y, err := yaml.ReadFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := c.GetValuesForMarkers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// for each ref in values, check if the setter already exists, if not create them
|
||||
for _, value := range c.Values {
|
||||
obj, err := y.Pipe(yaml.Lookup(
|
||||
// get the setter key from ref. Ex: from #/definitions/io.k8s.cli.setters.image_setter
|
||||
// extract io.k8s.cli.setters.image_setter
|
||||
"openAPI", "definitions", strings.TrimPrefix(value.Ref, "#/definitions/")))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
sd := setters2.SetterDefinition{
|
||||
// get the setter name from ref. Ex: from #/definitions/io.k8s.cli.setters.image_setter
|
||||
// extract image_setter
|
||||
Name: strings.TrimPrefix(value.Ref, "#/definitions/io.k8s.cli.setters."),
|
||||
Value: m[value.Marker],
|
||||
}
|
||||
err := sd.AddToFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValuesForMarkers parses the pattern and field value to derive values for the
|
||||
// markers in the pattern string. Returns error if the marker values can't be derived
|
||||
func (c SubstitutionCreator) GetValuesForMarkers() (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
indices, err := c.GetStartIndices()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fv := c.FieldValue
|
||||
pattern := c.Pattern
|
||||
fvInd := 0
|
||||
patternInd := 0
|
||||
// iterate fv, pattern with indices fvInd, patternInd respectively and when patternInd hits the index of a marker,
|
||||
// freeze patternInd and iterate fvInd and capture string till we find the substring just after current marker
|
||||
// and before next marker
|
||||
|
||||
// Ex: fv = "something/ubuntu:0.1.0", pattern = "something/IMAGE:VERSION", till patternInd reaches 10
|
||||
// just proceed fvInd and patternInd and check if fv[fvInd]==pattern[patternInd] when patternInd is 10,
|
||||
// freeze patternInd and move fvInd till it sees substring ':' which derives IMAGE = ubuntu and so on.
|
||||
for fvInd < len(fv) && patternInd < len(pattern) {
|
||||
// if we hit marker index, extract its corresponding value
|
||||
if marker, ok := indices[patternInd]; ok {
|
||||
// increment the patternInd to end of marker. This helps us to extract the substring before next marker.
|
||||
patternInd += len(marker)
|
||||
var value string
|
||||
if value, fvInd, err = c.extractValueForMarker(fvInd, fv, patternInd, indices); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// if marker is repeated in the pattern, make sure that the corresponding values
|
||||
// are same and throw error if not.
|
||||
if prevValue, ok := m[marker]; ok && prevValue != value {
|
||||
return nil, errors.Errorf(
|
||||
"marker %s is found to have different values %s and %s", marker, prevValue, value)
|
||||
}
|
||||
m[marker] = value
|
||||
} else {
|
||||
// Ex: fv = "samething/ubuntu:0.1.0" pattern = "something/IMAGE:VERSION". Error out at 'a' in fv.
|
||||
if fv[fvInd] != pattern[patternInd] {
|
||||
return nil, errors.Errorf(
|
||||
"unable to derive values for markers, " +
|
||||
"create setters for all markers and then try again")
|
||||
}
|
||||
fvInd++
|
||||
patternInd++
|
||||
}
|
||||
}
|
||||
// check if both strings are completely visited or throw error
|
||||
if fvInd < len(fv) || patternInd < len(pattern) {
|
||||
return nil, errors.Errorf(
|
||||
"unable to derive values for markers, " +
|
||||
"create setters for all markers and then try again")
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetStartIndices returns the start indices of all the markers in the pattern
|
||||
func (c SubstitutionCreator) GetStartIndices() (map[int]string, error) {
|
||||
indices := make(map[int]string)
|
||||
for _, value := range c.Values {
|
||||
found := false
|
||||
for i := range c.Pattern {
|
||||
if strings.HasPrefix(c.Pattern[i:], value.Marker) {
|
||||
indices[i] = value.Marker
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.Errorf("unable to find marker " + value.Marker + " in the pattern")
|
||||
}
|
||||
}
|
||||
if err := validateMarkers(indices); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// validateMarkers takes the indices map, checks if any of 2 markers not have delimiters,
|
||||
// checks if any marker is substring of other and returns error
|
||||
func validateMarkers(indices map[int]string) error {
|
||||
for k1, v1 := range indices {
|
||||
for k2, v2 := range indices {
|
||||
if k1 != k2 && k1+len(v1) == k2 {
|
||||
return errors.Errorf(
|
||||
"markers %s and %s are found to have no delimiters between them,"+
|
||||
" pre-create setters and try again", v1, v2)
|
||||
}
|
||||
if v1 != v2 && strings.Contains(v1, v2) {
|
||||
return errors.Errorf(
|
||||
"markers %s is substring of %s,"+
|
||||
" no marker should be substring of other", v2, v1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractValueForMarker returns the value string for a marker and the incremented index
|
||||
func (c SubstitutionCreator) extractValueForMarker(fvInd int, fv string, patternInd int, indices map[int]string) (string, int, error) {
|
||||
nonMarkerStr := strTillNextMarker(indices, patternInd, c.Pattern)
|
||||
|
||||
// return the remaining string of fv till end if patternInd is at end of pattern
|
||||
if patternInd == len(c.Pattern) {
|
||||
return fv[fvInd:], len(fv), nil
|
||||
}
|
||||
|
||||
// split remaining fv starting from fvInd with the non marker substring delimiter and get the first value
|
||||
// In example fv = "something/ubuntu::0.1.0", pattern = "something/IMAGE::VERSION",
|
||||
// split with "::" delimiter in fv which gives markerValue = ubuntu for marker IMAGE
|
||||
// increment fvInd by length of extracted marker value and return fvInd
|
||||
if markerValues := strings.Split(fv[fvInd:], nonMarkerStr); len(markerValues) > 0 {
|
||||
return markerValues[0], fvInd + len(markerValues[0]), nil
|
||||
}
|
||||
|
||||
return "", -1, errors.Errorf(
|
||||
"unable to derive values for markers," +
|
||||
" create setters for all markers and then try again")
|
||||
}
|
||||
|
||||
// substrOfLen takes a string, start index and length and returns substring of given length
|
||||
// or till end of string
|
||||
func substrOfLen(str string, startInd int, length int) string {
|
||||
return str[startInd:min(len(str), startInd+length)]
|
||||
}
|
||||
|
||||
// strTillNextMarker takes in the indices map, a start index and returns the substring till
|
||||
// start of next marker
|
||||
func strTillNextMarker(indices map[int]string, startInd int, pattern string) string {
|
||||
// initialize with max value which is length of pattern
|
||||
nextMarkerStartInd := len(pattern)
|
||||
for ind := range indices {
|
||||
if ind > startInd {
|
||||
nextMarkerStartInd = min(ind-startInd, nextMarkerStartInd)
|
||||
}
|
||||
}
|
||||
return substrOfLen(pattern, startInd, nextMarkerStartInd)
|
||||
}
|
||||
|
||||
func min(a int, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
111
kyaml/setters2/settersutil/substitutioncreator_test.go
Normal file
111
kyaml/setters2/settersutil/substitutioncreator_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
func TestGetValuesForMarkers(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
pattern string
|
||||
fieldValue string
|
||||
markers []string
|
||||
expectedError error
|
||||
expectedOutput map[string]string
|
||||
}{
|
||||
{
|
||||
name: "positive example",
|
||||
markers: []string{"IMAGE", "VERSION"},
|
||||
pattern: "something/IMAGE::VERSION/otherthing/IMAGE::VERSION/",
|
||||
fieldValue: "something/nginx::0.1.0/otherthing/nginx::0.1.0/",
|
||||
expectedOutput: map[string]string{"IMAGE": "nginx", "VERSION": "0.1.0"},
|
||||
},
|
||||
{
|
||||
name: "marker with different values",
|
||||
markers: []string{"IMAGE", "VERSION"},
|
||||
pattern: "something/IMAGE:VERSION/IMAGE",
|
||||
fieldValue: "something/nginx:0.1.0/ubuntu",
|
||||
expectedError: errors.Errorf("marker IMAGE is found to have different values nginx and ubuntu"),
|
||||
},
|
||||
{
|
||||
name: "unmatched pattern",
|
||||
markers: []string{"IMAGE", "VERSION"},
|
||||
pattern: "something/IMAGE:VERSION",
|
||||
fieldValue: "otherthing/nginx:0.1.0",
|
||||
expectedError: errors.Errorf("unable to derive values for markers"),
|
||||
},
|
||||
{
|
||||
name: "unmatched pattern at the end",
|
||||
markers: []string{"IMAGE", "VERSION"},
|
||||
pattern: "something/IMAGE:VERSION/abc",
|
||||
fieldValue: "something/nginx:0.1.0/abcd",
|
||||
expectedError: errors.Errorf("unable to derive values for markers"),
|
||||
},
|
||||
{
|
||||
name: "substring markers",
|
||||
markers: []string{"IMAGE", "VERSION", "MAGE"},
|
||||
pattern: "something/IMAGE:VERSION/abc/MAGE",
|
||||
fieldValue: "something/nginx:0.1.0/abc/ubuntu",
|
||||
expectedError: errors.Errorf("no marker should be substring of other"),
|
||||
},
|
||||
{
|
||||
name: "markers with no delimiters",
|
||||
markers: []string{"IMAGE", "VERSION"},
|
||||
pattern: "something/IMAGEVERSION/",
|
||||
fieldValue: "something/nginx0.1.0/",
|
||||
expectedError: errors.Errorf("no delimiters between them"),
|
||||
},
|
||||
{
|
||||
name: "unmatched delimiter",
|
||||
markers: []string{"IMAGE", "VERSION"},
|
||||
pattern: "something/IMAGE:^VERSION/otherthing/IMAGE::VERSION/",
|
||||
fieldValue: "something/nginx::0.1.0/otherthing/nginx::0.1.0/",
|
||||
expectedError: errors.Errorf("unable to derive values for markers"),
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
values := []setters2.Value{}
|
||||
for _, marker := range test.markers {
|
||||
value := setters2.Value{
|
||||
Marker: marker,
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
|
||||
sc := SubstitutionCreator{
|
||||
Pattern: test.pattern,
|
||||
Values: values,
|
||||
FieldValue: test.fieldValue,
|
||||
}
|
||||
|
||||
m, err := sc.GetValuesForMarkers()
|
||||
|
||||
if test.expectedError == nil {
|
||||
// fail if expectedError is nil but actual error is not
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
// check if all the expected markers and values are present in actual map
|
||||
for k, v := range test.expectedOutput {
|
||||
if val, ok := m[k]; ok {
|
||||
assert.Equal(t, v, val)
|
||||
} else {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//if expectedError is not nil, check for correctness of error message
|
||||
assert.Contains(t, err.Error(), test.expectedError.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,9 @@ type cliExtension struct {
|
||||
}
|
||||
|
||||
type setter struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Value string `yaml:"value,omitempty" json:"value,omitempty"`
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Value string `yaml:"value,omitempty" json:"value,omitempty"`
|
||||
EnumValues map[string]string `yaml:"enumValues,omitempty" json:"enumValues,omitempty"`
|
||||
}
|
||||
|
||||
type substitution struct {
|
||||
@@ -56,32 +57,32 @@ func getExtFromSchema(schema *spec.Schema) (*cliExtension, error) {
|
||||
|
||||
// getExtFromComment returns the cliExtension openAPI extension if it is present as
|
||||
// a comment on the field.
|
||||
func getExtFromComment(object *yaml.RNode) (*cliExtension, error) {
|
||||
func getExtFromComment(object *yaml.RNode) (*cliExtension, *spec.Schema, error) {
|
||||
// TODO(pwittrock): also use path to the field to get openapi, not just comments
|
||||
// parse comment containing the extended openapi for this field
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
if err := fm.Read(object); err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
if fm.Schema.Ref.String() == "" {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// resolve the comment reference to the extended openapi definitions
|
||||
r, err := openapi.Resolve(&fm.Schema.Ref)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
if r == nil {
|
||||
// no schema found
|
||||
// TODO(pwittrock): should this be an error if it doesn't resolve?
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// get the cli extension from the openapi (contains setter information)
|
||||
ext, err := getExtFromSchema(r)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
return ext, nil
|
||||
return ext, r, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user