Compare commits

..

46 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
064f0641ba Merge pull request #2287 from phanimarupaka/DontWriteToNoSetFiles
Skip writing to unedited files
2020-03-20 18:30:43 -07:00
Phani Teja Marupaka
a36d5b76be Skip writing to unedited files 2020-03-20 16:24:16 -07:00
Kubernetes Prow Robot
5c0fbf9a7f Merge pull request #2284 from phanimarupaka/AddNetworkFlag
Generate network config
2020-03-20 11:19:09 -07:00
Phani Teja Marupaka
9a9bdee605 Generate network config 2020-03-20 10:24:24 -07:00
Kubernetes Prow Robot
ce2e685619 Merge pull request #2257 from ticketmaster/misspell
fix misspellings
2020-03-19 09:17:23 -07:00
Michael Cook
aa46b6ec44 fix misspellings 2020-03-18 14:36:12 +01:00
Kubernetes Prow Robot
c4d949333d Merge pull request #2283 from phanimarupaka/AddFieldSetterFilter
Add FieldSetter filter
2020-03-17 10:34:22 -07:00
Phani Teja Marupaka
702b10d524 Add FieldSetter filter 2020-03-17 10:08:29 -07:00
Kubernetes Prow Robot
7840b7f949 Merge pull request #2265 from pwittrock/merge3
Setters: support for setting string list fields
2020-03-17 09:44:30 -07:00
Kubernetes Prow Robot
2cf5dc16cb Merge pull request #2137 from rhomel/image-match-tag-braces
Allow braces in image tag name
2020-03-17 09:44:22 -07:00
Kubernetes Prow Robot
036b93c8d0 Merge pull request #2280 from yujunz/golangci-lint
Exlude golangci-lint v1.24.0 which breaks CI
2020-03-17 09:26:22 -07:00
Yujun Zhang
cad69ae415 Exlude golangci-lint v1.24.0 which breaks CI 2020-03-16 18:16:38 +08:00
Kubernetes Prow Robot
5b774d09e1 Merge pull request #2215 from giteshk/master
#2214 : Variable reference does not work on the nfs.server
2020-03-10 09:33:36 -07:00
Kubernetes Prow Robot
9cbabe9135 Merge pull request #2267 from pwittrock/starlark
Starlark Filter support
2020-03-09 19:43:36 -07:00
Phillip Wittrock
7e8f8a649c Better starlark documentation 2020-03-09 16:41:57 -07:00
Phillip Wittrock
68a9389bfe starlark fn support for functionConfig input 2020-03-09 10:30:20 -07:00
Phillip Wittrock
5364b2198a Add Starlark kio.Filter
- Support for implementing filters in Starlark
2020-03-09 10:30:18 -07:00
Phillip Wittrock
63ff34ffe2 Walk: opt-in to visiting map keys when walking map entries 2020-03-09 10:29:26 -07:00
Kubernetes Prow Robot
6944cea234 Merge pull request #2264 from yujunz/revert-2169
Revert "Merge pull request #2169 from yujunz/loader/go-getter"
2020-03-09 09:47:36 -07:00
Phillip Wittrock
370502ed4b Setters: support for setting string list fields 2020-03-07 13:53:24 -08:00
Yujun Zhang
711bab85ae Revert "Merge pull request #2169 from yujunz/loader/go-getter"
This reverts commit 0b1ad031a9, reversing
changes made to 300dd108d5.
2020-03-07 18:58:46 +08:00
Kubernetes Prow Robot
0b1ad031a9 Merge pull request #2169 from yujunz/loader/go-getter
Replace git cloner with go getter to support various target
2020-03-06 15:45:34 -08:00
Kubernetes Prow Robot
300dd108d5 Merge pull request #2167 from yujunz/loader/http
Allow loading file from http
2020-03-06 15:31:34 -08:00
Kubernetes Prow Robot
65522b9e9f Merge pull request #2182 from chancez/per_resource_generator_options
Enable specifying GeneratorOptions per resource
2020-03-06 14:51:34 -08:00
Kubernetes Prow Robot
270f832b07 Merge pull request #2261 from pwittrock/master
release kyaml cmd/config 1.1
2020-03-06 12:33:08 -08:00
Phillip Wittrock
f2dddf48ae release kyaml cmd/config 1.1 2020-03-06 09:29:25 -08:00
Yujun Zhang
735cefa456 Use a lite fork of go-getter 2020-03-04 20:04:39 +08:00
Yujun Zhang
a24bf6aece Use a tailored go-getter to get rid of transitive depenencies 2020-03-04 20:04:39 +08:00
Yujun Zhang
fae66446a8 Replace git cloner with go getter to support various target 2020-03-04 20:04:39 +08:00
Yujun Zhang
382425d974 Add unit test for http fileloader 2020-02-29 16:19:21 +08:00
Yujun Zhang
ff6250cdb4 Allow loading file from http 2020-02-29 16:19:21 +08:00
Gitesh Koli
70ac18f720 Corrected CronJob volume Path and the expected results 2020-02-28 16:46:09 -05:00
Gitesh Koli
bb6677f80a Fixed the YAML format error 2020-02-28 16:31:23 -05:00
install_apigee_kickstart_on_gke.sh script
3408c3e4c6 Fixed Tab to spaces 2020-02-28 16:11:22 -05:00
install_apigee_kickstart_on_gke.sh script
a3800701f1 Additional Tests to cover CronJob, DaemonSet, ReplicaSet, StatefulSet, Pod, Job 2020-02-28 15:52:59 -05:00
Chance Zibolski
aec264d2b5 docs: Document per-resource generatorOptions 2020-02-26 09:54:43 -08:00
Chance Zibolski
62f21cbe69 Enable specifying GeneratorOptions per resource
In addition to specifying GeneratorOptions globally within a
kustomization, also allow users to set GeneratorOptions on a
per-resource level.
2020-02-26 09:54:43 -08:00
install_apigee_kickstart_on_gke.sh script
bfb1b44b15 Corrected the test data 2020-02-20 10:27:16 -05:00
install_apigee_kickstart_on_gke.sh script
9ebee1e247 Enable variable reference in the Volumes which involve NFS server 2020-02-20 00:23:10 -05:00
Rhomel Chinsio
59ce165355 docs: comment brace support purpose 2020-01-24 17:46:58 +09:00
Rhomel Chinsio
a7201a38e4 build: use local repo for test 2020-01-24 16:58:10 +09:00
Rhomel Chinsio
80633137a9 fix: test tags with braces 2020-01-24 15:10:22 +09:00
Rhomel Chinsio
2a28b37b3c fix: place '-' as last character in regex class 2020-01-22 16:57:39 +09:00
Rhomel Chinsio
866303a0d7 fix: update generated file 2020-01-22 15:39:12 +09:00
Rhomel Chinsio
bce9cb5710 fix: update regex comment 2020-01-22 15:36:58 +09:00
Rhomel Chinsio
a9d35cc598 fix: add braces to image tag match 2020-01-22 15:36:02 +09:00
62 changed files with 3047 additions and 113 deletions

View File

@@ -35,12 +35,12 @@ verify-kustomize-e2e: test-examples-e2e-kustomize
# since everything uses the same implicit GOPATH.
# This installs in a temp dir to avoid overwriting someone else's
# linter, then installs in MYGOBIN with a new name.
# Version pinned by api/go.mod
# Version pinned by hack/go.mod
$(MYGOBIN)/golangci-lint-kustomize:
( \
set -e; \
export GOBIN=$$(mktemp -d) \
cd api; \
export GOBIN=$$(mktemp -d); \
cd hack; \
GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint; \
mv $$GOBIN/golangci-lint $(MYGOBIN)/golangci-lint-kustomize \
)

View File

@@ -107,7 +107,7 @@ Take the work from step (1) above, move it into a
`someApp` subdirectory called `base`, then
place overlays in a sibling directory.
An overlay is just another kustomization, refering to
An overlay is just another kustomization, referring to
the base, and referring to patches to apply to that
base.

View File

@@ -52,7 +52,7 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
if len(matches) != 1 {
return fmt.Errorf("namespace tranformation produces ID conflict: %+v", matches)
return fmt.Errorf("namespace transformation produces ID conflict: %+v", matches)
}
}
return nil

View File

@@ -209,7 +209,7 @@ but they are functional.
## Technical details
### Overall design and imlpementation
### Overall design and implementations
There are a few components that are all running together in order to get
the overall application to work smoothly. This section will provide a brief

View File

@@ -11,7 +11,7 @@ metadata:
spec:
storage:
gcs:
# the bucket must exist for the snapshot respository to be created successfully.
# the bucket must exist for the snapshot repository to be created successfully.
bucket: kustomize-backup
# the path does not need to exist.
# If the path does not exist, the controller will create the folder in the GCS bucket.

View File

