Compare commits

..

46 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
37bab58095 Merge pull request #2258 from rhomel/api-0.3.3
increment api patch release to 0.3.3
2020-03-05 19:48:50 -08:00
Rhomel Chinsio
76e72ada16 increment api patch release to 0.3.3
Context: https://github.com/kubernetes-sigs/kustomize/pull/2249#issuecomment-595436980

Increased the patch version. But perhaps the minor version should increase instead since this is technically not a bug? Let me know if patch increment is sufficient in this case.
2020-03-06 09:56:34 +09:00
Kubernetes Prow Robot
0571f13da1 Merge pull request #2249 from rhomel/image-match-tag-braces-api
Allow braces in image tag name (api)
2020-03-05 12:28:38 -08:00
Rhomel Chinsio
b33d0f86f0 fix: add plugin file 2020-03-05 06:57:40 +00:00
Kubernetes Prow Robot
6c2c08c4df Merge pull request #2253 from khrisrichardson/issues/2252
#2252: convert tests to exercise openapi and inference
2020-03-03 21:07:47 -08:00
Khris Richardson
fcfe798b75 convert tests to exercise openapi and inference 2020-03-03 18:50:02 -08:00
Kubernetes Prow Robot
a484bd2efd Merge pull request #2251 from Liujingfang1/master
Use sigs.k8s.io/Application type instead of go template
2020-03-03 16:33:47 -08:00
Jingfang Liu
c8602cf9b6 Use sigs.k8s.io/Application type instead of go template 2020-03-03 13:57:03 -08:00
Kubernetes Prow Robot
caa8fdc3cd Merge pull request #2245 from pwittrock/merge3
Use OpenAPI when 3-way merging resources
2020-03-03 12:51:46 -08:00
Rhomel Chinsio
f0ae77abd5 allow braces in image tag name (api) 2020-03-03 05:48:36 +00:00
Phillip Wittrock
5d1a0346b5 Use OpenAPI when merging (3way) resources
- When merging (3way) resources use the patch strategy from the openAPI if the definition exists for the field
- Allow disabling of guessing patch strategy merge keys when no definition exists
- Support defining strategy and key directly on configuration fields through line and header coments
- Support attaching schema to parent fields of lists, and propagating -- e.g. that a field is a PodTemplate
2020-03-02 20:56:53 -08:00
Kubernetes Prow Robot
2a8a17e3af Merge pull request #2239 from Liujingfang1/master
Add an example function to append an Application CR to resources
2020-02-28 15:34:39 -08:00
Jingfang Liu
f6ad78650e Add an example function to append an Application CR to resources 2020-02-28 13:05:33 -08:00
Kubernetes Prow Robot
6c4370cd0d Merge pull request #2238 from pwittrock/release
Release kyaml and cmd/config v0.1.0
2020-02-27 16:06:39 -08:00
Phillip Wittrock
052deacbb2 Add license to kfns_test 2020-02-27 15:49:53 -08:00
Phillip Wittrock
523ab2e35f Add cmd/ and kyaml/ to prow tests 2020-02-27 15:49:50 -08:00
Phillip Wittrock
9a015ca8d5 kyaml v0.1.0 release 2020-02-27 15:27:26 -08:00
Kubernetes Prow Robot
a24cc4d305 Merge pull request #2237 from Liujingfang1/master
add SetLabel function in kyaml library
2020-02-27 14:18:38 -08:00
Jingfang Liu
ef76575ab6 add test for SetLabel and SetAnnotation 2020-02-27 13:37:36 -08:00
Jingfang Liu
39094f2aeb add SetLabel function in kyaml library 2020-02-27 12:11:06 -08:00
Kubernetes Prow Robot
49ebb3f155 Merge pull request #2236 from pwittrock/parse
Setters type support
2020-02-27 12:06:38 -08:00
Phillip Wittrock
fa507f782f Setters: support for explicit setter typing
- ensure OpenAPI definitions always uses strings for setter values
- allow the field type to be defined -- integer,boolean,string
- format values using yaml 1.1 compatibility
2020-02-27 11:51:18 -08:00
Phillip Wittrock
da548f65ea fixup go.sum 2020-02-27 11:48:24 -08:00
Kubernetes Prow Robot
8991b193c6 Merge pull request #2232 from pwittrock/setby
Setters: support for enums
2020-02-26 20:33:19 -08:00
Phillip Wittrock
3c776b3435 fix setter clear set-by test 2020-02-26 20:25:05 -08:00
Phillip Wittrock
cf61a360e0 Support for enum mappings in setters 2020-02-26 20:13:06 -08:00
Kubernetes Prow Robot
a588f498ea Merge pull request #2228 from pwittrock/setby
Setters: clear set-by if unspecified when setting a value
2020-02-26 14:23:19 -08:00
Phillip Wittrock
573d7b7234 Setters: clear set-by if unspecified when setting a value 2020-02-26 13:53:47 -08:00
Kubernetes Prow Robot
1d94ee86df Merge pull request #2219 from phanimarupaka/substitutions
Create setters for substitutions
2020-02-26 13:41:19 -08:00
Phani Teja Marupaka
275bf05ae2 Refactoring and Table tests 2020-02-26 11:37:14 -08:00
Phani Teja Marupaka
d70f3a7958 Update go files 2020-02-25 14:09:37 -08:00
Kubernetes Prow Robot
5707962df5 Merge pull request #2224 from monopole/disableTravis
Disable travis testing now that prow works.
2020-02-25 11:52:50 -08:00
Jeffrey Regan
63c06eeaf1 Disable travis testing now that prow works. 2020-02-25 11:38:36 -08:00
Kubernetes Prow Robot
b3ce3a7882 Merge pull request #2213 from vanou/correct_explanation_of_ApendAll_method_in_ResMap_interface
Correct explanation of ApendAll method in ResMap interface
2020-02-24 16:25:35 -08:00
Phani Teja Marupaka
a8b5ec2c61 Suggested Changes and Unit Tests 2020-02-24 12:35:14 -08:00
Kubernetes Prow Robot
d190e1c18a Merge pull request #2220 from kubernetes-sigs/monopole-patch-1
Add lint-kustomize to prow-presubmit-check
2020-02-24 10:42:50 -08:00
Jeff Regan
96ac25fff5 Add lint-kustomize to prow-presubmit-check
To do tool installs.
2020-02-24 10:17:52 -08:00
Phani Teja Marupaka
1d988a0fd8 Merge branch 'master' into substitutions 2020-02-24 09:39:22 -08:00
Phillip Wittrock
6f176b1507 Merge pull request #2203 from pwittrock/setter-wiring
Manually merging since the prow automation does not appear to be configured correctly
2020-02-24 09:23:00 -08:00
Phillip Wittrock
d2f0b1b345 Merge pull request #2218 from pwittrock/master
Update kyaml to v0.0.13
2020-02-21 14:35:25 -08:00
Phillip Wittrock
c95a40933b Update kyaml to v0.0.13 2020-02-21 10:18:52 -08:00
Phillip Wittrock
bc7b880ab1 Merge pull request #2211 from pwittrock/master
Make GetOpenAPIFile publicly settable
2020-02-21 10:16:21 -08:00
Phillip Wittrock
e004c31700 Make GetOpenAPIFile publicly settable 2020-02-21 08:09:48 -08:00
Phillip Wittrock
ca9aa62c26 Merge pull request #2216 from pwittrock/version
release cmd/config and kyaml 0.0.12
2020-02-21 08:09:08 -08:00
Phillip Wittrock
cee7cb6589 release cmd/config and kyaml 0.0.12 2020-02-19 15:01:14 -08:00
vanou
87b680e1c0 Correct explanation of ApendAll method in ResMap interface
This commit corrects expalation of ApendAll method in ResMap
interface. AppendAll method should fail on CurId collision,
not OrgId collision.
2020-02-19 23:53:10 +09:00
65 changed files with 3788 additions and 892 deletions

View File

@@ -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

View File

@@ -21,7 +21,10 @@ verify-kustomize: \
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
.PHONY: prow-presubmit-check
prow-presubmit-check: \
test-unit-kustomize-all
lint-kustomize \
test-unit-kustomize-all \
test-unit-cmd-all \
test-go-mod
.PHONY: verify-kustomize-e2e
verify-kustomize-e2e: test-examples-e2e-kustomize
@@ -203,6 +206,12 @@ test-unit-kustomize-all: \
test-unit-kustomize-cli \
test-unit-kustomize-plugins
test-unit-cmd-all:
./travis/kyaml-pre-commit.sh
test-go-mod:
./travis/check-go-mod.sh
.PHONY:
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD

View File

@@ -144,8 +144,10 @@ func (p *ImageTagTransformerPlugin) findContainers(obj map[string]interface{}) e
}
func isImageMatched(s, t string) bool {
// Tag values are limited to [a-zA-Z0-9_.-].
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
// Tag values are limited to [a-zA-Z0-9_.{}-].
// Some tools like Bazel rules_k8s allow tag patterns with {} characters.
// More info: https://github.com/bazelbuild/rules_k8s/pull/423
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.{}-]*)?$")
return pattern.MatchString(s)
}

View File

@@ -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

12
cmd/config/ext/ext.go Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package ext
import "path/filepath"
// GetOpenAPIFile returns the path to the file containing supplementary OpenAPI definitions.
// Maybe be overridden to configure which file to read OpenAPI definitions from.
var GetOpenAPIFile = func(args []string) (string, error) {
return filepath.Join(args[0], "kustomization"), nil
}