@@ -1,4 +1,4 @@
Find all the trasnformer files whose `kinds` field includes `HelmValues`, and
Find all the transformer files whose `kinds` field includes `HelmValues`, and
only output certain fields of each document:
```
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'

View File

@@ -38,10 +38,7 @@ func (f *Factory) MakeConfigMap(
return nil, errors.Wrap(err, "trouble mapping")
}
}
if f.options != nil {
cm.SetLabels(f.options.Labels)
cm.SetAnnotations(f.options.Annotations)
}
f.setLabelsAndAnnnotations(cm, args.GeneratorOptions)
return cm, nil
}

View File

@@ -53,7 +53,7 @@ BAR=baz
}
}
func makeLiteralConfigMap(name string) *corev1.ConfigMap {
func makeLiteralConfigMap(name string, labels, annotations map[string]string) *corev1.ConfigMap {
cm := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
@@ -69,7 +69,12 @@ func makeLiteralConfigMap(name string) *corev1.ConfigMap {
"d": "true",
},
}
cm.SetLabels(map[string]string{"foo": "bar"})
if labels != nil {
cm.SetLabels(labels)
}
if annotations != nil {
cm.SetAnnotations(annotations)
}
return cm
}
@@ -128,7 +133,49 @@ func TestConstructConfigMap(t *testing.T) {
"foo": "bar",
},
},
expected: makeLiteralConfigMap("literalConfigMap"),
expected: makeLiteralConfigMap("literalConfigMap", map[string]string{
"foo": "bar",
}, nil),
},
{
description: "construct config map from literal with GeneratorOptions in ConfigMapArgs",
input: types.ConfigMapArgs{
GeneratorArgs: types.GeneratorArgs{
Name: "literalConfigMap",
KvPairSources: types.KvPairSources{
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
},
GeneratorOptions: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "changed",
"cat": "dog",
},
Annotations: map[string]string{
"foo": "changed",
"cat": "dog",
},
},
},
},
options: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "bar",
},
Annotations: map[string]string{
"foo": "bar",
},
},
// GeneratorOptions from the ConfigMapArgs take precedence over the
// factory level GeneratorOptions and should overwrite
// labels/annotations set in the factory level if there are common
// labels/annotations
expected: makeLiteralConfigMap("literalConfigMap", map[string]string{
"foo": "changed",
"cat": "dog",
}, map[string]string{
"foo": "changed",
"cat": "dog",
}),
},
}

View File

@@ -4,6 +4,7 @@
package configmapandsecret
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types"
)
@@ -20,4 +21,35 @@ func NewFactory(
return &Factory{kvLdr: kvLdr, options: o}
}
// setLabelsAndAnnnotations will take the labels and annotations from
// global GeneratorOptions and resource level GeneratorOptions and merge them
// with the resource level taking precedence, and then set them on the provided
// obj.
func (f *Factory) setLabelsAndAnnnotations(obj metav1.Object, opts *types.GeneratorOptions) {
labels := make(map[string]string)
annotations := make(map[string]string)
if f.options != nil {
for k, v := range f.options.Labels {
labels[k] = v
}
for k, v := range f.options.Annotations {
annotations[k] = v
}
}
if opts != nil {
for k, v := range opts.Labels {
labels[k] = v
}
for k, v := range opts.Annotations {
annotations[k] = v
}
}
if len(labels) != 0 {
obj.SetLabels(labels)
}
if len(annotations) != 0 {
obj.SetAnnotations(annotations)
}
}
const keyExistsErrorMsg = "cannot add key %s, another key by that name already exists: %v"

View File

@@ -39,10 +39,7 @@ func (f *Factory) MakeSecret(
return nil, err
}
}
if f.options != nil {
s.SetLabels(f.options.Labels)
s.SetAnnotations(f.options.Annotations)
}
f.setLabelsAndAnnnotations(s, args.GeneratorOptions)
return s, nil
}

View File

@@ -51,7 +51,7 @@ BAR=baz
}
}
func makeLiteralSecret(name string) *corev1.Secret {
func makeLiteralSecret(name string, labels, annotations map[string]string) *corev1.Secret {
s := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
@@ -66,7 +66,12 @@ func makeLiteralSecret(name string) *corev1.Secret {
},
Type: "Opaque",
}
s.SetLabels(map[string]string{"foo": "bar"})
if labels != nil {
s.SetLabels(labels)
}
if annotations != nil {
s.SetAnnotations(annotations)
}
return s
}
@@ -120,7 +125,49 @@ func TestConstructSecret(t *testing.T) {
"foo": "bar",
},
},
expected: makeLiteralSecret("literalSecret"),
expected: makeLiteralSecret("literalSecret", map[string]string{
"foo": "bar",
}, nil),
},
{
description: "construct secret from literal with GeneratorOptions in SecretArgs",
input: types.SecretArgs{
GeneratorArgs: types.GeneratorArgs{
Name: "literalSecret",
KvPairSources: types.KvPairSources{
LiteralSources: []string{"a=x", "b=y"},
},
GeneratorOptions: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "changed",
"cat": "dog",
},
Annotations: map[string]string{
"foo": "changed",
"cat": "dog",
},
},
},
},
options: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "bar",
},
Annotations: map[string]string{
"foo": "bar",
},
},
// GeneratorOptions from the SecretArgs take precedence over the
// factory level GeneratorOptions and should overwrite
// labels/annotations set in the factory level if there are common
// labels/annotations
expected: makeLiteralSecret("literalSecret", map[string]string{
"foo": "changed",
"cat": "dog",
}, map[string]string{
"foo": "changed",
"cat": "dog",
}),
},
}

View File

@@ -7,7 +7,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/pkg/errors"
@@ -302,18 +301,15 @@ func (kt *KustTarget) configureExternalTransformers() ([]resmap.Transformer, err
func (kt *KustTarget) accumulateResources(
ra *accumulator.ResAccumulator, paths []string) error {
for _, path := range paths {
ldr, err := kt.ldr.New(path)
if err == nil {
err = kt.accumulateDirectory(ra, ldr)
if err != nil {
return err
// try loading resource as file then as base (directory or git repository)
if errF := kt.accumulateFile(ra, path); errF != nil {
ldr, errL := kt.ldr.New(path)
if errL != nil {
return fmt.Errorf("accumulateFile %q, loader.New %q", errF, errL)
}
} else {
err2 := kt.accumulateFile(ra, path)
if err2 != nil {
// Log ldr.New() error to highlight git failures.
log.Print(err.Error())
return err2
errD := kt.accumulateDirectory(ra, ldr)
if errD != nil {
return fmt.Errorf("accumulateFile %q, accumulateDirector: %q", errF, errD)
}
}
}

View File

@@ -30,6 +30,9 @@ varReference:
- path: spec/jobTemplate/spec/template/spec/initContainers/volumeMounts/mountPath
kind: CronJob
- path: spec/jobTemplate/spec/template/volumes/nfs/server
kind: CronJob
- path: spec/template/spec/containers/args
kind: DaemonSet
@@ -54,6 +57,9 @@ varReference:
- path: spec/template/spec/initContainers/volumeMounts/mountPath
kind: DaemonSet
- path: spec/template/spec/volumes/nfs/server
kind: DaemonSet
- path: spec/template/spec/containers/args
kind: Deployment
@@ -78,6 +84,9 @@ varReference:
- path: spec/template/spec/initContainers/volumeMounts/mountPath
kind: Deployment
- path: spec/template/spec/volumes/nfs/server
kind: Deployment
- path: spec/rules/host
kind: Ingress
@@ -111,6 +120,9 @@ varReference:
- path: spec/template/spec/initContainers/volumeMounts/mountPath
kind: Job
- path: spec/template/spec/volumes/nfs/server
kind: Job
- path: spec/containers/args
kind: Pod
@@ -135,6 +147,9 @@ varReference:
- path: spec/initContainers/volumeMounts/mountPath
kind: Pod
- path: spec/volumes/nfs/server
kind: Pod
- path: spec/template/spec/containers/args
kind: ReplicaSet
@@ -159,6 +174,9 @@ varReference:
- path: spec/template/spec/initContainers/volumeMounts/mountPath
kind: ReplicaSet
- path: spec/template/spec/volumes/nfs/server
kind: ReplicaSet
- path: spec/ports/port
kind: Service
@@ -189,6 +207,12 @@ varReference:
- path: spec/template/spec/initContainers/volumeMounts/mountPath
kind: StatefulSet
- path: spec/volumeClaimTemplates/spec/nfs/server
kind: StatefulSet
- path: spec/nfs/server
kind: PersistentVolume
- path: metadata/labels
- path: metadata/annotations

View File

@@ -74,7 +74,7 @@ spec:
if err == nil {
t.Fatalf("expected an error")
}
if !IsMissingKustomizationFileError(err) {
if !strings.Contains(err.Error(), "accumulating resources") {
t.Fatalf("unexpected error: %q", err)
}
}
@@ -89,7 +89,7 @@ resources:
if err == nil {
t.Fatalf("expected an error")
}
if !strings.Contains(err.Error(), "'/app/deployment.yaml' doesn't exist") {
if !strings.Contains(err.Error(), "accumulating resources") {
t.Fatalf("unexpected error: %q", err)
}
}

View File

@@ -1336,3 +1336,637 @@ spec:
protocol: TCP
`)
}
func TestVariableRefNFSServer(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", `
resources:
- pv_pvc.yaml
- nfs_deployment.yaml
- nfs_service.yaml
- Deployment.yaml
- CronJob.yaml
- DaemonSet.yaml
- ReplicaSet.yaml
- StatefulSet.yaml
- Pod.yaml
- Job.yaml
- nfs_pv.yaml
vars:
- name: NFS_SERVER_SERVICE_NAME
objref:
kind: Service
name: nfs-server-service
apiVersion: v1
fieldref:
fieldpath: metadata.name
`)
th.WriteF("/app/base/pv_pvc.yaml", `
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-volume-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
`)
th.WriteF("/app/base/nfs_deployment.yaml", `
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-server
spec:
replicas: 1
template:
spec:
metadata:
labels:
role: nfs-server
containers:
- name: nfs-server
image: gcr.io/google_containers/volume-nfs:0.8
ports:
- name: nfs
containerPort: 2049
- name: mountd
containerPort: 20048
- name: rpcbind
containerPort: 111
securityContext:
privileged: true
volumeMounts:
- mountPath: /exports
name: shared-files
volumes:
- name: shared-files
persistentVolumeClaim:
claimName: shared-volume-claim
`)
th.WriteF("/app/base/nfs_service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: nfs-server-service
spec:
ports:
- name: nfs
port: 2049
- name: mountd
port: 20048
- name: rpcbind
port: 111
selector:
role: nfs-server
`)
th.WriteF("/app/base/Deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app.kubernetes.io/component: nginx
spec:
selector:
matchLabels:
app.kubernetes.io/component: nginx
template:
metadata:
labels:
app.kubernetes.io/component: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.7-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteF("/app/base/CronJob.yaml", `
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteF("/app/base/DaemonSet.yaml", `
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- mountPath: /app/shared-files
name: nfs-files-vol
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: nfs-files-vol
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteF("/app/base/ReplicaSet.yaml", `
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# modify replicas according to your case
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteF("/app/base/Job.yaml", `
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
restartPolicy: Never
volumes:
- name: nfs-files-vol
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
backoffLimit: 4
`)
th.WriteF("/app/base/StatefulSet.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # by default is 1
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteMany" ]
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteF("/app/base/Pod.yaml", `
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: nginx
image: nginx:1.15.7-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: nfs-files-vol
mountPath: /app/shared-files
volumes:
- name: nfs-files-vol
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteF("/app/base/nfs_pv.yaml", `
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-files-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local
path: /
readOnly: false
`)
th.WriteK("/app/overlay", `
nameprefix: kustomized-
resources:
- ../base
`)
m := th.Run("/app/overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: kustomized-shared-volume-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kustomized-nfs-server
spec:
replicas: 1
template:
spec:
containers:
- image: gcr.io/google_containers/volume-nfs:0.8
name: nfs-server
ports:
- containerPort: 2049
name: nfs
- containerPort: 20048
name: mountd
- containerPort: 111
name: rpcbind
securityContext:
privileged: true
volumeMounts:
- mountPath: /exports
name: shared-files
metadata:
labels:
role: nfs-server
volumes:
- name: shared-files
persistentVolumeClaim:
claimName: kustomized-shared-volume-claim
---
apiVersion: v1
kind: Service
metadata:
name: kustomized-nfs-server-service
spec:
ports:
- name: nfs
port: 2049
- name: mountd
port: 20048
- name: rpcbind
port: 111
selector:
role: nfs-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: nginx
name: kustomized-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/component: nginx
template:
metadata:
labels:
app.kubernetes.io/component: nginx
spec:
containers:
- image: nginx:1.15.7-alpine
name: nginx
ports:
- containerPort: 80
name: http
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: kustomized-hello
spec:
jobTemplate:
spec:
template:
spec:
containers:
- args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
image: busybox
name: hello
restartPolicy: OnFailure
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
schedule: '*/1 * * * *'
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
k8s-app: fluentd-logging
name: kustomized-fluentd-elasticsearch
namespace: kube-system
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
containers:
- image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
name: fluentd-elasticsearch
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- mountPath: /var/log
name: varlog
- mountPath: /var/lib/docker/containers
name: varlibdockercontainers
readOnly: true
- mountPath: /app/shared-files
name: nfs-files-vol
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
volumes:
- hostPath:
path: /var/log
name: varlog
- hostPath:
path: /var/lib/docker/containers
name: varlibdockercontainers
- name: nfs-files-vol
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
labels:
app: guestbook
tier: frontend
name: kustomized-frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- image: gcr.io/google_samples/gb-frontend:v3
name: php-redis
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kustomized-web
spec:
replicas: 3
selector:
matchLabels:
app: nginx
serviceName: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
name: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- mountPath: /usr/share/nginx/html
name: www
terminationGracePeriodSeconds: 10
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes:
- ReadWriteMany
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
---
apiVersion: v1
kind: Pod
metadata:
labels:
app: myapp
name: kustomized-myapp-pod
spec:
containers:
- image: nginx:1.15.7-alpine
name: nginx
ports:
- containerPort: 80
name: http
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
volumes:
- name: nfs-files-vol
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
---
apiVersion: batch/v1
kind: Job
metadata:
name: kustomized-pi
spec:
backoffLimit: 4
template:
spec:
containers:
- command:
- perl
- -Mbignum=bpi
- -wle
- print bpi(2000)
image: perl
name: pi
volumeMounts:
- mountPath: /app/shared-files
name: nfs-files-vol
restartPolicy: Never
volumes:
- name: nfs-files-vol
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: kustomized-nfs-files-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 10Gi
nfs:
path: /
readOnly: false
server: kustomized-nfs-server-service.default.srv.cluster.local
`)
}

View File

@@ -5,7 +5,10 @@ package loader
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"path/filepath"
"strings"
@@ -86,6 +89,9 @@ type fileLoader struct {
// File system utilities.
fSys filesys.FileSystem
// Used to load from HTTP
http *http.Client
// Used to clone repositories.
cloner git.Cloner
@@ -293,6 +299,25 @@ func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
// else an error. Relative paths are taken relative
// to the root.
func (fl *fileLoader) Load(path string) ([]byte, error) {
if u, err := url.Parse(path); err == nil && (u.Scheme == "http" || u.Scheme == "https") {
var hc *http.Client
if fl.http != nil {
hc = fl.http
} else {
hc = &http.Client{}
}
resp, err := hc.Get(path)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
if !filepath.IsAbs(path) {
path = fl.root.Join(path)
}

View File

@@ -4,7 +4,9 @@
package loader
import (
"bytes"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
@@ -54,6 +56,7 @@ func makeLoader() *fileLoader {
return NewFileLoaderAtRoot(MakeFakeFs(testCases))
}
func TestLoaderLoad(t *testing.T) {
l1 := makeLoader()
if "/" != l1.Root() {
@@ -579,3 +582,93 @@ func TestRepoIndirectCycleDetection(t *testing.T) {
t.Fatalf("unexpected err: %v", err)
}
}
// Inspired by https://hassansin.github.io/Unit-Testing-http-client-in-Go
type fakeRoundTripper func(req *http.Request) *http.Response
func (f fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req), nil
}
func makeFakeHTTPClient(fn fakeRoundTripper) *http.Client {
return &http.Client{
Transport: fn,
}
}
// TestLoaderHTTP test http file loader
func TestLoaderHTTP(t *testing.T) {
var testCasesFile = []testData{
{
path: "http/file.yaml",
expectedContent: "file content",
},
}
l1 := NewFileLoaderAtRoot(MakeFakeFs(testCasesFile))
if "/" != l1.Root() {
t.Fatalf("incorrect root: '%s'\n", l1.Root())
}
for _, x := range testCasesFile {
b, err := l1.Load(x.path)
if err != nil {
t.Fatalf("unexpected load error: %v", err)
}
if !reflect.DeepEqual([]byte(x.expectedContent), b) {
t.Fatalf("in load expected %s, but got %s", x.expectedContent, b)
}
}
var testCasesHTTP = []testData{
{
path: "http://example.com/resource.yaml",
expectedContent: "http content",
},
{
path: "https://example.com/resource.yaml",
expectedContent: "https content",
},
}
for _, x := range testCasesHTTP {
hc := makeFakeHTTPClient(func(req *http.Request) *http.Response {
u := req.URL.String()
if x.path != u {
t.Fatalf("expected URL %s, but got %s", x.path, u)
}
return &http.Response{
StatusCode: 200,
Body: ioutil.NopCloser(bytes.NewBufferString(x.expectedContent)),
Header: make(http.Header),
}
})
l2 := l1
l2.http = hc
b, err := l2.Load(x.path)
if err != nil {
t.Fatalf("unexpected load error: %v", err)
}
if !reflect.DeepEqual([]byte(x.expectedContent), b) {
t.Fatalf("in load expected %s, but got %s", x.expectedContent, b)
}
}
var testCaseUnsupported = []testData{
{
path: "httpsnotreal://example.com/resource.yaml",
expectedContent: "invalid",
},
}
for _, x := range testCaseUnsupported {
hc := makeFakeHTTPClient(func(req *http.Request) *http.Response {
t.Fatalf("unexpected request to URL %s", req.URL.String())
return nil
})
l2 := l1
l2.http = hc
_, err := l2.Load(x.path)
if err == nil {
t.Fatalf("expect error but get %v", err)
}
}
}

View File

@@ -21,4 +21,7 @@ type GeneratorArgs struct {
// KvPairSources for the generator.
KvPairSources `json:",inline,omitempty" yaml:",inline,omitempty"`
// GeneratorOptions modify this generator
GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"`
}

View File

@@ -1,12 +1,18 @@
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.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/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -16,6 +22,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
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/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@@ -82,6 +89,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
@@ -91,6 +99,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
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.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
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=
@@ -100,6 +109,7 @@ github.com/posener/complete/v2 v2.0.1-alpha.12 h1:0wvkuDfHb5vSZlNBYgpEH4XQHpF46M
github.com/posener/complete/v2 v2.0.1-alpha.12/go.mod h1://JlL91cS2JV7rOl6LVHrRqBXoBUecJu3ILQPgbJiMQ=
github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w=
github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@@ -116,6 +126,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.2.3-0.20181224173747-660f15d67dbb/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=
@@ -123,10 +134,15 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
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.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
@@ -137,6 +153,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"os"
"strings"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
@@ -67,8 +68,15 @@ func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT"})
for i := range r.List.Setters {
s := r.List.Setters[i]
v := s.Value
// if the setter is for a list, populate the values
if len(s.ListValues) > 0 {
v = strings.Join(s.ListValues, ",")
v = fmt.Sprintf("[%s]", v)
}
table.Append([]string{
s.Name, s.Value, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count)})
s.Name, v, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count)})
}
table.Render()

View File

@@ -21,7 +21,7 @@ func NewSetRunner(parent string) *SetRunner {
r := &SetRunner{}
c := &cobra.Command{
Use: "set DIR NAME [VALUE]",
Args: cobra.RangeArgs(1, 3),
Args: cobra.MinimumNArgs(3),
Short: commands.SetShort,
Long: commands.SetLong,
Example: commands.SetExamples,
@@ -94,6 +94,11 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
var err error
r.Set.Name = args[1]
r.Set.Value = args[2]
// set remaining values as list values
if len(args) > 3 {
r.Set.ListValues = args[3:]
}
r.Set.Description = r.Perform.Description
r.Set.SetBy = r.Perform.SetBy
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)

View File

@@ -89,6 +89,14 @@ func (r *RunFnRunner) getFunctions(c *cobra.Command, args, dataItems []string) (
if err != nil {
return nil, err
}
if r.Network {
err = fn.PipeE(
yaml.LookupCreate(yaml.MappingNode, "container", "network"),
yaml.SetField("required", yaml.NewScalarRNode("true")))
if err != nil {
return nil, err
}
}
// create the function config
rc, err := yaml.Parse(`

View File

@@ -101,7 +101,7 @@ metadata:
name: function-input
annotations:
config.kubernetes.io/function: |
container: {image: 'foo:bar'}
container: {image: 'foo:bar', network: {required: true}}
data: {}
kind: ConfigMap
apiVersion: v1
@@ -118,7 +118,7 @@ metadata:
name: function-input
annotations:
config.kubernetes.io/function: |
container: {image: 'foo:bar'}
container: {image: 'foo:bar', network: {required: true}}
data: {}
kind: ConfigMap
apiVersion: v1

View File

@@ -18,7 +18,7 @@ cluster
Resources (including configmap and secret generators)
can _still be shared_ via the recommended best practice
of placing them in a directory with their own
kustomization file, and refering to this directory as a
kustomization file, and referring to this directory as a
[`base`](glossary.md#base) from any kustomization that
wants to use it. This encourages modularity and
relocatability.

View File

@@ -48,7 +48,7 @@ like adding labels or annotatations, get dedicated
transformer plugins - `LabelTransformer`,
`AnnotationsTransformer`, etc.
These accept relatively simple YAML configuration
allowing easy targetting of any number of resources.
allowing easy targeting of any number of resources.
Another class of edits take data from one specific
object's field and use it in another (e.g. a service

View File

@@ -131,6 +131,10 @@ crds:
Modifies behavior of all [ConfigMap](#configmapgenerator)
and [Secret](#secretgenerator) generators.
Additionally, generatorOptions can be set on a per resource level within each
generator. For details on per-resource generatorOptions usage see
[field-name-configMapGenerator] and See [field-name-secretGenerator].
```
generatorOptions:
# labels to add to all generated resources
@@ -202,7 +206,7 @@ See [field-name-replicas].
### resources
Each entry in this list must be a path to a
_file_, or a path (or URL) refering to another
_file_, or a path (or URL) referring to another
kustomization _directory_, e.g.
```

View File

@@ -273,9 +273,9 @@ more than one version).
_kustomize_ is a command line tool supporting
template-free, structured customization of declarative
configuration targetted to k8s-style objects.
configuration targeted to k8s-style objects.
_Targetted to k8s means_ that kustomize has some
_Targeted to k8s means_ that kustomize has some
understanding of API resources, k8s concepts like
names, labels, namespaces, etc. and the semantics of
resource patching.

View File

@@ -209,7 +209,7 @@ a YAML file containing its configuration (the file name
provided in the kustomization file).
> TODO: restrictions on plugin to allow the _same exec
> plugin_ to be targetted by both the
> plugin_ to be targeted by both the
> `generators` and `transformers` fields.
>
> - first arg could be the fixed string

View File

@@ -90,9 +90,9 @@ commonAnnotations:
Each entry in this list results in the creation of
one ConfigMap resource (it's a generator of n maps).
The example below creates two ConfigMaps. One with the
names and contents of the given files, the other with
key/value as data.
The example below creates three ConfigMaps. One with the names and contents of
the given files, one with key/value as data, and a third which sets an
annotation and label via generatorOptions for that single ConfigMap.
Each configMapGenerator item accepts a parameter of
`behavior: [create|replace|merge]`.
@@ -109,6 +109,14 @@ configMapGenerator:
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
- name: dashboards
files:
- mydashboard.json
generatorOptions:
annotations:
dashboard: "1"
labels:
app.kubernetes.io/name: "app1"
```
It is also possible to
@@ -651,6 +659,15 @@ secretGenerator:
envs:
- env.txt
type: Opaque
- name: secret-with-annotation
files:
- app-config.yaml
type: Opaque
generatorOptions:
annotations:
app_config: "true"
labels:
app.kubernetes.io/name: "app2"
```
### Usage via plugin

View File

@@ -78,7 +78,7 @@ a7a2589e Fix yaml in generator examples.
529db049 Introduce envs field.
6d309b52 Introduce stacked transformers.
abf538d8 Keep backward compatibility for image transformer
7e12918f Keep var refernce in resources
7e12918f Keep var references in resources
7130e3ff Leave defautconfig empty for images
3e85c458 Load default config for image transformer
4162dbc2 Maintain resources in order loaded.

View File

@@ -37,7 +37,7 @@ the _kustomize Go API_.
[import path]: https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher
In `kustomize/v3` (and preceeding major versions), the
In `kustomize/v3` (and preceding major versions), the
kustomize program and the API live the same Go
module at `sigs.k8s.io/kustomize`, at [import path]
`sigs.k8s.io/kustomize/v3`.

View File

@@ -4,7 +4,7 @@
# Demo: Inline Patch
A kustomization file supports patching in three ways:
- patchesStrategicMerge: A list of patch files where each file is parsed as a [Stragetic Merge Patch].
- patchesStrategicMerge: A list of patch files where each file is parsed as a [Strategic Merge Patch].
- patchesJSON6902: A list of patches and associated targetes, where each file is parsed as a [JSON Patch] and can only be applied to one target resource.
- patches: A list of patches and their associated targets. The patch can be applied to multiple objects. It auto detects whether the patch is a [Strategic Merge Patch] or [JSON Patch].

25
examples/loadHttp.md Normal file
View File

@@ -0,0 +1,25 @@
# load file from http
Resource and patch files could be loaded from http
<!-- @loadHttp -->
```sh
DEMO_HOME=$(mktemp -d)
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml
EOF
```
<!-- @loadHttp -->
```sh
test 1 == \
$(kustomize build $DEMO_HOME | grep "Good Morning!" | wc -l); \
echo $?
```
Kustomize will try loading resource as a file either from local or http. If it
fails, try to load it as a directory or git repository.
Http load applies to patches as well. See full example in [loadHttp](loadHttp/).

View File

@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/wordpress/wordpress/deployment.yaml
- https://github.com/knative/serving/releases/download/v0.12.0/serving.yaml # redirects to s3
patches:
- https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/wordpress/patch.yaml
patchesStrategicMerge:
- |-
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: custom-metrics-auth-reader
namespace: kube-system
$patch: delete

7
hack/go.mod Normal file
View File

@@ -0,0 +1,7 @@
module sigs.k8s.io/kustomize/hack
go 1.14
require github.com/golangci/golangci-lint v1.23.8
exclude github.com/golangci/golangci-lint v1.24.0

351
hack/go.sum Normal file
View File

@@ -0,0 +1,351 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
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/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bombsimon/wsl/v2 v2.0.0 h1:+Vjcn+/T5lSrO8Bjzhk4v14Un/2UyCA1E3V5j9nwTkQ=
github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/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 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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
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 v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.4.1 h1:4DTQfT1wWwLg/hzxwD9bkdhDQrdJtxe6DUTadPlrIeE=
github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b h1:ekuhfTjngPhisSjOJ0QWKpPQE8/rbknHaes6WVJj5Hw=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.23.8 h1:NlD+Ld2TKH8qVmADy4iEvPxVmXaqPIeQu3d1cGQP4jc=
github.com/golangci/golangci-lint v1.23.8/go.mod h1:g/38bxfhp4rI7zeWSxcdIeHTQGS58TCak8FYcyCmavQ=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/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.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
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/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
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 v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/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-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83 h1:AtnWoOvTioyDXFvu96MWEeE8qj4COSQnJogzLy/u41A=
github.com/securego/gosec v0.0.0-20200103095621-79fbf3af8d83/go.mod h1:vvbZ2Ae7AzSq3/kywjUDxSNq2SJ27RxCz2un0H3ePqE=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 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/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
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/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tommy-muehle/go-mnd v1.1.1 h1:4D0wuPKjOTiK2garzuPGGvm4zZ/wLYDOH8TJSABC7KU=
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs=
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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-20180911220305-26e67e76b6c3/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-20181220203305-927f97764cc3/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/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-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113232020-e2727e816f5a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200102140908-9497f49d5709/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204192400-7124308813f3 h1:Ms82wn6YK4ZycO6Bxyh0kxX3gFFVGo79CCuc52xgcys=
golang.org/x/tools v0.0.0-20200204192400-7124308813f3/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
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.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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -0,0 +1,56 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package comments
import (
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
)
// CopyComments recursively copies the comments on fields in from to fields in to
func CopyComments(from, to *yaml.RNode) error {
// walk the fields copying comments
_, err := walk.Walker{
Sources: []*yaml.RNode{from, to},
Visitor: &copier{},
VisitKeysAsScalars: true}.Walk()
return err
}
// copier implements walk.Visitor, and copies comments to fields shared between 2 instances
// of a resource
type copier struct{}
func (c *copier) VisitMap(s walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
copy(s.Dest(), s.Origin())
return s.Dest(), nil
}
func (c *copier) VisitScalar(s walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
copy(s.Dest(), s.Origin())
return s.Dest(), nil
}
func (c *copier) VisitList(s walk.Sources, _ *openapi.ResourceSchema, _ walk.ListKind) (
*yaml.RNode, error) {
copy(s.Dest(), s.Origin())
return s.Dest(), nil
}
// copy copies the comment from one field to another
func copy(from, to *yaml.RNode) {
if from == nil || to == nil {
return
}
if from.YNode().LineComment != "" {
to.YNode().LineComment = from.YNode().LineComment
}
if from.YNode().HeadComment != "" {
to.YNode().HeadComment = from.YNode().HeadComment
}
if from.YNode().FootComment != "" {
to.YNode().FootComment = from.YNode().FootComment
}
}

View File

@@ -140,11 +140,11 @@ func (it FieldValueType) Validate(value string) error {
func (it FieldValueType) Tag() string {
switch it {
case String:
return "!!str"
return yaml.StringTag
case Bool:
return "!!bool"
return yaml.BoolTag
case Int:
return "!!int"
return yaml.IntTag
}
return ""
}
@@ -152,17 +152,17 @@ func (it FieldValueType) Tag() string {
func (it FieldValueType) TagForValue(value string) string {
switch it {
case String:
return "!!str"
return yaml.StringTag
case Bool:
if _, err := strconv.ParseBool(string(it)); err != nil {
return ""
}
return "!!bool"
return yaml.BoolTag
case Int:
if _, err := strconv.ParseInt(string(it), 0, 32); err != nil {
return ""
}
return "!!int"
return yaml.IntTag
}
return ""
}

View File

@@ -6,9 +6,11 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/go-errors/errors v1.0.1
github.com/go-openapi/spec v0.19.5
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d
github.com/sergi/go-diff v1.1.0
github.com/stretchr/testify v1.4.0
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 // indirect
gopkg.in/yaml.v2 v2.2.7
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2

View File

@@ -1,10 +1,21 @@
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
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-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/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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/dustmop/soup v1.1.2-0.20190516214245-38228baa104e h1:44fmjqDtdCiUNlSjJVp+w1AOs6na3Y6Ai0aIeseFjkI=
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
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-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
@@ -25,23 +36,37 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/paulmach/orb v0.1.3 h1:Wa1nzU269Zv7V9paVEY1COWW8FCqv4PC/KJRbJSimpM=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
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/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/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/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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=

View File

@@ -210,6 +210,10 @@ func (rs *ResourceSchema) Elements() *ResourceSchema {
// either not an array, or array has multiple types
return nil
}
if rs == nil || rs.Schema == nil || rs.Schema.Items == nil {
// no-scheme for the items
return nil
}
s := *rs.Schema.Items.Schema
for s.Ref.String() != "" {
sc, e := Resolve(&s.Ref)

View File

@@ -43,9 +43,14 @@ func (a *Add) Filter(object *yaml.RNode) (*yaml.RNode, error) {
return object, accept(a, object)
}
func (a *Add) visitSequence(_ *yaml.RNode, _ string, _ *openapi.ResourceSchema) error {
// no-op
return nil
}
// visitScalar implements visitor
// visitScalar will set the field metadata on each scalar field whose name + value match
func (a *Add) visitScalar(object *yaml.RNode, p string) error {
func (a *Add) visitScalar(object *yaml.RNode, p string, _ *openapi.ResourceSchema) error {
// check if the field matches
if a.FieldName != "" && !strings.HasSuffix(p, a.FieldName) {
return nil
@@ -96,6 +101,9 @@ type SetterDefinition struct {
// Value is the value of the setter.
Value string `yaml:"value"`
// ListValues are the value of a list setter.
ListValues []string `yaml:"listValues,omitempty"`
// SetBy is the person or role that last set the value.
SetBy string `yaml:"setBy,omitempty"`

View File

@@ -8,7 +8,10 @@ import (
"github.com/go-openapi/spec"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -27,10 +30,36 @@ func (s *Set) Filter(object *yaml.RNode) (*yaml.RNode, error) {
return object, accept(s, object)
}
// visitSequence will perform setters for sequences
func (s *Set) visitSequence(object *yaml.RNode, p string, schema *openapi.ResourceSchema) error {
ext, err := getExtFromComment(schema)
if err != nil {
return err
}
if ext == nil || ext.Setter == nil || ext.Setter.Name != s.Name ||
len(ext.Setter.ListValues) == 0 {
// setter was not invoked for this sequence
return nil
}
s.Count++
// set the values on the sequences
var elements []*yaml.Node
for i := range ext.Setter.ListValues {
v := ext.Setter.ListValues[i]
n := yaml.NewScalarRNode(v).YNode()
n.Style = yaml.DoubleQuotedStyle
elements = append(elements, n)
}
object.YNode().Content = elements
object.YNode().Style = yaml.FoldedStyle
return nil
}
// visitScalar
func (s *Set) visitScalar(object *yaml.RNode, p string) error {
func (s *Set) visitScalar(object *yaml.RNode, p string, schema *openapi.ResourceSchema) error {
// get the openAPI for this field describing how to apply the setter
ext, sch, err := getExtFromComment(object)
ext, err := getExtFromComment(schema)
if err != nil {
return err
}
@@ -39,13 +68,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, sch) {
if s.set(object, ext, schema.Schema) {
s.Count++
return nil
}
// perform a substitution of the field if it matches
sub, err := s.substitute(object, ext, sch)
sub, err := s.substitute(object, ext, schema.Schema)
if err != nil {
return err
}
@@ -111,7 +140,7 @@ func (s *Set) substitute(field *yaml.RNode, ext *cliExtension, _ *spec.Schema) (
field.YNode().Value = p
// substitutions are always strings
field.YNode().Tag = "!!str"
field.YNode().Tag = yaml.StringTag
return true, nil
}
@@ -147,6 +176,9 @@ type SetOpenAPI struct {
// Value is the current value of the setter
Value string `yaml:"value"`
// ListValue is the current value for a list of items
ListValues []string `yaml:"listValue"`
Description string `yaml:"description"`
SetBy string `yaml:"setBy"`
@@ -159,8 +191,14 @@ func (s SetOpenAPI) UpdateFile(path string) error {
func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
key := SetterDefinitionPrefix + s.Name
def, err := object.Pipe(yaml.Lookup(
"openAPI", "definitions", key, "x-k8s-cli", "setter"))
oa, err := object.Pipe(yaml.Lookup("openAPI", "definitions", key))
if err != nil {
return nil, err
}
if oa == nil {
return nil, errors.Errorf("no setter %s found", s.Name)
}
def, err := oa.Pipe(yaml.Lookup("x-k8s-cli", "setter"))
if err != nil {
return nil, err
}
@@ -168,9 +206,16 @@ func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
return nil, errors.Errorf("no setter %s found", s.Name)
}
// record the OpenAPI type for the setter
var t string
if n := oa.Field("type"); n != nil {
t = n.Value.YNode().Value
}
// 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 {
if values, err := def.Pipe(
yaml.Lookup("enumValues")); err != nil {
// error looking up the enumValues
return nil, err
} else if values != nil {
@@ -202,11 +247,40 @@ func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
// 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().Tag = yaml.StringTag
v.YNode().Style = yaml.DoubleQuotedStyle
if err := def.PipeE(&yaml.FieldSetter{Name: "value", Value: v}); err != nil {
return nil, err
if t != "array" {
// set a scalar value
if err := def.PipeE(&yaml.FieldSetter{Name: "value", Value: v}); err != nil {
return nil, err
}
} else {
// set a list value
if err := def.PipeE(&yaml.FieldClearer{Name: "value"}); err != nil {
return nil, err
}
// create the list values
var elements []*yaml.Node
n := yaml.NewScalarRNode(s.Value).YNode()
n.Tag = yaml.StringTag
n.Style = yaml.DoubleQuotedStyle
elements = append(elements, n)
for i := range s.ListValues {
v := s.ListValues[i]
n := yaml.NewScalarRNode(v).YNode()
n.Style = yaml.DoubleQuotedStyle
elements = append(elements, n)
}
l := yaml.NewRNode(&yaml.Node{
Kind: yaml.SequenceNode,
Content: elements,
})
def.YNode().Style = yaml.FoldedStyle
if err := def.PipeE(&yaml.FieldSetter{Name: "listValues", Value: l}); err != nil {
return nil, err
}
}
if err := def.PipeE(&yaml.FieldSetter{Name: "setBy", StringValue: s.SetBy}); err != nil {
@@ -226,3 +300,39 @@ func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
return object, nil
}
// SetAll applies the set filter for all yaml nodes and only returns the nodes whose
// corresponding file has at least one node with input setter
func SetAll(s *Set) kio.Filter {
return kio.FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
filesToUpdate := sets.String{}
// for each node record the set fields count before and after filter is applied and
// store the corresponding file paths if there is an increment in setters count
for i := range nodes {
preCount := s.Count
_, err := s.Filter(nodes[i])
if err != nil {
return nil, errors.Wrap(err)
}
if s.Count > preCount {
path, _, err := kioutil.GetFileAnnotations(nodes[i])
if err != nil {
return nil, errors.Wrap(err)
}
filesToUpdate.Insert(path)
}
}
var nodesInUpdatedFiles []*yaml.RNode
// return only the nodes whose corresponding file has at least one node with input setter
for i := range nodes {
path, _, err := kioutil.GetFileAnnotations(nodes[i])
if err != nil {
return nil, errors.Wrap(err)
}
if filesToUpdate.Has(path) {
nodesInUpdatedFiles = append(nodesInUpdatedFiles, nodes[i])
}
}
return nodesInUpdatedFiles, nil
})
}

View File

@@ -595,6 +595,76 @@ spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
{
name: "set-args-list",
setter: "args",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.args:
x-k8s-cli:
type: array
setter:
name: args
listValues: ["1", "2", "3"]
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
# {"$ref": "#/definitions/io.k8s.cli.setters.args"}
replicas: []
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
# {"$ref": "#/definitions/io.k8s.cli.setters.args"}
replicas:
- "1"
- "2"
- "3"
`,
},
{
name: "set-args-list-replace",
setter: "args",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.args:
x-k8s-cli:
type: array
setter:
name: args
listValues: ["1", "2", "3"]
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
# {"$ref": "#/definitions/io.k8s.cli.setters.args"}
replicas: ["4", "5"]
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
# {"$ref": "#/definitions/io.k8s.cli.setters.args"}
replicas:
- "1"
- "2"
- "3"
`,
},
}
@@ -632,6 +702,160 @@ spec:
}
}
func TestSet_SetAll(t *testing.T) {
var tests = []struct {
name string
description string
setter string
openapi string
input []string
expected []string
}{
{
name: "set-replicas-same-file",
setter: "replicas",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: replicas
value: "4"
`,
input: []string{`
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'cluster.yaml'
spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment2
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'cluster.yaml'
spec:
replicas: 10
`},
expected: []string{`
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'cluster.yaml'
spec:
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment2
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'cluster.yaml'
spec:
replicas: 10
`},
},
{
name: "set-replicas-different-file",
setter: "replicas",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: replicas
value: "4"
`,
input: []string{`
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'cluster.yaml'
spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment2
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'another_cluster.yaml'
spec:
replicas: 10
`},
expected: []string{`
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'cluster.yaml'
spec:
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`},
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
defer openapi.ResetOpenAPI()
initSchema(t, test.openapi)
// parse the input to be modified
var inputNodes []*yaml.RNode
for _, s := range test.input {
r, err := yaml.Parse(s)
if !assert.NoError(t, err) {
t.FailNow()
}
inputNodes = append(inputNodes, r)
}
// invoke the setter
instance := &Set{Name: test.setter}
result, err := SetAll(instance).Filter(inputNodes)
if !assert.NoError(t, err) {
t.FailNow()
}
// compare the actual and expected output
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, len(result), len(test.expected)) {
t.FailNow()
}
for i := range result {
actual, _ := result[i].String()
actual = strings.TrimSpace(actual)
expected := strings.TrimSpace(test.expected[i])
if !assert.Equal(t, expected, actual) {
t.FailNow()
}
}
})
}
}
// initSchema initializes the openAPI with the definitions from s
func initSchema(t *testing.T, s string) {
// parse out the schema from the input openAPI
@@ -678,6 +902,7 @@ func TestSetOpenAPI_Filter(t *testing.T) {
name string
setter string
value string
values []string
input string
expected string
description string
@@ -1004,6 +1229,33 @@ openAPI:
value: "2"
`,
},
{
name: "set-args-list",
setter: "args",
value: "2",
values: []string{"3", "4"},
input: `
openAPI:
definitions:
io.k8s.cli.setters.args:
type: array
x-k8s-cli:
setter:
name: args
listValues: ["1"]
`,
expected: `
openAPI:
definitions:
io.k8s.cli.setters.args:
type: array
x-k8s-cli:
setter:
name: args
listValues: ["2", "3", "4"]
`,
},
}
for i := range tests {
test := tests[i]
@@ -1015,7 +1267,7 @@ openAPI:
// invoke the setter
instance := &SetOpenAPI{
Name: test.setter, Value: test.value,
Name: test.setter, Value: test.value, ListValues: test.values,
SetBy: test.setBy, Description: test.description}
result, err := instance.Filter(in)
if test.err != "" {

View File

@@ -7,6 +7,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// FieldSetter sets the value for a field setter.
@@ -17,16 +18,35 @@ type FieldSetter struct {
// Value is the value to set
Value string
// ListValues contains a list of values to set on a Sequence
ListValues []string
Description string
SetBy string
Count int
OpenAPIPath string
ResourcesPath string
}
func (fs *FieldSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
fs.Count, _ = fs.Set(fs.OpenAPIPath, fs.ResourcesPath)
return nil, nil
}
// Set updates the OpenAPI definitions and resources with the new setter value
func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
// Update the OpenAPI definitions
soa := setters2.SetOpenAPI{
Name: fs.Name, Value: fs.Value, Description: fs.Description, SetBy: fs.SetBy}
Name: fs.Name,
Value: fs.Value,
ListValues: fs.ListValues,
Description: fs.Description,
SetBy: fs.SetBy,
}
if err := soa.UpdateFile(openAPIPath); err != nil {
return 0, err
}
@@ -37,11 +57,13 @@ func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
}
// Update the resources with the new value
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
// Set NoDeleteFiles to true as SetAll will return only the nodes of files which should be updated and
// hence, rest of the files should not be deleted
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath, NoDeleteFiles: true}
s := &setters2.Set{Name: fs.Name}
err := kio.Pipeline{
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(s)},
Filters: []kio.Filter{setters2.SetAll(s)},
Outputs: []kio.Writer{inout},
}.Execute()
return s.Count, err