View File

@@ -10,5 +10,7 @@ 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.12
sigs.k8s.io/kustomize/kyaml v0.0.0
)
replace sigs.k8s.io/kustomize/kyaml v0.0.0 => ../../kyaml

View File

@@ -156,15 +156,15 @@ 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.0.12 h1:5HFupVWGTHD4Tfu1+YL+A7oqUujySRkLiTeN1mACpc8=
sigs.k8s.io/kustomize/kyaml v0.0.12/go.mod h1:tDOfJjL6slQVBLHJ76XfXAFgAOEdfm04AW2HehYOp8k=
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=

View File

@@ -5,6 +5,7 @@ package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/setters"
@@ -37,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")
@@ -85,9 +85,10 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
}
if setterVersion == "v2" {
var err error
r.OpenAPIFile, err = GetOpenAPIFile(args)
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
}

View File

@@ -11,6 +11,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
@@ -77,7 +78,9 @@ kind: Example
if !assert.NoError(t, err) {
t.FailNow()
}
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}

View File

@@ -7,6 +7,7 @@ import (
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/setters2"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
@@ -58,7 +59,7 @@ func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
return err
}
r.OpenAPIFile, err = GetOpenAPIFile(args)
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
if err != nil {
return err
}

View File

@@ -11,6 +11,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
@@ -101,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
`,
},
}
@@ -120,7 +187,9 @@ spec:
if !assert.NoError(t, err) {
t.FailNow()
}
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/setters"
"sigs.k8s.io/kustomize/kyaml/setters2"
@@ -55,7 +56,7 @@ func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
if setterVersion == "v2" {
// use setters v2
path, err := GetOpenAPIFile(args)
path, err := ext.GetOpenAPIFile(args)
if err != nil {
return err
}

View File

@@ -10,6 +10,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
@@ -261,7 +262,9 @@ spec:
t.FailNow()
}
defer os.Remove(f.Name())
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
err = ioutil.WriteFile(f.Name(), []byte(test.openapi), 0600)
if !assert.NoError(t, err) {
t.FailNow()

View File

@@ -6,10 +6,10 @@ package commands
import (
"fmt"
"os"
"path/filepath"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/setters"
@@ -43,10 +43,6 @@ func NewSetRunner(parent string) *SetRunner {
var setterVersion string
var GetOpenAPIFile = func(args []string) (string, error) {
return filepath.Join(args[0], "kustomization"), nil
}
func SetCommand(parent string) *cobra.Command {
return NewSetRunner(parent).Command
}
@@ -100,7 +96,7 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
r.Set.Value = args[2]
r.Set.Description = r.Perform.Description
r.Set.SetBy = r.Perform.SetBy
r.OpenAPIFile, err = GetOpenAPIFile(args)
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
if err != nil {
return err
}

View File

@@ -11,6 +11,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
@@ -108,7 +109,6 @@ openAPI:
setter:
name: replicas
value: "4"
setBy: me
`,
expectedResources: `
apiVersion: apps/v1
@@ -224,7 +224,9 @@ spec:
if !assert.NoError(t, err) {
t.FailNow()
}
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}

View File

@@ -0,0 +1,2 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,42 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy image
GOBIN := $(shell go env GOPATH)/bin
build:
(cd image && go build -v -o $(GOBIN)/config-function .)
all: generate license build fix vet fmt test lint tidy
fix:
(cd image && go fix ./...)
fmt:
(cd image && go fmt ./...)
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
(cd image && $(GOBIN)/golangci-lint run ./...)
test:
(cd image && go test -cover ./...)
vet:
(cd image && go vet ./...)
image:
docker build image -t gcr.io/kustomize-functions/create-application:v0.1.0
docker push gcr.io/kustomize-functions/create-application:v0.1.0

View File

@@ -0,0 +1,30 @@
# Append Application CR
This is an example of appending an Application CR to a group of resources.
This example is written in `go` and uses the `kyaml` libraries for parsing the
input and writing the output. Writing in `go` is not a requirement.
## Function implementation
The function is implemented as an [image](image), and built using `make image`.
The template is implemented as a go program, which reads a collection of input
Resource configuration, and looks for invalid configuration.
## Function invocation
The function is invoked by authoring a [local Resource](local-resource)
with `metadata.annotations.[config.kubernetes.io/function]` and running:
kustomize config run local-resource/ --fn-path config/
This exits non-zero if there is an error.
## Running the Example
Run the validator with:
kustomize config run local-resource/ --fn-path config/
This will append an Application CR.

View File

@@ -0,0 +1,13 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1
kind: CreateApp
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/create-application:v0.1.0
spec:
managedBy: jingfang
name: example-app
namespace: example-namespace

View File

@@ -0,0 +1,15 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY main.go .
RUN go build -v -o /usr/local/bin/config-function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/config-function /usr/local/bin/config-function
CMD ["config-function"]

Binary file not shown.

View File

@@ -0,0 +1,13 @@
module sigs.k8s.io/kustomize/functions/examples/application-cr
go 1.13
require (
github.com/gogo/protobuf v1.3.1 // indirect
github.com/google/gofuzz v1.1.0 // indirect
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
k8s.io/apimachinery v0.17.2
sigs.k8s.io/application v0.8.2-0.20200228023238-a3b16d81fa81
sigs.k8s.io/kustomize/kyaml v0.1.1-0.20200227221838-a24cc4d305f4
sigs.k8s.io/yaml v1.1.0
)

View File

@@ -0,0 +1,520 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
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/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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-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-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
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 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
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=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
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=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
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=
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
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/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
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 h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
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=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
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=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/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=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
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=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48=
k8s.io/api v0.17.0 h1:H9d/lw+VkZKEVIUc8F3wgiQ+FUXTTr21M87jXLU7yqM=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
k8s.io/apiextensions-apiserver v0.17.0 h1:+XgcGxqaMztkbbvsORgCmHIb4uImHKvTjNyu7b8gRnA=
k8s.io/apiextensions-apiserver v0.17.0/go.mod h1:XiIFUakZywkUl54fVXa7QTEHcqQz9HG55nHd1DCoHj8=
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4=
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg=
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/client-go v0.17.0 h1:8QOGvUGdqDMFrm9sD6IUFl256BcffynGoe80sxgTEDg=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA=
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
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-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
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-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/application v0.8.2-0.20200228023238-a3b16d81fa81 h1:UfB69/nuzg30T/EyejFxgIPHjKNbBmNL2rqOrtcV6Cs=
sigs.k8s.io/application v0.8.2-0.20200228023238-a3b16d81fa81/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.1.1-0.20200227221838-a24cc4d305f4 h1:sAwSXsspJa7zmKAmrKlYpkdhoMjQUqA06SdBTvPL0S4=
sigs.k8s.io/kustomize/kyaml v0.1.1-0.20200227221838-a24cc4d305f4/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
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=
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -0,0 +1,160 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package main implements adding an Application CR to a group of resources and
// is run with `kustomize config run -- DIR/`.
package main
import (
"fmt"
"os"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/application/api/v1beta1"
yaml2 "sigs.k8s.io/yaml"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func appCR() error {
rw := &kio.ByteReadWriter{
Reader: os.Stdin,
Writer: os.Stdout,
OmitReaderAnnotations: true,
KeepReaderAnnotations: true,
}
p := kio.Pipeline{
Inputs: []kio.Reader{rw}, // read the inputs into a slice
Filters: []kio.Filter{appCRFilter{rw: rw}},
Outputs: []kio.Writer{rw}, // copy the inputs to the output
}
if err := p.Execute(); err != nil {
return err
}
return nil
}
func main() {
if err := appCR(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
// appCRFilter implements kio.Filter
type appCRFilter struct {
rw *kio.ByteReadWriter
}
// define the input API schema as a struct
type API struct {
Spec struct {
ManagedBy string `yaml:"managedBy"`
Name string `yaml:"name"`
Namespace string `yaml:"namespace"`
Descriptor v1beta1.Descriptor `yaml:"descriptor,omitempty"`
} `yaml:"spec"`
}
// Filter checks each resource for validity, otherwise returning an error.
func (f appCRFilter) Filter(in []*yaml.RNode) ([]*yaml.RNode, error) {
api := f.parseAPI()
groupKinds, err := getGroupKinds(in)
if err != nil {
return nil, err
}
app, err := addApplicationCR(api, groupKinds)
if err != nil {
return nil, err
}
err = addApplicationLabel(api.Spec.Name, in)
if err != nil {
return nil, err
}
if app != nil {
return append(in, app), nil
}
return in, nil
}
// parseAPI parses the functionConfig into an API struct.
func (f *appCRFilter) parseAPI() API {
// parse the input function config -- TODO: simplify this
var api API
if err := yaml.Unmarshal([]byte(f.rw.FunctionConfig.MustString()), &api); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
return api
}
func getGroupKinds(in []*yaml.RNode) ([]metav1.GroupKind, error) {
var groupKinds []metav1.GroupKind
for _, r := range in {
meta, err := r.GetMeta()
if err != nil {
return nil, err
}
gvk := schema.FromAPIVersionAndKind(meta.APIVersion, meta.Kind)
found := false
for _, gk := range groupKinds {
if gk.Group == gvk.Group && gk.Kind == gvk.Kind {
break
}
}
if !found {
groupKinds = append(groupKinds, metav1.GroupKind{
Group: gvk.Group,
Kind: gvk.Kind,
})
}
}
return groupKinds, nil
}
func addApplicationCR(api API, groupKinds []metav1.GroupKind) (*yaml.RNode, error) {
app := v1beta1.Application{
TypeMeta: metav1.TypeMeta{
APIVersion: "app.k8s.io/v1beta1",
Kind: "Application",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: api.Spec.Namespace,
Name: api.Spec.Name,
Labels: map[string]string{"app.kubernetes.io/name": api.Spec.Name},
Annotations: map[string]string{"app.kubernetes.io/managed-by": api.Spec.ManagedBy},
},
Spec: v1beta1.ApplicationSpec{
ComponentGroupKinds: groupKinds,
Descriptor: api.Spec.Descriptor,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app.kubernetes.io/name": api.Spec.Name},
},
},
}
data, err := yaml2.Marshal(app)
if err != nil {
return nil, err
}
return yaml.Parse(string(data))
}
func addApplicationLabel(name string, in []*yaml.RNode) error {
for _, r := range in {
if _, err := r.Pipe(yaml.SetLabel("app.kubernetes.io/name", name)); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,251 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package main implements adding an Application CR to a group of resources and
// is run with `kustomize config run -- DIR/`.
package main
import (
"bytes"
"io"
"io/ioutil"
"os"
"testing"
)
var input = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'example.yaml'
data:
altGreeting: "Good Morning!"
enableRisky: "false"
- apiVersion: apps/v1
kind: Deployment
metadata:
name: the-deployment
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'example.yaml'
spec:
replicas: 3
template:
metadata:
labels:
deployment: hello
spec:
containers:
- name: the-container
image: monopole/hello:1
command: ["/hello", "--port=8080", "--enableRiskyFeature=$(ENABLE_RISKY)"]
ports:
- containerPort: 8080
env:
- name: ALT_GREETING
valueFrom:
configMapKeyRef:
name: the-map
key: altGreeting
- name: ENABLE_RISKY
valueFrom:
configMapKeyRef:
name: the-map
key: enableRisky
- kind: Service
apiVersion: v1
metadata:
name: the-service
annotations:
config.kubernetes.io/index: '2'
config.kubernetes.io/path: 'example.yaml'
spec:
selector:
deployment: hello
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080
functionConfig:
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1
kind: CreateApp
spec:
managedBy: jingfang
name: example-app
namespace: example-namespace
descriptor:
links:
- description: About
url: https://worldpress.org/
- description: web server dashboard
url: https://metrics/internal/worldpress-01/web-app
`
var output = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'example.yaml'
labels:
app.kubernetes.io/name: 'example-app'
data:
altGreeting: "Good Morning!"
enableRisky: "false"
- apiVersion: apps/v1
kind: Deployment
metadata:
name: the-deployment
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'example.yaml'
labels:
app.kubernetes.io/name: 'example-app'
spec:
replicas: 3
template:
metadata:
labels:
deployment: hello
spec:
containers:
- name: the-container
image: monopole/hello:1
command: ["/hello", "--port=8080", "--enableRiskyFeature=$(ENABLE_RISKY)"]
ports:
- containerPort: 8080
env:
- name: ALT_GREETING
valueFrom:
configMapKeyRef:
name: the-map
key: altGreeting
- name: ENABLE_RISKY
valueFrom:
configMapKeyRef:
name: the-map
key: enableRisky
- kind: Service
apiVersion: v1
metadata:
name: the-service
annotations:
config.kubernetes.io/index: '2'
config.kubernetes.io/path: 'example.yaml'
labels:
app.kubernetes.io/name: 'example-app'
spec:
selector:
deployment: hello
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080
- apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
annotations:
app.kubernetes.io/managed-by: jingfang
creationTimestamp: null
labels:
app.kubernetes.io/name: example-app
name: example-app
namespace: example-namespace
spec:
componentKinds:
- group: ""
kind: ConfigMap
- group: apps
kind: Deployment
- group: ""
kind: Service
descriptor:
links:
- description: About
url: https://worldpress.org/
- description: web server dashboard
url: https://metrics/internal/worldpress-01/web-app
selector:
matchLabels:
app.kubernetes.io/name: example-app
status: {}
functionConfig:
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: examples.config.kubernetes.io/v1beta1
kind: CreateApp
spec:
managedBy: jingfang
name: example-app
namespace: example-namespace
descriptor:
links:
- description: About
url: https://worldpress.org/
- description: web server dashboard
url: https://metrics/internal/worldpress-01/web-app
`
func Test(t *testing.T) {
oldStdin := os.Stdin
oldStdout := os.Stdout
defer func() {
os.Stdin = oldStdin
os.Stdout = oldStdout
}()
r, w, _ := os.Pipe()
os.Stdout = w
outC := make(chan string)
go func() {
var buf bytes.Buffer
_, err := io.Copy(&buf, r)
if err != nil {
outC <- ""
}
outC <- buf.String()
}()
tmpfile, err := ioutil.TempFile("", "test-input")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // noerrcheck
if _, err := tmpfile.Write([]byte(input)); err != nil {
t.Fatal(err)
}
if _, err := tmpfile.Seek(0, 0); err != nil {
t.Fatal(err)
}
os.Stdin = tmpfile
err = appCR()
if err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
out := <-outC
if out != output {
t.Fatalf("expected %s\nbut got %s\n", output, out)
}
}

View File

@@ -0,0 +1,52 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Good Morning!"
enableRisky: "false"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: the-deployment
spec:
replicas: 3
template:
metadata:
labels:
deployment: hello
spec:
containers:
- name: the-container
image: monopole/hello:1
command: ["/hello", "--port=8080", "--enableRiskyFeature=$(ENABLE_RISKY)"]
ports:
- containerPort: 8080
env:
- name: ALT_GREETING
valueFrom:
configMapKeyRef:
name: the-map
key: altGreeting
- name: ENABLE_RISKY
valueFrom:
configMapKeyRef:
name: the-map
key: enableRisky
---
kind: Service
apiVersion: v1
metadata:
name: the-service
spec:
selector:
deployment: hello
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080

View File

@@ -5,6 +5,7 @@ package fieldmeta
import (
"encoding/json"
"reflect"
"strconv"
"strings"
@@ -36,16 +37,29 @@ type PartialFieldSetter struct {
Value string `yaml:"value" json:"value"`
}
// IsEmpty returns true if the FieldMeta has any empty Schema
func (fm *FieldMeta) IsEmpty() bool {
if fm == nil {
return true
}
return reflect.DeepEqual(fm.Schema, spec.Schema{})
}
// Read reads the FieldMeta from a node
func (fm *FieldMeta) Read(n *yaml.RNode) error {
if n.YNode().LineComment != "" {
v := strings.TrimLeft(n.YNode().LineComment, "#")
// 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 it doesn't Unmarshal that is fine, it means there is no metadata
// other comments are valid, they just don't parse
// TODO: consider most sophisticated parsing techniques similar to what is used
// TODO: consider more sophisticated parsing techniques similar to what is used
// for go struct tags.
if err := fm.Schema.UnmarshalJSON([]byte(v)); err != nil {
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
}

View File

@@ -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
)

View File

@@ -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=

View File

@@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"sync"
"github.com/go-openapi/spec"
@@ -32,6 +33,14 @@ type ResourceSchema struct {
Schema *spec.Schema
}
// IsEmpty returns true if the ResourceSchema is empty
func (rs *ResourceSchema) IsEmpty() bool {
if rs == nil || rs.Schema == nil {
return true
}
return reflect.DeepEqual(*rs.Schema, spec.Schema{})
}
// SchemaForResourceType returns the Schema for the given Resource
// TODO(pwittrock): create a version of this function that will return a schema
// which can be used for duck-typed Resources -- e.g. contains common fields such
@@ -193,15 +202,15 @@ func SuppressBuiltInSchemaUse() {
}
// Elements returns the Schema for the elements of an array.
func (r *ResourceSchema) Elements() *ResourceSchema {
func (rs *ResourceSchema) Elements() *ResourceSchema {
// load the schema from swagger.json
initSchema()
if len(r.Schema.Type) != 1 || r.Schema.Type[0] != "array" {
if len(rs.Schema.Type) != 1 || rs.Schema.Type[0] != "array" {
// either not an array, or array has multiple types
return nil
}
s := *r.Schema.Items.Schema
s := *rs.Schema.Items.Schema
for s.Ref.String() != "" {
sc, e := Resolve(&s.Ref)
if e != nil {
@@ -219,8 +228,8 @@ const Elements = "[]"
// Field is called.
// If any Field or Elements call returns nil, then Lookup returns
// nil immediately.
func (r *ResourceSchema) Lookup(path ...string) *ResourceSchema {
s := r
func (rs *ResourceSchema) Lookup(path ...string) *ResourceSchema {
s := rs
for _, p := range path {
if s == nil {
break
@@ -235,19 +244,19 @@ func (r *ResourceSchema) Lookup(path ...string) *ResourceSchema {
}
// Field returns the Schema for a field.
func (r *ResourceSchema) Field(field string) *ResourceSchema {
func (rs *ResourceSchema) Field(field string) *ResourceSchema {
// load the schema from swagger.json
initSchema()
// locate the Schema
s, found := r.Schema.Properties[field]
s, found := rs.Schema.Properties[field]
switch {
case found:
// no-op, continue with s as the schema
case r.Schema.AdditionalProperties != nil && r.Schema.AdditionalProperties.Schema != nil:
case rs.Schema.AdditionalProperties != nil && rs.Schema.AdditionalProperties.Schema != nil:
// map field type -- use Schema of the value
// (the key doesn't matter, they all have the same value type)
s = *r.Schema.AdditionalProperties.Schema
s = *rs.Schema.AdditionalProperties.Schema
default:
// no Schema found from either swagger.json or line comments
return nil
@@ -267,14 +276,14 @@ func (r *ResourceSchema) Field(field string) *ResourceSchema {
}
// PatchStrategyAndKey returns the patch strategy and merge key extensions
func (r *ResourceSchema) PatchStrategyAndKey() (string, string) {
ps, found := r.Schema.Extensions[kubernetesPatchStrategyExtensionKey]
func (rs *ResourceSchema) PatchStrategyAndKey() (string, string) {
ps, found := rs.Schema.Extensions[kubernetesPatchStrategyExtensionKey]
if !found {
// merge key and patch strategy must appear together
return "", ""
}
mk, found := r.Schema.Extensions[kubernetesMergeKeyExtensionKey]
mk, found := rs.Schema.Extensions[kubernetesMergeKeyExtensionKey]
if !found {
// merge key and patch strategy must appear together
return "", ""

View File

@@ -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

View File

@@ -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 != "" {

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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
}

View 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())
}
})
}
}

View File

@@ -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
}

View File

@@ -15,6 +15,7 @@ var Filters = map[string]func() Filter{
"AnnotationClearer": func() Filter { return &AnnotationClearer{} },
"AnnotationGetter": func() Filter { return &AnnotationGetter{} },
"AnnotationSetter": func() Filter { return &AnnotationSetter{} },
"LabelSetter": func() Filter { return &LabelSetter{} },
"ElementAppender": func() Filter { return &ElementAppender{} },
"ElementMatcher": func() Filter { return &ElementMatcher{} },
"FieldClearer": func() Filter { return &FieldClearer{} },

View File

@@ -68,3 +68,25 @@ func (g AnnotationGetter) Filter(rn *RNode) (*RNode, error) {
func GetAnnotation(key string) AnnotationGetter {
return AnnotationGetter{Key: key}
}
// LabelSetter sets a label at metadata.labels.
// Creates metadata.labels if does not exist.
type LabelSetter struct {
Kind string `yaml:"kind,omitempty"`
Key string `yaml:"key,omitempty"`
Value string `yaml:"value,omitempty"`
}
func (s LabelSetter) Filter(rn *RNode) (*RNode, error) {
// some tools get confused about the type if labels are not quoted
v := NewScalarRNode(s.Value)
v.YNode().Tag = "!!str"
v.YNode().Style = yaml.SingleQuotedStyle
return rn.Pipe(
PathGetter{Path: []string{"metadata", "labels"}, Create: yaml.MappingNode},
FieldSetter{Name: s.Key, Value: v})
}
func SetLabel(key, value string) LabelSetter {
return LabelSetter{Key: key, Value: value}
}

63
kyaml/yaml/kfns_test.go Normal file
View File

@@ -0,0 +1,63 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
import (
"testing"
)
var input = `apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Good Morning!"
enableRisky: "false"
`
func TestSetLabel(t *testing.T) {
rn := MustParse(input)
_, err := rn.Pipe(SetLabel("foo", "bar"))
if err != nil {
t.Fatalf("unexpected error %v", err)
}
output := rn.MustString()
expected := `apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
labels:
foo: 'bar'
data:
altGreeting: "Good Morning!"
enableRisky: "false"
`
if output != expected {
t.Fatalf("expected \n%s\nbut got \n%s\n", expected, output)
}
}
func TestAnnotation(t *testing.T) {
rn := MustParse(input)
_, err := rn.Pipe(SetAnnotation("foo", "bar"))
if err != nil {
t.Fatalf("unexpected error %v", err)
}
output := rn.MustString()
expected := `apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
annotations:
foo: 'bar'
data:
altGreeting: "Good Morning!"
enableRisky: "false"
`
if output != expected {
t.Fatalf("expected \n%s\nbut got \n%s\n", expected, output)
}
}

View File

@@ -4,251 +4,461 @@
package merge2_test
var elementTestCases = []testCase{
{`merge Element -- keep field in dest`,
`
{description: `merge Element -- keep field in dest`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v0
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v0
command: ['run.sh']
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
},
{`merge Element -- add field to dest`,
`
{description: `merge Element -- add field to dest`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v0
spec:
template:
spec:
containers:
- name: foo
image: foo:v0
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
},
{`merge Element -- add list, empty in dest`,
`
{description: `merge Element -- add list, empty in dest`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items: []
spec:
template:
spec:
containers: []
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
},
{`merge Element -- add list, missing from dest`,
`
{description: `merge Element -- add list, missing from dest`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
command: ['run.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
command: ['run.sh']
`,
},
{`merge Element -- add Element first`,
`
{description: `merge Element -- add Element first`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: bar
image: bar:v1
command: ['run2.sh']
- name: foo
image: foo:v1
spec:
template:
spec:
containers:
- name: bar
image: bar:v1
command: ['run2.sh']
- name: foo
image: foo:v1
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v0
spec:
template:
spec:
containers:
- name: foo
image: foo:v0
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
},
{`merge Element -- add Element second`,
`
{description: `merge Element -- add Element second`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v0
spec:
template:
spec:
containers:
- name: foo
image: foo:v0
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
},
//
// Test Case
//
{`keep list -- list missing from src`,
`
{description: `keep list -- list missing from src`,
source: `
apiVersion: apps/v1
kind: Deployment
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
},
//
// Test Case
//
{`keep Element -- element missing in src`,
`
{description: `keep Element -- element missing in src`,
source: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v0
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v0
- name: bar
image: bar:v1
command: ['run2.sh']
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
},
//
// Test Case
//
{`keep element -- empty list in src`,
`
{description: `keep element -- empty list in src`,
source: `
apiVersion: apps/v1
kind: Deployment
items: {}
spec:
template:
spec:
containers: {}
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
items:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
},
//
// Test Case
//
{`remove Element -- null in src`,
`
{description: `remove Element -- null in src`,
source: `
apiVersion: apps/v1
kind: Deployment
items: null
spec:
template:
spec:
containers: null
`,
`
dest: `
apiVersion: apps/v1
kind: Deployment
items:
spec:
template:
spec:
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
expected: `
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec: {}
`,
},
//
// Test Case
//
{description: `infer merge keys merge'`,
source: `
apiVersion: custom
kind: Deployment
containers:
- name: foo
image: foo:v1
- name: bar
image: bar:v1
command: ['run2.sh']
`,
`
dest: `
apiVersion: custom
kind: Deployment
containers:
- name: foo
image: foo:bar
`,
expected: `
apiVersion: custom
kind: Deployment
containers:
- name: foo
image: foo:bar
command: ['run2.sh']
`,
infer: true,
},
//
// Test Case
//
{description: `no infer merge keys merge using schema`,
source: `
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: foo
command: ['run2.sh']
`,
dest: `
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: foo
image: foo:bar
`,
expected: `
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: foo
image: foo:bar
command: ['run2.sh']
`,
infer: false,
},
//
// Test Case
//
{description: `no infer merge keys merge using explicit schema as line comment'`,
source: `
apiVersion: custom
kind: Deployment
containers:
- name: foo
command: ['run2.sh']
`,
dest: `
apiVersion: custom
kind: Deployment
containers: # {"items":{"$ref": "#/definitions/io.k8s.api.core.v1.Container"},"type":"array","x-kubernetes-patch-merge-key":"name","x-kubernetes-patch-strategy": "merge"}
- name: foo # hell ow
image: foo:bar
`,
expected: `
apiVersion: custom
kind: Deployment
containers: # {"items":{"$ref": "#/definitions/io.k8s.api.core.v1.Container"},"type":"array","x-kubernetes-patch-merge-key":"name","x-kubernetes-patch-strategy": "merge"}
- name: foo
image: foo:bar
command: ['run2.sh']
`,
infer: false,
},
}

View File

@@ -4,21 +4,21 @@
package merge2_test
var listTestCases = []testCase{
{`replace List -- different value in dest`,
`
{description: `replace List -- different value in dest`,
source: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
dest: `
kind: Deployment
items:
- 0
- 1
`,
`
expected: `
kind: Deployment
items:
- 1
@@ -27,18 +27,18 @@ items:
`,
},
{`replace List -- missing from dest`,
`
{description: `replace List -- missing from dest`,
source: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
dest: `
kind: Deployment
`,
`
expected: `
kind: Deployment
items:
- 1
@@ -50,22 +50,22 @@ items:
//
// Test Case
//
{`keep List -- same value in src and dest`,
`
{description: `keep List -- same value in src and dest`,
source: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
dest: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
expected: `
kind: Deployment
items:
- 1
@@ -77,18 +77,18 @@ items:
//
// Test Case
//
{`keep List -- unspecified in src`,
`
{description: `keep List -- unspecified in src`,
source: `
kind: Deployment
`,
`
dest: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
expected: `
kind: Deployment
items:
- 1
@@ -100,19 +100,19 @@ items:
//
// Test Case
//
{`remove List -- null in src`,
`
{description: `remove List -- null in src`,
source: `
kind: Deployment
items: null
`,
`
dest: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
expected: `
kind: Deployment
`,
},
@@ -120,19 +120,19 @@ kind: Deployment
//
// Test Case
//
{`remove list -- empty in src`,
`
{description: `remove list -- empty in src`,
source: `
kind: Deployment
items: {}
`,
`
dest: `
kind: Deployment
items:
- 1
- 2
- 3
`,
`
expected: `
kind: Deployment
items: {}
`,

View File

@@ -4,19 +4,19 @@
package merge2_test
var mapTestCases = []testCase{
{`merge Map -- update field in dest`,
`
{description: `merge Map -- update field in dest`,
source: `
kind: Deployment
spec:
foo: bar1
`,
`
dest: `
kind: Deployment
spec:
foo: bar0
baz: buz
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -24,19 +24,19 @@ spec:
`,
},
{`merge Map -- add field to dest`,
`
{description: `merge Map -- add field to dest`,
source: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
dest: `
kind: Deployment
spec:
foo: bar0
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -44,18 +44,18 @@ spec:
`,
},
{`merge Map -- add list, empty in dest`,
`
{description: `merge Map -- add list, empty in dest`,
source: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
dest: `
kind: Deployment
spec: {}
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -63,17 +63,17 @@ spec:
`,
},
{`merge Map -- add list, missing from dest`,
`
{description: `merge Map -- add list, missing from dest`,
source: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
dest: `
kind: Deployment
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -81,19 +81,19 @@ spec:
`,
},
{`merge Map -- add Map first`,
`
{description: `merge Map -- add Map first`,
source: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
dest: `
kind: Deployment
spec:
foo: bar1
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -101,19 +101,19 @@ spec:
`,
},
{`merge Map -- add Map second`,
`
{description: `merge Map -- add Map second`,
source: `
kind: Deployment
spec:
baz: buz
foo: bar1
`,
`
dest: `
kind: Deployment
spec:
foo: bar1
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -124,17 +124,17 @@ spec:
//
// Test Case
//
{`keep map -- map missing from src`,
`
{description: `keep map -- map missing from src`,
source: `
kind: Deployment
`,
`
dest: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -145,18 +145,18 @@ spec:
//
// Test Case
//
{`keep map -- empty list in src`,
`
{description: `keep map -- empty list in src`,
source: `
kind: Deployment
items: {}
`,
`
dest: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
expected: `
kind: Deployment
spec:
foo: bar1
@@ -168,18 +168,18 @@ items: {}
//
// Test Case
//
{`remove Map -- null in src`,
`
{description: `remove Map -- null in src`,
source: `
kind: Deployment
spec: null
`,
`
dest: `
kind: Deployment
spec:
foo: bar1
baz: buz
`,
`
expected: `
kind: Deployment
`,
},

View File

@@ -6,6 +6,7 @@
package merge2
import (
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
)
@@ -16,7 +17,7 @@ func Merge(src, dest *yaml.RNode) (*yaml.RNode, error) {
}
// Merge parses the arguments, and merges fields from srcStr into destStr.
func MergeStrings(srcStr, destStr string) (string, error) {
func MergeStrings(srcStr, destStr string, infer bool) (string, error) {
src, err := yaml.Parse(srcStr)
if err != nil {
return "", err
@@ -26,10 +27,15 @@ func MergeStrings(srcStr, destStr string) (string, error) {
return "", err
}
result, err := Merge(src, dest)
result, err := walk.Walker{
Sources: []*yaml.RNode{dest, src},
Visitor: Merger{},
InferAssociativeLists: infer,
}.Walk()
if err != nil {
return "", err
}
return result.String()
}
@@ -39,7 +45,7 @@ type Merger struct {
var _ walk.Visitor = Merger{}
func (m Merger) VisitMap(nodes walk.Sources) (*yaml.RNode, error) {
func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
if err := m.SetComments(nodes); err != nil {
return nil, err
}
@@ -58,7 +64,7 @@ func (m Merger) VisitMap(nodes walk.Sources) (*yaml.RNode, error) {
return nodes.Dest(), nil
}
func (m Merger) VisitScalar(nodes walk.Sources) (*yaml.RNode, error) {
func (m Merger) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
if err := m.SetComments(nodes); err != nil {
return nil, err
}
@@ -73,7 +79,7 @@ func (m Merger) VisitScalar(nodes walk.Sources) (*yaml.RNode, error) {
return nodes.Dest(), nil
}
func (m Merger) VisitList(nodes walk.Sources, kind walk.ListKind) (*yaml.RNode, error) {
func (m Merger) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
if err := m.SetComments(nodes); err != nil {
return nil, err
}

View File

@@ -365,7 +365,7 @@ a:
b:
# header comment
c: d
`)
`, true)
if !assert.NoError(t, err) {
return
}
@@ -385,7 +385,7 @@ a:
b:
c: d
# footer comment
`)
`, true)
if !assert.NoError(t, err) {
return
}
@@ -404,7 +404,7 @@ a:
a:
b:
c: d # line comment
`)
`, true)
if !assert.NoError(t, err) {
return
}
@@ -426,7 +426,7 @@ a:
b:
# replace comment
c: d
`)
`, true)
if !assert.NoError(t, err) {
return
}
@@ -447,7 +447,7 @@ a:
b:
c: d
# replace comment
`)
`, true)
if !assert.NoError(t, err) {
return
}
@@ -466,7 +466,7 @@ a:
a:
b:
c: d # replace comment
`)
`, true)
if !assert.NoError(t, err) {
return
}
@@ -484,7 +484,7 @@ a:
a:
b:
c: d # replace comment
`)
`, true)
if !assert.NoError(t, err) {
return
}

View File

@@ -17,24 +17,27 @@ var testCases = [][]testCase{scalarTestCases, listTestCases, elementTestCases, m
func TestMerge(t *testing.T) {
for i := range testCases {
for _, tc := range testCases[i] {
actual, err := MergeStrings(tc.source, tc.dest)
if !assert.NoError(t, err, tc.description) {
t.FailNow()
}
e, err := filters.FormatInput(bytes.NewBufferString(tc.expected))
if !assert.NoError(t, err) {
t.FailNow()
}
estr := strings.TrimSpace(e.String())
a, err := filters.FormatInput(bytes.NewBufferString(actual))
if !assert.NoError(t, err) {
t.FailNow()
}
astr := strings.TrimSpace(a.String())
if !assert.Equal(t, estr, astr, tc.description) {
t.FailNow()
}
for j := range testCases[i] {
tc := testCases[i][j]
t.Run(tc.description, func(t *testing.T) {
actual, err := MergeStrings(tc.source, tc.dest, tc.infer)
if !assert.NoError(t, err, tc.description) {
t.FailNow()
}
e, err := filters.FormatInput(bytes.NewBufferString(tc.expected))
if !assert.NoError(t, err) {
t.FailNow()
}
estr := strings.TrimSpace(e.String())
a, err := filters.FormatInput(bytes.NewBufferString(actual))
if !assert.NoError(t, err) {
t.FailNow()
}
astr := strings.TrimSpace(a.String())
if !assert.Equal(t, estr, astr, tc.description) {
t.FailNow()
}
})
}
}
}
@@ -44,4 +47,5 @@ type testCase struct {
source string
dest string
expected string
infer bool
}

View File

@@ -4,30 +4,30 @@
package merge2_test
var scalarTestCases = []testCase{
{`replace scalar -- different value in dest`,
`
{description: `replace scalar -- different value in dest`,
source: `
kind: Deployment
field: value1
`,
`
dest: `
kind: Deployment
field: value0
`,
`
expected: `
kind: Deployment
field: value1
`,
},
{`replace scalar -- missing from dest`,
`
{description: `replace scalar -- missing from dest`,
source: `
kind: Deployment
field: value1
`,
`
dest: `
kind: Deployment
`,
`
expected: `
kind: Deployment
field: value1
`,
@@ -36,16 +36,16 @@ field: value1
//
// Test Case
//
{`keep scalar -- same value in src and dest`,
`
{description: `keep scalar -- same value in src and dest`,
source: `
kind: Deployment
field: value1
`,
`
dest: `
kind: Deployment
field: value1
`,
`
expected: `
kind: Deployment
field: value1
`,
@@ -54,15 +54,15 @@ field: value1
//
// Test Case
//
{`keep scalar -- unspecified in src`,
`
{description: `keep scalar -- unspecified in src`,
source: `
kind: Deployment
`,
`
dest: `
kind: Deployment
field: value1
`,
`
expected: `
kind: Deployment
field: value1
`,
@@ -71,16 +71,16 @@ field: value1
//
// Test Case
//
{`remove scalar -- null in src`,
`
{description: `remove scalar -- null in src`,
source: `
kind: Deployment
field: null
`,
`
dest: `
kind: Deployment
field: value1
`,
`
expected: `
kind: Deployment
`,
},
@@ -88,16 +88,16 @@ kind: Deployment
//
// Test Case
//
{`remove scalar -- empty in src`,
`
{description: `remove scalar -- empty in src`,
source: `
kind: Deployment
field: {}
`,
`
dest: `
kind: Deployment
field: value1
`,
`
expected: `
kind: Deployment
field: {}
`,
@@ -106,15 +106,15 @@ field: {}
//
// Test Case
//
{`remove scalar -- null in src, missing in dest`,
`
{description: `remove scalar -- null in src, missing in dest`,
source: `
kind: Deployment
field: null
`,
`
dest: `
kind: Deployment
`,
`
expected: `
kind: Deployment
`,
},
@@ -122,15 +122,15 @@ kind: Deployment
//
// Test Case
//
{`merge an empty value`,
`
{description: `merge an empty value`,
source: `
kind: Deployment
field: {}
`,
`
dest: `
kind: Deployment
`,
`
expected: `
kind: Deployment
field: {}
`,

File diff suppressed because it is too large Load Diff

View File

@@ -9,224 +9,226 @@ var listTestCases = []testCase{
//
// Test Case
//
{`Replace list`,
`
{description: `Replace list`,
origin: `
list:
- 1
- 2
- 3`,
`
update: `
list:
- 2
- 3
- 4`,
`
local: `
list:
- 1
- 2
- 3`,
`
expected: `
list:
- 2
- 3
- 4`, nil},
- 4`},
//
// Test Case
//
{`Add an updated list`,
`
{description: `Add an updated list`,
origin: `
apiVersion: apps/v1
list: # old value
- 1
- 2
- 3
`,
`
update: `
apiVersion: apps/v1
list: # new value
- 2
- 3
- 4
`,
`
local: `
apiVersion: apps/v1`,
`
expected: `
apiVersion: apps/v1
list:
- 2
- 3
- 4
`, nil},
`},
//
// Test Case
//
{`Add keep an omitted field`,
`
{description: `Add keep an omitted field`,
origin: `
apiVersion: apps/v1
kind: Deployment`,
`
update: `
apiVersion: apps/v1
kind: StatefulSet`,
`
local: `
apiVersion: apps/v1
list: # not present in sources
- 2
- 3
- 4
`,
`
expected: `
apiVersion: apps/v1
list: # not present in sources
- 2
- 3
- 4
kind: StatefulSet
`, nil},
`},
//
// Test Case
//
// TODO(#36): consider making this an error
{`Change an updated field`,
`
{description: `Change an updated field`,
origin: `
apiVersion: apps/v1
list: # old value
- 1
- 2
- 3`,
`
update: `
apiVersion: apps/v1
list: # new value
- 2
- 3
- 4`,
`
local: `
apiVersion: apps/v1
list: # conflicting value
- a
- b
- c`,
`
expected: `
apiVersion: apps/v1
list: # conflicting value
- 2
- 3
- 4
`, nil},
`},
//
// Test Case
//
{`Ignore a field -- set`,
`
{description: `Ignore a field -- set`,
origin: `
apiVersion: apps/v1
list: # ignore value
- 1
- 2
- 3
`,
`
update: `
apiVersion: apps/v1
list: # ignore value
- 1
- 2
- 3`, `
- 3`,
local: `
apiVersion: apps/v1
list:
- 2
- 3
- 4
`, `
`,
expected: `
apiVersion: apps/v1
list:
- 2
- 3
- 4
`, nil},
`},
//
// Test Case
//
{`Ignore a field -- empty`,
`
{description: `Ignore a field -- empty`,
origin: `
apiVersion: apps/v1
list: # ignore value
- 1
- 2
- 3`,
`
update: `
apiVersion: apps/v1
list: # ignore value
- 1
- 2
- 3`,
`
local: `
apiVersion: apps/v1
`,
`
expected: `
apiVersion: apps/v1
`, nil},
`},
//
// Test Case
//
{`Explicitly clear a field`,
`
{description: `Explicitly clear a field`,
origin: `
apiVersion: apps/v1`,
`
update: `
apiVersion: apps/v1
list: null # clear`,
`
local: `
apiVersion: apps/v1
list: # value to clear
- 1
- 2
- 3`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
//
// Test Case
//
{`Implicitly clear a field`,
`
{description: `Implicitly clear a field`,
origin: `
apiVersion: apps/v1
list: # clear value
- 1
- 2
- 3`,
`
update: `
apiVersion: apps/v1`,
`
local: `
apiVersion: apps/v1
list: # old value
- 1
- 2
- 3`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
//
// Test Case
//
// TODO(#36): consider making this an error
{`Implicitly clear a changed field`,
`
{description: `Implicitly clear a changed field`,
origin: `
apiVersion: apps/v1
list: # old value
- 1
- 2
- 3`,
`
update: `
apiVersion: apps/v1`,
`
local: `
apiVersion: apps/v1
list: # old value
- a
- b
- c`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
}

View File

@@ -7,267 +7,267 @@ var mapTestCases = []testCase{
//
// Test Case
//
{`Add the annotations map field`,
`
{description: `Add the annotations map field`,
origin: `
kind: Deployment`,
`
update: `
kind: Deployment
metadata:
annotations:
d: e # add these annotations
`,
`
local: `
kind: Deployment`,
`
expected: `
kind: Deployment
metadata:
annotations:
d: e # add these annotations`, nil},
d: e # add these annotations`},
//
// Test Case
//
{`Add an annotation to the field`,
`
{description: `Add an annotation to the field`,
origin: `
kind: Deployment
metadata:
annotations:
a: b`,
`
update: `
kind: Deployment
metadata:
annotations:
a: b
d: e # add these annotations`,
`
local: `
kind: Deployment
metadata:
annotations:
g: h # keep these annotations`,
`
expected: `
kind: Deployment
metadata:
annotations:
g: h # keep these annotations
d: e # add these annotations`, nil},
d: e # add these annotations`},
//
// Test Case
//
{`Add an annotation to the field, field missing from dest`,
`
{description: `Add an annotation to the field, field missing from dest`,
origin: `
kind: Deployment
metadata:
annotations:
a: b # ignored because unchanged`,
`
update: `
kind: Deployment
metadata:
annotations:
a: b # ignore because unchanged
d: e`,
`
local: `
kind: Deployment`,
`
expected: `
kind: Deployment
metadata:
annotations:
d: e`, nil},
d: e`},
//
// Test Case
//
{`Update an annotation on the field, field messing rom the dest`,
`
{description: `Update an annotation on the field, field messing rom the dest`,
origin: `
kind: Deployment
metadata:
annotations:
a: b
d: c`,
`
update: `
kind: Deployment
metadata:
annotations:
a: b
d: e # set these annotations`,
`
local: `
kind: Deployment
metadata:
annotations:
g: h # keep these annotations`,
`
expected: `
kind: Deployment
metadata:
annotations:
g: h # keep these annotations
d: e # set these annotations`, nil},
d: e # set these annotations`},
//
// Test Case
//
{`Add an annotation to the field, field missing from dest`,
`
{description: `Add an annotation to the field, field missing from dest`,
origin: `
kind: Deployment
metadata:
annotations:
a: b # ignored because unchanged`,
`
update: `
kind: Deployment
metadata:
annotations:
a: b # ignore because unchanged
d: e`,
`
local: `
kind: Deployment`,
`
expected: `
kind: Deployment
metadata:
annotations:
d: e`, nil},
d: e`},
//
// Test Case
//
{`Remove an annotation`,
`
{description: `Remove an annotation`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a: b`,
`
update: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations: {}`,
`
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
c: d
a: b`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
c: d`, nil},
c: d`},
//
// Test Case
//
// TODO(#36) support ~annotations~: {} deletion
{`Specify a field as empty that isn't present in the source`,
`
{description: `Specify a field as empty that isn't present in the source`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo`,
`
update: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations: null`,
`
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a: b`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo`, nil},
name: foo`},
//
// Test Case
//
{`Remove an annotation`,
`
{description: `Remove an annotation`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a: b`,
`
update: `
apiVersion: apps/v1
kind: Deployment`,
`
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
c: d
a: b`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
c: d`, nil},
c: d`},
//
// Test Case
//
{`Remove annotations field`,
`
{description: `Remove annotations field`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a: b`,
`
update: `
apiVersion: apps/v1
kind: Deployment`,
`
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`, nil},
`},
//
// Test Case
//
{`Remove annotations field, but keep in dest`,
`
{description: `Remove annotations field, but keep in dest`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a: b`,
`
update: `
apiVersion: apps/v1
kind: Deployment`,
`
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
foo: bar # keep this annotation even though the parent field was removed`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
foo: bar # keep this annotation even though the parent field was removed`, nil},
foo: bar # keep this annotation even though the parent field was removed`},
//
// Test Case
//
{`Remove annotations, but they are already empty`,
`
{description: `Remove annotations, but they are already empty`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -275,24 +275,24 @@ metadata:
annotations:
a: b
`,
`
update: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
`
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations: {}
`,
`
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations: {}
`, nil},
`},
}

View File

@@ -13,11 +13,12 @@ import (
func Merge(dest, original, update *yaml.RNode) (*yaml.RNode, error) {
// if update == nil && original != nil => declarative deletion
return walk.Walker{Visitor: Visitor{},
return walk.Walker{
Visitor: Visitor{},
Sources: []*yaml.RNode{dest, original, update}}.Walk()
}
func MergeStrings(dest, original, update string) (string, error) {
func MergeStrings(dest, original, update string, infer bool) (string, error) {
srcOriginal, err := yaml.Parse(original)
if err != nil {
return "", err
@@ -31,7 +32,10 @@ func MergeStrings(dest, original, update string) (string, error) {
return "", err
}
result, err := Merge(d, srcOriginal, srcUpdated)
result, err := walk.Walker{
InferAssociativeLists: infer,
Visitor: Visitor{},
Sources: []*yaml.RNode{d, srcOriginal, srcUpdated}}.Walk()
if err != nil {
return "", err
}

View File

@@ -15,24 +15,27 @@ var testCases = [][]testCase{scalarTestCases, listTestCases, mapTestCases, eleme
func TestMerge(t *testing.T) {
for i := range testCases {
for _, tc := range testCases[i] {
actual, err := MergeStrings(tc.local, tc.origin, tc.update)
if tc.err == nil {
if !assert.NoError(t, err, tc.description) {
t.FailNow()
for j := range testCases[i] {
tc := testCases[i][j]
t.Run(tc.description, func(t *testing.T) {
actual, err := MergeStrings(tc.local, tc.origin, tc.update, tc.infer)
if tc.err == nil {
if !assert.NoError(t, err, tc.description) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(tc.expected), strings.TrimSpace(actual), tc.description) {
t.FailNow()
}
} else {
if !assert.Errorf(t, err, tc.description) {
t.FailNow()
}
if !assert.Contains(t, tc.err.Error(), err.Error()) {
t.FailNow()
}
}
if !assert.Equal(t,
strings.TrimSpace(tc.expected), strings.TrimSpace(actual), tc.description) {
t.FailNow()
}
} else {
if !assert.Errorf(t, err, tc.description) {
t.FailNow()
}
if !assert.Contains(t, tc.err.Error(), err.Error()) {
t.FailNow()
}
}
})
}
}
}
@@ -44,4 +47,5 @@ type testCase struct {
local string
expected string
err error
infer bool
}

View File

@@ -8,128 +8,128 @@ var scalarTestCases = []testCase{
//
// Test Case
//
{`Set and updated a field`,
`kind: Deployment`,
`kind: StatefulSet`,
`kind: Deployment`,
`kind: StatefulSet`, nil},
{description: `Set and updated a field`,
origin: `kind: Deployment`,
update: `kind: StatefulSet`,
local: `kind: Deployment`,
expected: `kind: StatefulSet`},
{`Add an updated field`,
`
{description: `Add an updated field`,
origin: `
apiVersion: apps/v1
kind: Deployment # old value`,
`
update: `
apiVersion: apps/v1
kind: StatefulSet # new value`,
`
local: `
apiVersion: apps/v1`,
`
expected: `
apiVersion: apps/v1
kind: StatefulSet # new value`, nil},
kind: StatefulSet # new value`},
{`Add keep an omitted field`,
`
{description: `Add keep an omitted field`,
origin: `
apiVersion: apps/v1
kind: Deployment`,
`
update: `
apiVersion: apps/v1
kind: StatefulSet`,
`
local: `
apiVersion: apps/v1
spec: foo # field not present in source
`,
`
expected: `
apiVersion: apps/v1
spec: foo # field not present in source
kind: StatefulSet
`, nil},
`},
//
// Test Case
//
// TODO(#36): consider making this an error
{`Change an updated field`,
`
{description: `Change an updated field`,
origin: `
apiVersion: apps/v1
kind: Deployment # old value`,
`
update: `
apiVersion: apps/v1
kind: StatefulSet # new value`,
`
local: `
apiVersion: apps/v1
kind: Service # conflicting value`,
`
expected: `
apiVersion: apps/v1
kind: StatefulSet # new value`, nil},
kind: StatefulSet # new value`},
{`Ignore a field`,
`
{description: `Ignore a field`,
origin: `
apiVersion: apps/v1
kind: Deployment # ignore this field`,
`
update: `
apiVersion: apps/v1
kind: Deployment # ignore this field`,
`
local: `
apiVersion: apps/v1`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
{`Explicitly clear a field`,
`
{description: `Explicitly clear a field`,
origin: `
apiVersion: apps/v1`,
`
update: `
apiVersion: apps/v1
kind: null # clear this value`,
`
local: `
apiVersion: apps/v1
kind: Deployment # value to be cleared`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
{`Implicitly clear a field`,
`
{description: `Implicitly clear a field`,
origin: `
apiVersion: apps/v1
kind: Deployment # clear this field`,
`
update: `
apiVersion: apps/v1`,
`
local: `
apiVersion: apps/v1
kind: Deployment # clear this field`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
//
// Test Case
//
// TODO(#36): consider making this an error
{`Implicitly clear a changed field`,
`
{description: `Implicitly clear a changed field`,
origin: `
apiVersion: apps/v1
kind: Deployment`,
`
update: `
apiVersion: apps/v1`,
`
local: `
apiVersion: apps/v1
kind: StatefulSet`,
`
apiVersion: apps/v1`, nil},
expected: `
apiVersion: apps/v1`},
//
// Test Case
//
{`Merge an empty scalar value`,
`
{description: `Merge an empty scalar value`,
origin: `
apiVersion: apps/v1
`,
`
update: `
apiVersion: apps/v1
kind: {}
`,
`
local: `
apiVersion: apps/v1
`,
`
expected: `
apiVersion: apps/v1
kind: {}
`, nil},
`},
}

View File

@@ -4,6 +4,7 @@
package merge3
import (
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
)
@@ -17,7 +18,7 @@ const (
type Visitor struct{}
func (m Visitor) VisitMap(nodes walk.Sources) (*yaml.RNode, error) {
func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
// explicitly cleared from either dest or update
return walk.ClearNode, nil
@@ -36,7 +37,7 @@ func (m Visitor) VisitMap(nodes walk.Sources) (*yaml.RNode, error) {
return nodes.Dest(), nil
}
func (m Visitor) visitAList(nodes walk.Sources) (*yaml.RNode, error) {
func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
if yaml.IsEmpty(nodes.Updated()) && !yaml.IsEmpty(nodes.Origin()) {
// implicitly cleared from update -- element was deleted
return walk.ClearNode, nil
@@ -51,7 +52,7 @@ func (m Visitor) visitAList(nodes walk.Sources) (*yaml.RNode, error) {
return nodes.Dest(), nil
}
func (m Visitor) VisitScalar(nodes walk.Sources) (*yaml.RNode, error) {
func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
// explicitly cleared from either dest or update
return nil, nil
@@ -103,9 +104,9 @@ func (m Visitor) visitNAList(nodes walk.Sources) (*yaml.RNode, error) {
return nodes.Dest(), nil
}
func (m Visitor) VisitList(nodes walk.Sources, kind walk.ListKind) (*yaml.RNode, error) {
func (m Visitor) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
if kind == walk.AssociativeList {
return m.visitAList(nodes)
return m.visitAList(nodes, s)
}
// non-associative list
return m.visitNAList(nodes)

View File

@@ -0,0 +1,34 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package schema contains libraries for working with the yaml and openapi packages.
package schema
import (
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// IsAssociative returns true if all elements in the list contain an AssociativeSequenceKey
// as a field.
func IsAssociative(schema *openapi.ResourceSchema, nodes []*yaml.RNode, infer bool) bool {
if schema != nil {
// use the schema to identify if the list is associative
s, _ := schema.PatchStrategyAndKey()
return s == "merge"
}
if !infer {
return false
}
for i := range nodes {
node := nodes[i]
if yaml.IsEmpty(node) {
continue
}
if node.IsAssociative() {
return true
}
}
return false
}

View File

@@ -651,24 +651,8 @@ func (rn *RNode) VisitElements(fn func(node *RNode) error) error {
// The order sets the precedence of the merge keys -- if multiple keys are present
// in Resources in a list, then the FIRST key which ALL elements in the list have is used as the
// associative key for merging that list.
var AssociativeSequenceKeys = []string{
"mountPath", "devicePath", "ip", "type", "topologyKey", "name", "containerPort",
}
// IsAssociative returns true if all elements in the list contain an AssociativeSequenceKey
// as a field.
func IsAssociative(nodes []*RNode) bool {
for i := range nodes {
node := nodes[i]
if IsEmpty(node) {
continue
}
if node.IsAssociative() {
return true
}
}
return false
}
// Only infer name as a merge key.
var AssociativeSequenceKeys = []string{"name"}
// IsAssociative returns true if the RNode contains an AssociativeSequenceKey as a field.
func (rn *RNode) IsAssociative() bool {

View File

@@ -7,28 +7,44 @@ import (
"strings"
"github.com/go-errors/errors"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
// may require initializing the dest node
dest, err := l.Sources.setDestNode(l.VisitList(l.Sources, AssociativeList))
dest, err := l.Sources.setDestNode(l.VisitList(l.Sources, l.Schema, AssociativeList))
if dest == nil || err != nil {
return nil, err
}
// find the list of elements we need to recursively walk
key, err := l.elementKey()
if err != nil {
return nil, err
var key string
if l.Schema != nil {
_, key = l.Schema.PatchStrategyAndKey()
}
if key == "" { // no key from the schema, try to infer one
// find the list of elements we need to recursively walk
key, err = l.elementKey()
if err != nil {
return nil, err
}
}
values := l.elementValues(key)
// recursively set the elements in the list
var s *openapi.ResourceSchema
if l.Schema != nil {
s = l.Schema.Elements()
}
for _, value := range values {
val, err := Walker{Visitor: l,
Sources: l.elementValue(key, value)}.Walk()
val, err := Walker{
InferAssociativeLists: l.InferAssociativeLists,
Visitor: l,
Schema: s,
Sources: l.elementValue(key, value),
}.Walk()
if err != nil {
return nil, err
}

View File

@@ -6,6 +6,8 @@ package walk
import (
"sort"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -18,15 +20,27 @@ import (
// - set each source field value on l.Dest
func (l Walker) walkMap() (*yaml.RNode, error) {
// get the new map value
dest, err := l.Sources.setDestNode(l.VisitMap(l.Sources))
dest, err := l.Sources.setDestNode(l.VisitMap(l.Sources, l.Schema))
if dest == nil || err != nil {
return nil, err
}
// recursively set the field values on the map
for _, key := range l.fieldNames() {
val, err := Walker{Visitor: l,
Sources: l.fieldValue(key), Path: append(l.Path, key)}.Walk()
var s *openapi.ResourceSchema
if l.Schema != nil {
s = l.Schema.Field(key)
}
fv, commentSch := l.fieldValue(key)
if commentSch != nil {
s = commentSch
}
val, err := Walker{
InferAssociativeLists: l.InferAssociativeLists,
Visitor: l,
Schema: s,
Sources: fv,
Path: append(l.Path, key)}.Walk()
if err != nil {
return nil, err
}
@@ -42,11 +56,40 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
}
// valueIfPresent returns node.Value if node is non-nil, otherwise returns nil
func (l Walker) valueIfPresent(node *yaml.MapNode) *yaml.RNode {
func (l Walker) valueIfPresent(node *yaml.MapNode) (*yaml.RNode, *openapi.ResourceSchema) {
if node == nil {
return nil
return nil, nil
}
return node.Value
// parse the schema for the field if present
var s *openapi.ResourceSchema
fm := fieldmeta.FieldMeta{}
var err error
// check the value for a schema
if err = fm.Read(node.Value); err == nil {
s = &openapi.ResourceSchema{Schema: &fm.Schema}
if fm.Schema.Ref.String() != "" {
r, err := openapi.Resolve(&fm.Schema.Ref)
if err == nil && r != nil {
s.Schema = r
}
}
}
// check the key for a schema -- this will be used
// when the value is a Sequence (comments are attached)
// to the key
if fm.IsEmpty() {
if err = fm.Read(node.Key); err == nil {
s = &openapi.ResourceSchema{Schema: &fm.Schema}
}
if fm.Schema.Ref.String() != "" {
r, err := openapi.Resolve(&fm.Schema.Ref)
if err == nil && r != nil {
s.Schema = r
}
}
}
return node.Value, s
}
// fieldNames returns a sorted slice containing the names of all fields that appear in any of
@@ -67,15 +110,20 @@ func (l Walker) fieldNames() []string {
}
// fieldValue returns a slice containing each source's value for fieldName
func (l Walker) fieldValue(fieldName string) []*yaml.RNode {
func (l Walker) fieldValue(fieldName string) ([]*yaml.RNode, *openapi.ResourceSchema) {
var fields []*yaml.RNode
var sch *openapi.ResourceSchema
for i := range l.Sources {
if l.Sources[i] == nil {
fields = append(fields, nil)
continue
}
field := l.Sources[i].Field(fieldName)
fields = append(fields, l.valueIfPresent(field))
f, s := l.valueIfPresent(field)
fields = append(fields, f)
if sch == nil && !s.IsEmpty() {
sch = s
}
}
return fields
return fields, sch
}

View File

@@ -9,5 +9,5 @@ import (
// walkNonAssociativeSequence returns the value of VisitList
func (l Walker) walkNonAssociativeSequence() (*yaml.RNode, error) {
return l.VisitList(l.Sources, NonAssociateList)
return l.VisitList(l.Sources, l.Schema, NonAssociateList)
}

View File

@@ -7,5 +7,5 @@ import "sigs.k8s.io/kustomize/kyaml/yaml"
// walkScalar returns the value of VisitScalar
func (l Walker) walkScalar() (*yaml.RNode, error) {
return l.VisitScalar(l.Sources)
return l.VisitScalar(l.Sources, l.Schema)
}

View File

@@ -4,6 +4,7 @@
package walk
import (
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -16,11 +17,11 @@ const (
// Visitor is invoked by walk with source and destination node pairs
type Visitor interface {
VisitMap(nodes Sources) (*yaml.RNode, error)
VisitMap(Sources, *openapi.ResourceSchema) (*yaml.RNode, error)
VisitScalar(nodes Sources) (*yaml.RNode, error)
VisitScalar(Sources, *openapi.ResourceSchema) (*yaml.RNode, error)
VisitList(nodes Sources, kind ListKind) (*yaml.RNode, error)
VisitList(Sources, *openapi.ResourceSchema, ListKind) (*yaml.RNode, error)
}
// ClearNode is returned if GrepFilter should do nothing after calling Set

View File

@@ -8,20 +8,30 @@ import (
"os"
"strings"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/schema"
)
// Filter walks the Source RNode and modifies the RNode provided to GrepFilter.
// Walker walks the Source RNode and modifies the RNode provided to GrepFilter.
type Walker struct {
// Visitor is invoked by GrepFilter
Visitor
Schema *openapi.ResourceSchema
// Source is the RNode to walk. All Source fields and associative list elements
// will be visited.
Sources Sources
// Path is the field path to the current Source Node.
Path []string
// InferAssociativeLists if set to true will infer merge strategies for
// fields which it doesn't have the schema based on the fields in the
// list elements.
InferAssociativeLists bool
}
func (l Walker) Kind() yaml.Kind {
@@ -35,6 +45,8 @@ func (l Walker) Kind() yaml.Kind {
// GrepFilter implements yaml.GrepFilter
func (l Walker) Walk() (*yaml.RNode, error) {
l.Schema = l.GetSchema()
// invoke the handler for the corresponding node type
switch l.Kind() {
case yaml.MappingNode:
@@ -46,7 +58,7 @@ func (l Walker) Walk() (*yaml.RNode, error) {
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.SequenceNode, l.Sources...); err != nil {
return nil, err
}
if yaml.IsAssociative(l.Sources) {
if schema.IsAssociative(l.Schema, l.Sources, l.InferAssociativeLists) {
return l.walkAssociativeSequence()
}
return l.walkNonAssociativeSequence()
@@ -64,6 +76,49 @@ func (l Walker) Walk() (*yaml.RNode, error) {
}
}
func (l Walker) GetSchema() *openapi.ResourceSchema {
for i := range l.Sources {
r := l.Sources[i]
if yaml.IsEmpty(r) {
continue
}
fm := fieldmeta.FieldMeta{}
if err := fm.Read(r); err == nil && !fm.IsEmpty() {
// per-field schema, this is fine
if fm.Schema.Ref.String() != "" {
// resolve the reference
s, err := openapi.Resolve(&fm.Schema.Ref)
if err == nil && s != nil {
fm.Schema = *s
}
}
return &openapi.ResourceSchema{Schema: &fm.Schema}
}
}
if l.Schema != nil {
return l.Schema
}
for i := range l.Sources {
r := l.Sources[i]
if yaml.IsEmpty(r) {
continue
}
m, _ := r.GetMeta()
if m.Kind == "" || m.APIVersion == "" {
continue
}
s := openapi.SchemaForResourceType(yaml.TypeMeta{Kind: m.Kind, APIVersion: m.APIVersion})
if s != nil {
return s
}
}
return nil
}
const (
DestIndex = iota
OriginIndex

View File

@@ -148,8 +148,10 @@ func (p *plugin) findContainers(obj map[string]interface{}) error {
}
func isImageMatched(s, t string) bool {
// Tag values are limited to [a-zA-Z0-9_.-].
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
// Tag values are limited to [a-zA-Z0-9_.{}-].
// Some tools like Bazel rules_k8s allow tag patterns with {} characters.
// More info: https://github.com/bazelbuild/rules_k8s/pull/423
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.{}-]*)?$")
return pattern.MatchString(s)
}

View File

@@ -5,8 +5,8 @@
# kyaml version
export kyaml_major=0
export kyaml_minor=0
export kyaml_patch=11
export kyaml_minor=1
export kyaml_patch=0
# kstatus version
export kstatus_major=0
@@ -16,12 +16,12 @@ export kstatus_patch=1
# kustomize api version
export api_major=0
export api_minor=3
export api_patch=2
export api_patch=3
# cmd/config version
export cmd_config_major=0
export cmd_config_minor=0
export cmd_config_patch=11
export cmd_config_minor=1
export cmd_config_patch=0
# cmd/kubectl version
export cmd_kubectl_major=0

View File

@@ -5,7 +5,7 @@
set -e
# run all tests for kyaml and related commands
targets="kyaml cmd/config cmd/kubectl functions/examples/injection-tshirt-sizes functions/examples/template-go-nginx functions/examples/template-heredoc-cockroachdb functions/examples/validator-kubeval functions/examples/validator-resource-requests"
targets="kyaml cmd/config cmd/kubectl functions/examples/injection-tshirt-sizes functions/examples/template-go-nginx functions/examples/template-heredoc-cockroachdb functions/examples/validator-kubeval functions/examples/validator-resource-requests functions/examples/application-cr"
for target in $targets; do
pushd .
cd $target