View File

@@ -8,9 +8,7 @@ import (
"github.com/go-openapi/spec"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type cliExtension struct {
@@ -21,6 +19,7 @@ type cliExtension struct {
type setter struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Value string `yaml:"value,omitempty" json:"value,omitempty"`
ListValues []string `yaml:"listValues,omitempty" json:"listValues,omitempty"`
EnumValues map[string]string `yaml:"enumValues,omitempty" json:"enumValues,omitempty"`
}
@@ -57,32 +56,17 @@ 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, *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, nil, errors.Wrap(err)
}
if fm.Schema.Ref.String() == "" {
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, nil, errors.Wrap(err)
}
if r == nil {
func getExtFromComment(schema *openapi.ResourceSchema) (*cliExtension, error) {
if schema == nil {
// no schema found
// TODO(pwittrock): should this be an error if it doesn't resolve?
return nil, nil, nil
return nil, nil
}
// get the cli extension from the openapi (contains setter information)
ext, err := getExtFromSchema(r)
ext, err := getExtFromSchema(schema.Schema)
if err != nil {
return nil, nil, errors.Wrap(err)
return nil, errors.Wrap(err)
}
return ext, r, nil
return ext, nil
}

View File

@@ -4,6 +4,8 @@
package setters2
import (
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -13,33 +15,100 @@ type visitor interface {
// visitScalar is called for each scalar field value on a resource
// node is the scalar field value
// path is the path to the field; path elements are separated by '.'
visitScalar(node *yaml.RNode, path string) error
// oa is the OpenAPI schema for the field
visitScalar(node *yaml.RNode, path string, oa *openapi.ResourceSchema) error
// visitSequence is called for each sequence field value on a resource
// node is the sequence field value
// path is the path to the field
// oa is the OpenAPI schema for the field
visitSequence(node *yaml.RNode, path string, oa *openapi.ResourceSchema) error
}
// accept invokes the appropriate function on v for each field in object
func accept(v visitor, object *yaml.RNode) error {
return acceptImpl(v, object, "")
// get the OpenAPI for the type if it exists
oa := getSchema(object, nil, "")
return acceptImpl(v, object, "", oa)
}
// acceptImpl implements accept using recursion
func acceptImpl(v visitor, object *yaml.RNode, p string) error {
func acceptImpl(v visitor, object *yaml.RNode, p string, oa *openapi.ResourceSchema) error {
switch object.YNode().Kind {
case yaml.DocumentNode:
// Traverse the child of the document
return accept(v, yaml.NewRNode(object.YNode()))
case yaml.MappingNode:
return object.VisitFields(func(node *yaml.MapNode) error {
// get the schema for the field and propagate it
oa = getSchema(node.Key, oa, node.Key.YNode().Value)
// Traverse each field value
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value)
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value, oa)
})
case yaml.SequenceNode:
// get the schema for the sequence node, use the schema provided if not present
// on the field
if err := v.visitSequence(object, p, oa); err != nil {
return err
}
// get the schema for the elements
oa = getSchema(object, oa, "")
return object.VisitElements(func(node *yaml.RNode) error {
// Traverse each list element
return acceptImpl(v, node, p)
return acceptImpl(v, node, p, oa)
})
case yaml.ScalarNode:
// Visit the scalar field
return v.visitScalar(object, p)
oa = getSchema(object, oa, "")
return v.visitScalar(object, p, oa)
}
return nil
}
// getSchema returns OpenAPI schema for an RNode or field of the
// RNode. It will overriding the provide schema with field specific values
// if they are found
// r is the Node to get the Schema for
// s is the provided schema for the field if known
// field is the name of the field
func getSchema(r *yaml.RNode, s *openapi.ResourceSchema, field string) *openapi.ResourceSchema {
// get the override schema if it exists on the field
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}
}
// get the schema for a field of the node if the field is provided
if s != nil && field != "" {
return s.Field(field)
}
// get the schema for the elements if this is a list
if s != nil && r.YNode().Kind == yaml.SequenceNode {
return s.Elements()
}
// use the provided schema if present
if s != nil {
return s
}
if yaml.IsEmpty(r) {
return nil
}
// lookup the schema for the type
m, _ := r.GetMeta()
if m.Kind == "" || m.APIVersion == "" {
return nil
}
return openapi.SchemaForResourceType(yaml.TypeMeta{Kind: m.Kind, APIVersion: m.APIVersion})
}

36
kyaml/starlark/doc.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package starlark contains a kio.Filter which can be applied to resources to transform
// them through starlark program.
//
// Starlark has become a popular runtime embedding in go programs, especially for Kubernetes
// and data processing.
// Examples: https://github.com/cruise-automation/isopod, https://qri.io/docs/starlark/starlib,
// https://github.com/stripe/skycfg, https://github.com/k14s/ytt
//
// The resources are provided to the starlark program through the global variable "resourceList".
// "resourceList" is a dictionary containing an "items" field with a list of resources.
// The starlark modified "resourceList" is the Filter output.
//
// After being run through the starlark program, the filter will copy the comments from the input
// resources to restore them -- due to them being dropped as a result of serializing the resources
// as starlark values.
//
// "resourceList" may also contain a "functionConfig" entry to configure the starlark script itself.
// Changes made by the starlark program to the "functionConfig" will be reflected in the
// Filter.FunctionConfig value.
//
// The Filter will also format the output so that output has the preferred field ordering
// rather than an alphabetical field ordering.
//
// The resourceList variable adheres to the kustomize function spec as specified by:
// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
//
// All items in the resourceList are resources represented as starlark dictionaries/
// The items in the resourceList respect the io spec specified by:
// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/config-io.md
//
// The starlark language spec can be found here:
// https://github.com/google/starlark-go/blob/master/doc/spec.md
package starlark

View File

@@ -0,0 +1,290 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package starlark_test
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/starlark"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func ExampleFilter_Filter() {
// input contains the items that will provided to the starlark program
input := bytes.NewBufferString(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-1
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-1"}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-2"}
`)
// fltr transforms the input using a starlark program
fltr := &starlark.Filter{
Name: "annotate",
Program: `
def run(items):
for item in items:
item["metadata"]["annotations"]["foo"] = "bar"
run(resourceList["items"])
`,
}
// output contains the transformed resources
output := &bytes.Buffer{}
// run the fltr against the inputs using a kio.Pipeline
err := kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: input}},
Filters: []kio.Filter{fltr},
Outputs: []kio.Writer{&kio.ByteWriter{Writer: output}}}.Execute()
if err != nil {
log.Fatal(err)
}
fmt.Println(output.String())
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: deployment-1
// annotations:
// foo: bar
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-1"}
//---
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: deployment-2
// annotations:
// foo: bar
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-2"}
}
func ExampleFilter_Filter_functionConfig() {
// input contains the items that will provided to the starlark program
input := bytes.NewBufferString(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-1
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-1"}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-2"}
`)
fc, err := yaml.Parse(`
kind: AnnotationSetter
spec:
value: "hello world"
`)
if err != nil {
log.Fatal(err)
}
// fltr transforms the input using a starlark program
fltr := &starlark.Filter{
Name: "annotate",
Program: `
def run(items, value):
for item in items:
item["metadata"]["annotations"]["foo"] = value
run(resourceList["items"], resourceList["functionConfig"]["spec"]["value"])
`,
FunctionConfig: fc,
}
// output contains the transformed resources
output := &bytes.Buffer{}
// run the fltr against the inputs using a kio.Pipeline
err = kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: input}},
Filters: []kio.Filter{fltr},
Outputs: []kio.Writer{&kio.ByteWriter{Writer: output}}}.Execute()
if err != nil {
log.Fatal(err)
}
fmt.Println(output.String())
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: deployment-1
// annotations:
// foo: hello world
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-1"}
//---
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: deployment-2
// annotations:
// foo: hello world
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-2"}
}
// ExampleFilter_Filter_file applies a starlark program in a local file to a collection of
// resource configuration read from a directory.
func ExampleFilter_Filter_file() {
// setup the configuration
d, err := ioutil.TempDir("", "")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "deploy1.yaml"), []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-1
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-1"}
`), 0600)
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile(filepath.Join(d, "deploy2.yaml"), []byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-2"}
`), 0600)
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile(filepath.Join(d, "annotate.star"), []byte(`
def run(items):
for item in items:
item["metadata"]["annotations"]["foo"] = "bar"
run(resourceList["items"])
`), 0600)
if err != nil {
log.Fatal(err)
}
fltr := &starlark.Filter{
Name: "annotate",
Path: filepath.Join(d, "annotate.star"),
}
// output contains the transformed resources
output := &bytes.Buffer{}
// run the fltr against the inputs using a kio.Pipeline
err = kio.Pipeline{
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: d}},
Filters: []kio.Filter{fltr},
Outputs: []kio.Writer{&kio.ByteWriter{
Writer: output,
ClearAnnotations: []string{"config.kubernetes.io/path"},
}}}.Execute()
if err != nil {
log.Fatal(err)
}
fmt.Println(output.String())
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: deployment-1
// annotations:
// foo: bar
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-1"}
//---
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: deployment-2
// annotations:
// foo: bar
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-2"}
}

251
kyaml/starlark/starlark.go Normal file
View File

@@ -0,0 +1,251 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package starlark
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"github.com/qri-io/starlib/util"
"go.starlark.net/starlark"
"sigs.k8s.io/kustomize/kyaml/comments"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Filter transforms a set of resources through the provided program
type Filter struct {
Name string
// Program is a starlark script which will be run against the resources
Program string
// URL is the url of a starlark program to fetch and run
URL string
// Path is the path to a starlark program to read and run
Path string
// FunctionConfig is the value to be provided for resourceList.functionConfig as specified by
// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md.
FunctionConfig *yaml.RNode
}
func (sf *Filter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
if sf.URL != "" && sf.Path != "" ||
sf.URL != "" && sf.Program != "" ||
sf.Path != "" && sf.Program != "" {
return nil, errors.Errorf("Filter Path, Program and URL are mutually exclusive")
}
// read the program from a file
if sf.Path != "" {
b, err := ioutil.ReadFile(sf.Path)
if err != nil {
return nil, err
}
sf.Program = string(b)
}
// read the program from a URL
if sf.URL != "" {
err := func() error {
resp, err := http.Get(sf.URL)
if err != nil {
return err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
sf.Program = string(b)
return nil
}()
if err != nil {
return nil, err
}
}
// retain map of inputs to outputs by id so if the name is changed by the
// starlark program, we are able to match the same resources
value, ids, err := sf.inputToResourceList(input)
if err != nil {
return nil, errors.Wrap(err)
}
// run the starlark as program as transformation function
thread := &starlark.Thread{Name: sf.Name}
predeclared := starlark.StringDict{"resourceList": value}
_, err = starlark.ExecFile(thread, sf.Name, sf.Program, predeclared)
if err != nil {
return nil, errors.Wrap(err)
}
results, err := sf.resourceListToOutput(value, ids)
if err != nil {
return nil, errors.Wrap(err)
}
// starlark will serialize the resources sorting the fields alphabetically,
// format them to have a better ordering
return filters.FormatFilter{}.Filter(results)
}
// tuple maps an input resource to the output resource
type tuple struct {
// in is the RNode provided to the starlark program
in *yaml.RNode
// out is the RNode emitted by the starlark program with the id matching in
out *yaml.RNode
}
// inputToResourceList transforms input into a starlark.Value
func (sf *Filter) inputToResourceList(
input []*yaml.RNode) (starlark.Value, map[int]*tuple, error) {
var id int
ids := map[int]*tuple{}
// convert into a ResourceList which will be converted to a starlark dictionary
// create the ResourceList
resourceList, err := yaml.Parse(`kind: ResourceList`)
if err != nil {
return nil, nil, errors.Wrap(err)
}
// set the functionConfig
if sf.FunctionConfig != nil {
if err := resourceList.PipeE(
yaml.FieldSetter{Name: "functionConfig", Value: sf.FunctionConfig}); err != nil {
return nil, nil, err
}
}
// the inputs should be provided as the list "items"
items, err := resourceList.Pipe(yaml.LookupCreate(yaml.SequenceNode, "items"))
if err != nil {
return nil, nil, errors.Wrap(err)
}
// add the input as items, give each resource an id
for i := range input {
item := input[i]
// create an id for tracking the resource through the program
err := item.PipeE(yaml.SetAnnotation("config.k8s.io/id", fmt.Sprintf("%d", id)))
if err != nil {
return nil, nil, errors.Wrap(err)
}
ids[id] = &tuple{in: item}
id++
items.YNode().Content = append(items.YNode().Content, item.YNode())
}
// convert the ResourceList into a starlark dictionary by
// first converting it into a map[string]interface{}
s, err := resourceList.String()
if err != nil {
return nil, nil, errors.Wrap(err)
}
var in map[string]interface{}
if err := yaml.Unmarshal([]byte(s), &in); err != nil {
return nil, nil, errors.Wrap(err)
}
value, err := util.Marshal(in)
if err != nil {
return nil, nil, errors.Wrap(err)
}
return value, ids, err
}
// resourceListToOutput converts the output of the starlark program to the filter output
func (sf *Filter) resourceListToOutput(
value starlark.Value, ids map[int]*tuple) ([]*yaml.RNode, error) {
// convert the modified resourceList back into a slice of RNodes
// by first converting to a map[string]interface{}
out, err := util.Unmarshal(value)
if err != nil {
return nil, errors.Wrap(err)
}
o := out.(map[string]interface{})
// parse the function config
if _, found := o["functionConfig"]; found {
fc := (o["functionConfig"].(map[string]interface{}))
b, err := yaml.Marshal(fc)
if err != nil {
return nil, errors.Wrap(err)
}
sf.FunctionConfig, err = yaml.Parse(string(b))
if err != nil {
return nil, errors.Wrap(err)
}
}
// parse the items
// copy the items out of the ResourceList, and into the Filter output
var results []*yaml.RNode
it := (o["items"].([]interface{}))
for i := range it {
// convert the resource back to the native yaml form
b, err := yaml.Marshal(it[i])
if err != nil {
return nil, errors.Wrap(err)
}
node, err := yaml.Parse(string(b))
if err != nil {
return nil, errors.Wrap(err)
}
// match it to an input
idS, err := node.Pipe(yaml.GetAnnotation("config.k8s.io/id"))
if err != nil {
return nil, errors.Wrap(err)
}
if idS == nil {
// no matching input -- new resource
results = append(results, node)
continue
}
id, err := strconv.Atoi(idS.YNode().Value)
if err != nil {
return nil, errors.Wrap(err)
}
if match, found := ids[id]; found {
// matching resources
match.out = node
} else {
// no matching input with the same id -- new resource
// this may be an error case, the outputs probably shouldn't have ids
// assigned by the starlark program
results = append(results, node)
}
}
// retain the comments instead of dropping them by copying them from the original inputs
for i := 0; i < len(ids); i++ {
v := ids[i]
if v.out == nil {
continue
}
if err := comments.CopyComments(v.in, v.out); err != nil {
return nil, errors.Wrap(err)
}
results = append(results, v.out)
}
// delete the ids from resources, these were only to track through the starlark program
// and that is finished.
for i := range results {
err := results[i].PipeE(yaml.ClearAnnotation("config.k8s.io/id"))
if err != nil {
return nil, err
}
}
return results, nil
}

View File

@@ -0,0 +1,389 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package starlark
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
"github.com/go-errors/errors"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestFilter_Filter(t *testing.T) {
var tests = []struct {
name string
input string
functionConfig string
script string
expected string
expectedFunctionConfig string
}{
{
name: "add_annotation",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
script: `
# set the foo annotation on each resource
def run(r):
for resource in r:
resource["metadata"]["annotations"]["foo"] = "bar"
run(resourceList["items"])
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: bar
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
{
name: "update_annotation",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: baz
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
script: `
# set the foo annotation on each resource
def run(r):
for resource in r:
resource["metadata"]["annotations"]["foo"] = "bar"
run(resourceList["items"])
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: bar
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
{
name: "update_annotation_multiple",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1
annotations:
foo: baz
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-2
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
script: `
# set the foo annotation on each resource
def run(r):
for resource in r:
resource["metadata"]["annotations"]["foo"] = "bar"
run(resourceList["items"])
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1
annotations:
foo: bar
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-2
annotations:
foo: bar
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}`,
},
{
name: "add_resource",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
script: `
def run(r):
d = {
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "nginx-deployment-2",
},
}
r.append(d)
run(resourceList["items"])
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
{
name: "remove_resource",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-2
`,
script: `
def run(r):
r.pop()
run(resourceList["items"])
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-1
`,
},
{
name: "functionConfig",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
functionConfig: `
kind: Script
spec:
value: "hello world"
`,
script: `
# set the foo annotation on each resource
def run(r, an):
for resource in r:
resource["metadata"]["annotations"]["foo"] = an
an = resourceList["functionConfig"]["spec"]["value"]
run(resourceList["items"], an)
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: hello world
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
expectedFunctionConfig: `
kind: Script
spec:
value: hello world
`,
},
{
name: "functionConfig_update_functionConfig",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
functionConfig: `
kind: Script
spec:
value: "hello world"
`,
script: `
# set the foo annotation on each resource
def run(r, an):
for resource in r:
resource["metadata"]["annotations"]["foo"] = an
an = resourceList["functionConfig"]["spec"]["value"]
run(resourceList["items"], an)
resourceList["functionConfig"]["spec"]["value"] = "updated"
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: hello world
spec:
template:
spec:
containers:
- name: nginx
# head comment
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
expectedFunctionConfig: `
kind: Script
spec:
value: updated
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
f := &Filter{Name: test.name, Program: test.script}
if test.functionConfig != "" {
fc, err := yaml.Parse(test.functionConfig)
if !assert.NoError(t, err) {
t.FailNow()
}
f.FunctionConfig = fc
}
r := &kio.ByteReader{Reader: bytes.NewBufferString(test.input)}
o := &bytes.Buffer{}
w := &kio.ByteWriter{Writer: o}
p := kio.Pipeline{
Inputs: []kio.Reader{r},
Filters: []kio.Filter{f},
Outputs: []kio.Writer{w},
}
err := p.Execute()
if !assert.NoError(t, err) {
if e, ok := err.(*errors.Error); ok {
fmt.Fprintf(os.Stderr, "%s\n", e.Stack())
}
t.FailNow()
}
if !assert.Equal(t, strings.TrimSpace(test.expected), strings.TrimSpace(o.String())) {
t.FailNow()
}
if test.expectedFunctionConfig != "" {
if !assert.Equal(t,
strings.TrimSpace(test.expectedFunctionConfig),
strings.TrimSpace(f.FunctionConfig.MustString())) {
t.FailNow()
}
}
})
}
}

View File

@@ -14,9 +14,9 @@ import (
// typeToTag maps OpenAPI schema types to yaml 1.2 tags
var typeToTag = map[string]string{
"string": "!!str",
"integer": "!!int",
"boolean": "!!bool",
"string": StringTag,
"integer": IntTag,
"boolean": BoolTag,
"number": "!!float",
}

View File

@@ -3,7 +3,9 @@
package yaml
import "gopkg.in/yaml.v3"
import (
"gopkg.in/yaml.v3"
)
// AnnotationClearer removes an annotation at metadata.annotations.
// Returns nil if the annotation or field does not exist.
@@ -33,7 +35,7 @@ type AnnotationSetter struct {
func (s AnnotationSetter) Filter(rn *RNode) (*RNode, error) {
// some tools get confused about the type if annotations are not quoted
v := NewScalarRNode(s.Value)
v.YNode().Tag = "!!str"
v.YNode().Tag = StringTag
v.YNode().Style = yaml.SingleQuotedStyle
return rn.Pipe(
PathGetter{Path: []string{"metadata", "annotations"}, Create: yaml.MappingNode},
@@ -80,7 +82,7 @@ type LabelSetter struct {
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().Tag = StringTag
v.YNode().Style = yaml.SingleQuotedStyle
return rn.Pipe(
PathGetter{Path: []string{"metadata", "labels"}, Create: yaml.MappingNode},

View File

@@ -588,6 +588,12 @@ func (rn *RNode) VisitFields(fn func(node *MapNode) error) error {
return nil
}
const (
StringTag = "!!str"
BoolTag = "!!bool"
IntTag = "!!int"
)
// Elements returns the list of elements in the RNode.
// Returns an error for non-SequenceNodes.
func (rn *RNode) Elements() ([]*RNode, error) {

View File

@@ -40,6 +40,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
}
for _, value := range values {
val, err := Walker{
VisitKeysAsScalars: l.VisitKeysAsScalars,
InferAssociativeLists: l.InferAssociativeLists,
Visitor: l,
Schema: s,

View File

@@ -27,6 +27,32 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
// recursively set the field values on the map
for _, key := range l.fieldNames() {
if l.VisitKeysAsScalars {
// visit the map keys as if they were scalars,
// this is necessary if doing things such as copying
// comments
var keys []*yaml.RNode
for i := range l.Sources {
// construct the sources from the keys
if l.Sources[i] == nil {
keys = append(keys, nil)
continue
}
field := l.Sources[i].Field(key)
if field == nil || yaml.IsEmpty(field.Key) {
keys = append(keys, nil)
continue
}
keys = append(keys, field.Key)
}
// visit the sources as a scalar
// keys don't have any schema --pass in nil
_, err := l.Visitor.VisitScalar(keys, nil)
if err != nil {
return nil, err
}
}
var s *openapi.ResourceSchema
if l.Schema != nil {
s = l.Schema.Field(key)
@@ -36,6 +62,7 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
s = commentSch
}
val, err := Walker{
VisitKeysAsScalars: l.VisitKeysAsScalars,
InferAssociativeLists: l.InferAssociativeLists,
Visitor: l,
Schema: s,

View File

@@ -32,6 +32,10 @@ type Walker struct {
// fields which it doesn't have the schema based on the fields in the
// list elements.
InferAssociativeLists bool
// VisitKeysAsScalars if true will call VisitScalar on map entry keys,
// providing nil as the OpenAPI schema.
VisitKeysAsScalars bool
}
func (l Walker) Kind() yaml.Kind {

View File

@@ -388,3 +388,45 @@ spec:
initContainers: null
`)
}
func TestImageTagTransformerTagWithBraces(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
PrepBuiltin("ImageTagTransformer")
defer th.Reset()
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: ImageTagTransformer
metadata:
name: notImportantHere
imageTag:
name: some.registry.io/my-image
newTag: "my-fixed-tag"
`, `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: some.registry.io/my-image:{GENERATED_TAG}
name: my-image
`)
th.AssertActualEqualsExpected(rm, `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: some.registry.io/my-image:my-fixed-tag
name: my-image
`)
}

View File

@@ -6,3 +6,5 @@ require (
sigs.k8s.io/kustomize/api v0.3.1
sigs.k8s.io/yaml v1.1.0
)
replace sigs.k8s.io/kustomize/api => ../../../api

View File

@@ -56,7 +56,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
if len(matches) != 1 {
return fmt.Errorf("namespace tranformation produces ID conflict: %+v", matches)
return fmt.Errorf("namespace transformation produces ID conflict: %+v", matches)
}
}
return nil

View File

@@ -6,7 +6,7 @@
# kyaml version
export kyaml_major=0
export kyaml_minor=1
export kyaml_patch=0
export kyaml_patch=1
# kstatus version
export kstatus_major=0
@@ -21,7 +21,7 @@ export api_patch=3
# cmd/config version
export cmd_config_major=0
export cmd_config_minor=1
export cmd_config_patch=0
export cmd_config_patch=1
# cmd/kubectl version
export cmd_kubectl_major=0

View File

@@ -6,7 +6,7 @@ set -x
set -e
# verify all modules pass validation
for i in $(find . -name go.mod); do
for i in $(find . -name go.mod -not -path "./hack/*"); do
pushd .
cd $(dirname $i);
go list -m -json all > /dev/null