diff --git a/functions/go/gcp-set-project-id/Dockerfile b/functions/go/gcp-set-project-id/Dockerfile new file mode 100644 index 000000000..60ded351a --- /dev/null +++ b/functions/go/gcp-set-project-id/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.17-alpine3.13 +ENV CGO_ENABLED=0 +WORKDIR /go/src/ + +COPY go.mod go.sum ./ + +COPY . . +RUN go build -o /usr/local/bin/function ./ + +############################################# + +FROM alpine:3.13 +COPY --from=0 /usr/local/bin/function /usr/local/bin/function +ENTRYPOINT ["function"] \ No newline at end of file diff --git a/functions/go/gcp-set-project-id/consts/annotation.go b/functions/go/gcp-set-project-id/consts/annotation.go new file mode 100644 index 000000000..73153f569 --- /dev/null +++ b/functions/go/gcp-set-project-id/consts/annotation.go @@ -0,0 +1,38 @@ +package consts + +// annotationFieldSpecs update the KRM resources' annotations whose field spec matches the path. +// This requires the annotation to already exist in the KRM resource, which is different from kustomize's +// commonAnnotations that creates the annotation if not exist. +const AnnotationFieldSpecs = ` +annotations: +- path: metadata/annotations + +- path: spec/template/metadata/annotations + version: v1 + kind: ReplicationController + +- path: spec/template/metadata/annotations + kind: Deployment + +- path: spec/template/metadata/annotations + kind: ReplicaSet + +- path: spec/template/metadata/annotations + kind: DaemonSet + +- path: spec/template/metadata/annotations + kind: StatefulSet + +- path: spec/template/metadata/annotations + group: batch + kind: Job + +- path: spec/jobTemplate/metadata/annotations + group: batch + kind: CronJob + +- path: spec/jobTemplate/spec/template/metadata/annotations + group: batch + kind: CronJob + +` diff --git a/functions/go/gcp-set-project-id/consts/project.go b/functions/go/gcp-set-project-id/consts/project.go new file mode 100644 index 000000000..11f3d1902 --- /dev/null +++ b/functions/go/gcp-set-project-id/consts/project.go @@ -0,0 +1,96 @@ +package consts + +// ProjectFieldSpecs contains the fieldSpec paths of Google Cloud ProjectID +const ProjectFieldSpecs = ` +projectFieldSpec: +# Blueprint redis-bucket +- path: spec/authorizedNetworkRef/external + regexPattern: (?Pprojects\/)(?P\S+)(?P\/global\/networks\/default) + group: redis.cnrm.cloud.google.com + version: v1beta1 + kind: RedisInstance + +# Blueprint cluster +- path: spec/resourceRef/external + group: iam.cnrm.cloud.google.com + version: v1beta1 + kind: IAMPolicyMember + +# Blueprint cluster +- path: spec/projectRef/external + group: gkehub.cnrm.cloud.google.com + version: v1beta1 + kind: GKEHubFeatureMembership + +- path: spec/googleServiceAccount + regexPattern: (?P\S+@)(?P\S+)(?P\.iam\.gserviceaccount\.com) + group: core.cnrm.cloud.google.com + version: v1beta1 + kind: ConfigConnectorContext + +- path: spec/member + regexPattern: (?PserviceAccount:\S+\.svc\.id\.goog\[cnrm-system\/cnrm-controller-manager-)(?P\S+)(?P\]) + group: iam.cnrm.cloud.google.com + version: v1beta1 + kind: IAMPolicyMember +` + +/* +# Test-Only +- path: metadata/name +version: v1 +group: apps +kind: Deployment +create: true +*/ + +/* UNSUPPORTED Project FieldSpec due to substitution/partial-setter +- path: metadata/annotations[cnrm.cloud.google.com/project-id] + group: storage.cnrm.cloud.google.com + version: v1beta1 + kind: StorageBucket + create: true + +- path: spec/workloadIdentityConfig/identityNamespace + regexPattern: "(\s+)\.svc\.id\.goog" + group: container.cnrm.cloud.google.com + version: v1beta1 + kind: ContainerCluster + create: true +- path: spec/networkRef/external + regexPattern: "projects/(\s+)/global/networks/default"" + group: container.cnrm.cloud.google.com + version: v1beta1 + kind: ContainerCluster + create: true + +- path: spec/bindings[]/members[]/member + regexPattern: "^serviceAccount:\s+@(\s+).iam.gserviceaccount.com" + group: iam.cnrm.cloud.google.com + version: v1beta1 + kind: IAMPartialPolicy + create: true +- path: spec/authority/issuer + regexPattern: "https://container.googleapis.com/v1/projects/(\s+)/locations/\s+/clusters/\s+" + group: gkehub.cnrm.cloud.google.com + version: v1beta1 + kind: GKEHubMembership +- path: spec/endpoint/gkeCluster/resourceRef/external + regexPattern: "//container.googleapis.com/projects/(\s+)/locations/\s+/clusters/\s+" + group: gkehub.cnrm.cloud.google.com + version: v1beta1 + kind: GKEHubMembership + create: true +- path: spec/projectRef/external + regexPattern: "//container.googleapis.com/projects/(\s+)/locations/\s+/clusters/\s+" + group: gkehub.cnrm.cloud.google.com + version: v1beta1 + kind: GKEHubFeature + create: true +- path: spec/projectRef/configmanagement/configSync/git/gcpServiceAccountRef/external + regexPattern: "\s+@(\s+).iam.gserviceaccount.com" + group: gkehub.cnrm.cloud.google.com + version: v1beta1 + kind: GKEHubFeatureMembership + create: true +*/ diff --git a/functions/go/gcp-set-project-id/custom/filter.go b/functions/go/gcp-set-project-id/custom/filter.go new file mode 100644 index 000000000..c506edc8c --- /dev/null +++ b/functions/go/gcp-set-project-id/custom/filter.go @@ -0,0 +1,74 @@ +package custom + +import ( + "regexp" + + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ kio.Filter = Filter{} + +type Filter struct { + ProjectID string + FsSlice []types.FieldSpec +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + _, err := kio.FilterAll(yaml.FilterFunc( + func(node *yaml.RNode) (*yaml.RNode, error) { + var fns []yaml.Filter + for _, fs := range f.FsSlice { + + fn := fieldspec.Filter{ + SetValue: f.updateProjectIDFn(fs.RegexPattern), + FieldSpec: fs, + } + fns = append(fns, fn) + } + return node.Pipe(fns...) + })).Filter(nodes) + return nodes, err +} + +func (f Filter) updateProjectIDFn(regexPath string) filtersutil.SetFn { + return func(node *yaml.RNode) (err error) { + if regexPath == "" { + return node.PipeE(updater{ProjectID: f.ProjectID}) + } + defer func() { + // recover from regex panic. + if recover() != nil { + // err = fmt.Errorf("invalid regex pattern %v", regexPath) + } + }() + re := regexp.MustCompile(regexPath) + match := re.FindStringSubmatch(node.YNode().Value) + namedGroup := make(map[string]string) + for i, name := range re.SubexpNames() { + if i != 0 && name != "" { + namedGroup[name] = match[i] + } + } + newProjectID := "" + if prefix, ok := namedGroup["prefix"]; ok { + newProjectID = newProjectID + prefix + } + newProjectID = newProjectID + f.ProjectID + if suffix, ok := namedGroup["suffix"]; ok { + newProjectID = newProjectID + suffix + } + return node.PipeE(updater{ProjectID: newProjectID}) + } +} + +type updater struct { + ProjectID string +} + +func (u updater) Filter(rn *yaml.RNode) (*yaml.RNode, error) { + return rn.Pipe(yaml.FieldSetter{StringValue: u.ProjectID}) +} diff --git a/functions/go/set-project-id/generated/docs.go b/functions/go/gcp-set-project-id/generated/docs.go similarity index 100% rename from functions/go/set-project-id/generated/docs.go rename to functions/go/gcp-set-project-id/generated/docs.go diff --git a/functions/go/set-project-id/go.mod b/functions/go/gcp-set-project-id/go.mod similarity index 76% rename from functions/go/set-project-id/go.mod rename to functions/go/gcp-set-project-id/go.mod index c53243cd9..8f84ec0e1 100644 --- a/functions/go/set-project-id/go.mod +++ b/functions/go/gcp-set-project-id/go.mod @@ -1,10 +1,11 @@ -module github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/set-project-id +module github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/gcp-set-project-id go 1.17 require ( - github.com/GoogleContainerTools/kpt-functions-sdk/go v0.0.0-20210810181223-632b30549de6 - sigs.k8s.io/kustomize/kyaml v0.11.0 + sigs.k8s.io/kustomize/api v0.11.1 + sigs.k8s.io/kustomize/kyaml v0.13.3 + sigs.k8s.io/yaml v1.3.0 ) require ( @@ -20,13 +21,15 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/cobra v1.0.0 // indirect + github.com/spf13/cobra v1.2.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.7.0 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect - golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect - golang.org/x/text v0.3.2 // indirect + golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect + golang.org/x/text v0.3.5 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect ) + +replace sigs.k8s.io/kustomize/api => ./thirdparty/kustomize/api diff --git a/functions/go/gcp-set-project-id/go.sum b/functions/go/gcp-set-project-id/go.sum new file mode 100644 index 000000000..995deb6f5 --- /dev/null +++ b/functions/go/gcp-set-project-id/go.sum @@ -0,0 +1,647 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +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/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/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.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.3/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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/kustomize/api v0.11.1 h1:/Vutu+gAqVo8skw1xCZrsZD39SN4Adg+z7FrSTw9pds= +sigs.k8s.io/kustomize/api v0.11.1/go.mod h1:GZuhith5YcqxIDe0GnRJNx5xxPTjlwaLTt/e+ChUtJA= +sigs.k8s.io/kustomize/kyaml v0.13.3 h1:tNNQIC+8cc+aXFTVg+RtQAOsjwUdYBZRAgYOVI3RBc4= +sigs.k8s.io/kustomize/kyaml v0.13.3/go.mod h1:/ya3Gk4diiQzlE4mBh7wykyLRFZNvqlbh+JnwQ9Vhrc= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/functions/go/gcp-set-project-id/main.go b/functions/go/gcp-set-project-id/main.go new file mode 100644 index 000000000..9483a3484 --- /dev/null +++ b/functions/go/gcp-set-project-id/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "fmt" + "os" + + "sigs.k8s.io/kustomize/api/hasher" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/kyaml/fn/framework" + "sigs.k8s.io/kustomize/kyaml/fn/framework/command" +) + +const projectIDKey = "projectID" + + +type Processor struct {} + +func newResMapFactory() *resmap.Factory { + resourceFactory := resource.NewFactory(&hasher.Hasher{}) + resourceFactory.IncludeLocalConfigs = true + return resmap.NewFactory(resourceFactory) +} + +func (p *Processor) Process(resourceList *framework.ResourceList) error { + err := func() error{ + trans := &ProjectIDTransformer{} + err := trans.Config(resourceList.FunctionConfig) + if err != nil { + return err + } + resmapFactory := newResMapFactory() + resMap, err := resmapFactory.NewResMapFromRNodeSlice(resourceList.Items) + if err != nil { + return fmt.Errorf("failed to convert items to resource map: %w", err) + } + if err := trans.Transform(resMap); err != nil { + return err + } + resourceList.Items = resMap.ToRNodeSlice() + return nil + }() + if err != nil { + resourceList.Results = framework.Results{ + &framework.Result{ + Message: err.Error(), + Severity: framework.Error, + }, + } + return resourceList.Results + } + return nil +} + +func main() { + cmd := command.Build(&Processor{}, command.StandaloneEnabled, false) + if err := cmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/functions/go/gcp-set-project-id/plugins/annotation.go b/functions/go/gcp-set-project-id/plugins/annotation.go new file mode 100644 index 000000000..366a08091 --- /dev/null +++ b/functions/go/gcp-set-project-id/plugins/annotation.go @@ -0,0 +1,27 @@ +package plugins + +import ( + "sigs.k8s.io/kustomize/api/filters/annotations" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +type AnnotationPlugin struct { + Annotations map[string]string + FieldSpecs []types.FieldSpec `json:"annotations,omitempty" yaml:"annotations,omitempty"` +} + +func (p *AnnotationPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Annotations = nil + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *AnnotationPlugin) Transform(m resmap.ResMap) error { + return m.ApplyFilter(annotations.Filter{ + Annotations: p.Annotations, + FsSlice: p.FieldSpecs, + }) +} diff --git a/functions/go/gcp-set-project-id/plugins/custom.go b/functions/go/gcp-set-project-id/plugins/custom.go new file mode 100644 index 000000000..37cdf94c1 --- /dev/null +++ b/functions/go/gcp-set-project-id/plugins/custom.go @@ -0,0 +1,25 @@ +package plugins + +import ( + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/gcp-set-project-id/custom" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +type CustomFieldSpecPlugin struct { + ProjectID string + FsSlice []types.FieldSpec `json:"projectFieldSpec,omitempty" yaml:"projectFieldSpec,omitempty"` +} + +func (f *CustomFieldSpecPlugin) Config(c []byte) error { + f.FsSlice = nil + return yaml.Unmarshal(c, f) +} + +func (f *CustomFieldSpecPlugin) Transform(m resmap.ResMap) error { + return m.ApplyFilter(custom.Filter{ + ProjectID: f.ProjectID, + FsSlice: f.FsSlice, + }) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/builtins/builtins.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/builtins/builtins.go new file mode 100644 index 000000000..5f671260b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/builtins/builtins.go @@ -0,0 +1,51 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Deprecated: Package api/builtins will not be available in API v1. +package builtins + +import ( + internal "sigs.k8s.io/kustomize/api/internal/builtins" +) + +type ( + AnnotationsTransformerPlugin = internal.AnnotationsTransformerPlugin + ConfigMapGeneratorPlugin = internal.ConfigMapGeneratorPlugin + HashTransformerPlugin = internal.HashTransformerPlugin + HelmChartInflationGeneratorPlugin = internal.HelmChartInflationGeneratorPlugin + IAMPolicyGeneratorPlugin = internal.IAMPolicyGeneratorPlugin + ImageTagTransformerPlugin = internal.ImageTagTransformerPlugin + LabelTransformerPlugin = internal.LabelTransformerPlugin + LegacyOrderTransformerPlugin = internal.LegacyOrderTransformerPlugin + NamespaceTransformerPlugin = internal.NamespaceTransformerPlugin + PatchJson6902TransformerPlugin = internal.PatchJson6902TransformerPlugin + PatchStrategicMergeTransformerPlugin = internal.PatchStrategicMergeTransformerPlugin + PatchTransformerPlugin = internal.PatchTransformerPlugin + PrefixTransformerPlugin = internal.PrefixTransformerPlugin + SuffixTransformerPlugin = internal.SuffixTransformerPlugin + ReplacementTransformerPlugin = internal.ReplacementTransformerPlugin + ReplicaCountTransformerPlugin = internal.ReplicaCountTransformerPlugin + SecretGeneratorPlugin = internal.SecretGeneratorPlugin + ValueAddTransformerPlugin = internal.ValueAddTransformerPlugin +) + +var ( + NewAnnotationsTransformerPlugin = internal.NewAnnotationsTransformerPlugin + NewConfigMapGeneratorPlugin = internal.NewConfigMapGeneratorPlugin + NewHashTransformerPlugin = internal.NewHashTransformerPlugin + NewHelmChartInflationGeneratorPlugin = internal.NewHelmChartInflationGeneratorPlugin + NewIAMPolicyGeneratorPlugin = internal.NewIAMPolicyGeneratorPlugin + NewImageTagTransformerPlugin = internal.NewImageTagTransformerPlugin + NewLabelTransformerPlugin = internal.NewLabelTransformerPlugin + NewLegacyOrderTransformerPlugin = internal.NewLegacyOrderTransformerPlugin + NewNamespaceTransformerPlugin = internal.NewNamespaceTransformerPlugin + NewPatchJson6902TransformerPlugin = internal.NewPatchJson6902TransformerPlugin + NewPatchStrategicMergeTransformerPlugin = internal.NewPatchStrategicMergeTransformerPlugin + NewPatchTransformerPlugin = internal.NewPatchTransformerPlugin + NewPrefixTransformerPlugin = internal.NewPrefixTransformerPlugin + NewSuffixTransformerPlugin = internal.NewSuffixTransformerPlugin + NewReplacementTransformerPlugin = internal.NewReplacementTransformerPlugin + NewReplicaCountTransformerPlugin = internal.NewReplicaCountTransformerPlugin + NewSecretGeneratorPlugin = internal.NewSecretGeneratorPlugin + NewValueAddTransformerPlugin = internal.NewValueAddTransformerPlugin +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filesys/filesys.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filesys/filesys.go new file mode 100644 index 000000000..171f7306b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filesys/filesys.go @@ -0,0 +1,61 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package filesys provides a file system abstraction, +// a subset of that provided by golang.org/pkg/os, +// with an on-disk and in-memory representation. +// +// Deprecated: use sigs.k8s.io/kustomize/kyaml/filesys instead. +package filesys + +import "sigs.k8s.io/kustomize/kyaml/filesys" + +const ( + // Separator is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.Separator. + Separator = filesys.Separator + // SelfDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.SelfDir. + SelfDir = filesys.SelfDir + // ParentDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.ParentDir. + ParentDir = filesys.ParentDir +) + +type ( + // FileSystem is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.FileSystem. + FileSystem = filesys.FileSystem + // FileSystemOrOnDisk is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.FileSystemOrOnDisk. + FileSystemOrOnDisk = filesys.FileSystemOrOnDisk + // ConfirmedDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.ConfirmedDir. + ConfirmedDir = filesys.ConfirmedDir +) + +// MakeEmptyDirInMemory is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.MakeEmptyDirInMemory. +func MakeEmptyDirInMemory() FileSystem { return filesys.MakeEmptyDirInMemory() } + +// MakeFsInMemory is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.MakeFsInMemory. +func MakeFsInMemory() FileSystem { return filesys.MakeFsInMemory() } + +// MakeFsOnDisk is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.MakeFsOnDisk. +func MakeFsOnDisk() FileSystem { return filesys.MakeFsOnDisk() } + +// NewTmpConfirmedDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.NewTmpConfirmedDir. +func NewTmpConfirmedDir() (filesys.ConfirmedDir, error) { return filesys.NewTmpConfirmedDir() } + +// RootedPath is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.RootedPath. +func RootedPath(elem ...string) string { return filesys.RootedPath(elem...) } + +// StripTrailingSeps is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.StripTrailingSeps. +func StripTrailingSeps(s string) string { return filesys.StripTrailingSeps(s) } + +// StripLeadingSeps is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.StripLeadingSeps. +func StripLeadingSeps(s string) string { return filesys.StripLeadingSeps(s) } + +// PathSplit is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.PathSplit. +func PathSplit(incoming string) []string { return filesys.PathSplit(incoming) } + +// PathJoin is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.PathJoin. +func PathJoin(incoming []string) string { return filesys.PathJoin(incoming) } + +// InsertPathPart is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.InsertPathPart. +func InsertPathPart(path string, pos int, part string) string { + return filesys.InsertPathPart(path, pos, part) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/annotations.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/annotations.go new file mode 100644 index 000000000..4998f5a3e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/annotations.go @@ -0,0 +1,52 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package annotations + +import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/filters/fsslice" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type annoMap map[string]string + +type Filter struct { + // Annotations is the set of annotations to apply to the inputs + Annotations annoMap `yaml:"annotations,omitempty"` + + // FsSlice contains the FieldSpecs to locate the namespace field + FsSlice types.FsSlice + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + keys := yaml.SortedMapKeys(f.Annotations) + _, err := kio.FilterAll(yaml.FilterFunc( + func(node *yaml.RNode) (*yaml.RNode, error) { + for _, k := range keys { + if err := node.PipeE(fsslice.Filter{ + FsSlice: f.FsSlice, + SetValue: f.trackableSetter.SetEntry( + k, f.Annotations[k], yaml.NodeTagString), + CreateKind: yaml.MappingNode, // Annotations are MappingNodes. + CreateTag: yaml.NodeTagMap, + }); err != nil { + return nil, err + } + } + return node, nil + })).Filter(nodes) + return nodes, err +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/annotations_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/annotations_test.go new file mode 100644 index 000000000..d52cd673a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/annotations_test.go @@ -0,0 +1,299 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package annotations + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var annosFs = builtinconfig.MakeDefaultConfig().CommonAnnotations + +func TestAnnotations_Filter(t *testing.T) { + mutationTrackStub := filtertest_test.MutationTrackerStub{} + testCases := map[string]struct { + input string + expectedOutput string + filter Filter + fsslice types.FsSlice + setEntryCallback func(key, value, tag string, node *yaml.RNode) + expectedSetEntryArgs []filtertest_test.SetValueArg + }{ + "add": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler + auto: ford + bean: cannellini + clown: emmett kelley + dragon: smaug +`, + filter: Filter{Annotations: annoMap{ + "clown": "emmett kelley", + "auto": "ford", + "dragon": "smaug", + "bean": "cannellini", + }}, + }, + "update": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: superman + fiend: luthor + bean: cannellini + clown: emmett kelley +`, + filter: Filter{Annotations: annoMap{ + "clown": "emmett kelley", + "hero": "superman", + "fiend": "luthor", + "bean": "cannellini", + }}, + }, + "data-fieldspecs": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + sleater: kinney +a: + b: + sleater: kinney +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + annotations: + sleater: kinney +a: + b: + sleater: kinney +`, + filter: Filter{Annotations: annoMap{ + "sleater": "kinney", + }}, + fsslice: []types.FieldSpec{ + { + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + }, + + "number": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler + 2: ford + clown: "1" +`, + filter: Filter{Annotations: annoMap{ + "clown": "1", + "2": "ford", + }}, + }, + + // test quoting of values which are not considered strings in yaml 1.1 + "yaml_1_1_compatibility": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + hero: batman + fiend: riddler + a: "y" + b: y1 + c: "yes" + d: yes1 + e: "true" + f: true1 +`, + filter: Filter{Annotations: annoMap{ + "a": "y", + "b": "y1", + "c": "yes", + "d": "yes1", + "e": "true", + "f": "true1", + }}, + }, + + // test quoting of values which are not considered strings in yaml 1.1 + "null_annotations": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: null +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + a: a1 + b: b1 +`, + filter: Filter{Annotations: annoMap{ + "a": "a1", + "b": "b1", + }}, + }, + + // test usage of SetEntryCallback + "set_entry_callback": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + a: a1 + b: b1 +spec: + template: + metadata: + annotations: + a: a1 + b: b1 +`, + filter: Filter{ + Annotations: annoMap{ + "a": "a1", + "b": "b1", + }, + }, + setEntryCallback: mutationTrackStub.MutationTracker, + fsslice: []types.FieldSpec{ + { + Path: "spec/template/metadata/annotations", + CreateIfNotPresent: true, + }, + }, + expectedSetEntryArgs: []filtertest_test.SetValueArg{ + { + Key: "a", + Value: "a1", + Tag: "!!str", + NodePath: []string{"metadata", "annotations"}, + }, + { + Key: "a", + Value: "a1", + Tag: "!!str", + NodePath: []string{"spec", "template", "metadata", "annotations"}, + }, + { + Key: "b", + Value: "b1", + Tag: "!!str", + NodePath: []string{"metadata", "annotations"}, + }, + { + Key: "b", + Value: "b1", + Tag: "!!str", + NodePath: []string{"spec", "template", "metadata", "annotations"}, + }, + }, + }, + } + + for tn, tc := range testCases { + mutationTrackStub.Reset() + t.Run(tn, func(t *testing.T) { + filter := tc.filter + filter.WithMutationTracker(tc.setEntryCallback) + filter.FsSlice = append(annosFs, tc.fsslice...) + if !assert.Equal(t, + strings.TrimSpace(tc.expectedOutput), + strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) { + t.FailNow() + } + if !assert.Equal(t, tc.expectedSetEntryArgs, mutationTrackStub.SetValueArgs()) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/doc.go new file mode 100644 index 000000000..b1f6a0b66 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package annotations contains a kio.Filter implementation of the kustomize +// annotations transformer. +package annotations diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/example_test.go new file mode 100644 index 000000000..75eb51314 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/annotations/example_test.go @@ -0,0 +1,61 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package annotations + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + fss := builtinconfig.MakeDefaultConfig().CommonAnnotations + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`)}}, + Filters: []kio.Filter{Filter{ + Annotations: map[string]string{ + "foo": "bar", + "booleanValue": "true", + "numberValue": "42", + }, + FsSlice: fss, + }}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // annotations: + // booleanValue: "true" + // foo: bar + // numberValue: "42" + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // annotations: + // booleanValue: "true" + // foo: bar + // numberValue: "42" +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/doc.go new file mode 100644 index 000000000..8ced59dd1 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/doc.go @@ -0,0 +1,5 @@ +package filters + +// Package filters collects various implementations +// sigs.k8s.io/kustomize/kyaml/kio.Filter used by kustomize +// transformers to modify kubernetes objects. diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/doc.go new file mode 100644 index 000000000..6f643630a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package fieldspec contains a yaml.Filter to modify a resource +// that matches the FieldSpec. +package fieldspec diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/example_test.go new file mode 100644 index 000000000..53c37a6e5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/example_test.go @@ -0,0 +1,61 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fieldspec_test + +import ( + "bytes" + "log" + "os" + + . "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func ExampleFilter() { + in := &kio.ByteReader{ + Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`), + } + fltr := Filter{ + CreateKind: yaml.ScalarNode, + SetValue: filtersutil.SetScalar("green"), + FieldSpec: types.FieldSpec{Path: "a/b", CreateIfNotPresent: true}, + } + + err := kio.Pipeline{ + Inputs: []kio.Reader{in}, + Filters: []kio.Filter{kio.FilterAll(fltr)}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // a: + // b: green + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // a: + // b: green +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/fieldspec.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/fieldspec.go new file mode 100644 index 000000000..8ee052865 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/fieldspec.go @@ -0,0 +1,182 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fieldspec + +import ( + "fmt" + "strings" + + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ yaml.Filter = Filter{} + +// Filter possibly mutates its object argument using a FieldSpec. +// If the object matches the FieldSpec, and the node found +// by following the fieldSpec's path is non-null, this filter calls +// the setValue function on the node at the end of the path. +// If any part of the path doesn't exist, the filter returns +// without doing anything and without error, unless it was set +// to create the path. If set to create, it creates a tree of maps +// along the path, and the leaf node gets the setValue called on it. +// Error on GVK mismatch, empty or poorly formed path. +// Filter expect kustomize style paths, not JSON paths. +// Filter stores internal state and should not be reused +type Filter struct { + // FieldSpec contains the path to the value to set. + FieldSpec types.FieldSpec `yaml:"fieldSpec"` + + // Set the field using this function + SetValue filtersutil.SetFn + + // CreateKind defines the type of node to create if the field is not found + CreateKind yaml.Kind + + CreateTag string + + // path keeps internal state about the current path + path []string +} + +func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) { + // check if the FieldSpec applies to the object + if match := isMatchGVK(fltr.FieldSpec, obj); !match { + return obj, nil + } + fltr.path = utils.PathSplitter(fltr.FieldSpec.Path, "/") + if err := fltr.filter(obj); err != nil { + return nil, errors.WrapPrefixf(err, + "considering field '%s' of object %s", fltr.FieldSpec.Path, resid.FromRNode(obj)) + } + return obj, nil +} + +// Recursively called. +func (fltr Filter) filter(obj *yaml.RNode) error { + if len(fltr.path) == 0 { + // found the field -- set its value + return fltr.SetValue(obj) + } + if obj.IsTaggedNull() || obj.IsNil() { + return nil + } + switch obj.YNode().Kind { + case yaml.SequenceNode: + return fltr.handleSequence(obj) + case yaml.MappingNode: + return fltr.handleMap(obj) + case yaml.AliasNode: + return fltr.filter(yaml.NewRNode(obj.YNode().Alias)) + default: + return errors.Errorf("expected sequence or mapping node") + } +} + +// handleMap calls filter on the map field matching the next path element +func (fltr Filter) handleMap(obj *yaml.RNode) error { + fieldName, isSeq := isSequenceField(fltr.path[0]) + if fieldName == "" { + return fmt.Errorf("cannot set or create an empty field name") + } + // lookup the field matching the next path element + var operation yaml.Filter + var kind yaml.Kind + tag := yaml.NodeTagEmpty + switch { + case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq: + // don't create the field if we don't find it + operation = yaml.Lookup(fieldName) + if isSeq { + // The query path thinks this field should be a sequence; + // accept this hint for use later if the tag is NodeTagNull. + kind = yaml.SequenceNode + } + case len(fltr.path) <= 1: + // create the field if it is missing: use the provided node kind + operation = yaml.LookupCreate(fltr.CreateKind, fieldName) + kind = fltr.CreateKind + tag = fltr.CreateTag + default: + // create the field if it is missing: must be a mapping node + operation = yaml.LookupCreate(yaml.MappingNode, fieldName) + kind = yaml.MappingNode + tag = yaml.NodeTagMap + } + + // locate (or maybe create) the field + field, err := obj.Pipe(operation) + if err != nil { + return errors.WrapPrefixf(err, "fieldName: %s", fieldName) + } + if field == nil { + // No error if field not found. + return nil + } + + // if the value exists, but is null and kind is set, + // then change it to the creation type + // TODO: update yaml.LookupCreate to support this + if field.YNode().Tag == yaml.NodeTagNull && yaml.IsCreate(kind) { + field.YNode().Kind = kind + field.YNode().Tag = tag + } + + // copy the current fltr and change the path on the copy + var next = fltr + // call filter for the next path element on the matching field + next.path = fltr.path[1:] + return next.filter(field) +} + +// seq calls filter on all sequence elements +func (fltr Filter) handleSequence(obj *yaml.RNode) error { + if err := obj.VisitElements(func(node *yaml.RNode) error { + // set an accurate FieldPath for nested elements + node.AppendToFieldPath(obj.FieldPath()...) + // recurse on each element -- re-allocating a Filter is + // not strictly required, but is more consistent with field + // and less likely to have side effects + // keep the entire path -- it does not contain parts for sequences + return fltr.filter(node) + }); err != nil { + return errors.WrapPrefixf(err, + "visit traversal on path: %v", fltr.path) + } + return nil +} + +// isSequenceField returns true if the path element is for a sequence field. +// isSequence also returns the path element with the '[]' suffix trimmed +func isSequenceField(name string) (string, bool) { + shorter := strings.TrimSuffix(name, "[]") + return shorter, shorter != name +} + +// isMatchGVK returns true if the fs.GVK matches the obj GVK. +func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) bool { + if kind := obj.GetKind(); fs.Kind != "" && fs.Kind != kind { + // kind doesn't match + return false + } + + // parse the group and version from the apiVersion field + group, version := resid.ParseGroupVersion(obj.GetApiVersion()) + + if fs.Group != "" && fs.Group != group { + // group doesn't match + return false + } + + if fs.Version != "" && fs.Version != version { + // version doesn't match + return false + } + + return true +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/fieldspec_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/fieldspec_test.go new file mode 100644 index 000000000..fdc48a28e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fieldspec/fieldspec_test.go @@ -0,0 +1,642 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fieldspec_test + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestFilter_Filter(t *testing.T) { + testCases := map[string]struct { + input string + expected string + filter fieldspec.Filter + fieldSpec string + error string + }{ + "path not found": { + fieldSpec: ` +path: a/b +group: foo +kind: Bar +`, + input: ` +apiVersion: foo +kind: Bar +xxx: +`, + expected: ` +apiVersion: foo +kind: Bar +xxx: +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + "empty path": { + fieldSpec: ` +group: foo +version: v1 +kind: Bar +`, + input: ` +apiVersion: foo/v1 +kind: Bar +xxx: +`, + expected: ` +apiVersion: foo +kind: Bar +xxx: +`, + error: `considering field '' of object Bar.v1.foo/[noName].[noNs]: cannot set or create an empty field name`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "update": { + fieldSpec: ` +path: a/b +group: foo +kind: Bar +`, + input: ` +apiVersion: foo/v1beta1 +kind: Bar +a: + b: c +`, + expected: ` +apiVersion: foo/v1beta1 +kind: Bar +a: + b: e +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "update-kind-not-match": { + fieldSpec: ` +path: a/b +group: foo +kind: Bar1 +`, + input: ` +apiVersion: foo/v1beta1 +kind: Bar2 +a: + b: c +`, + expected: ` +apiVersion: foo/v1beta1 +kind: Bar2 +a: + b: c +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "update-group-not-match": { + fieldSpec: ` +path: a/b +group: foo1 +kind: Bar +`, + input: ` +apiVersion: foo2/v1beta1 +kind: Bar +a: + b: c +`, + expected: ` +apiVersion: foo2/v1beta1 +kind: Bar +a: + b: c +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "update-version-not-match": { + fieldSpec: ` +path: a/b +group: foo +version: v1beta1 +kind: Bar +`, + input: ` +apiVersion: foo/v1beta2 +kind: Bar +a: + b: c +`, + expected: ` +apiVersion: foo/v1beta2 +kind: Bar +a: + b: c +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "bad-version": { + fieldSpec: ` +path: a/b +group: foo +version: v1beta1 +kind: Bar +`, + input: ` +apiVersion: foo/v1beta2/something +kind: Bar +a: + b: c +`, + expected: ` +apiVersion: foo/v1beta2/something +kind: Bar +a: + b: c +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "bad-meta": { + fieldSpec: ` +path: a/b +group: foo +version: v1beta1 +kind: Bar +`, + input: ` +a: + b: c +`, + expected: ` +a: + b: c +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "miss-match-type": { + fieldSpec: ` +path: a/b/c +kind: Bar +`, + input: ` +kind: Bar +a: + b: a +`, + error: `considering field 'a/b/c' of object Bar.[noVer].[noGrp]/[noName].[noNs]: expected sequence or mapping node`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + "add": { + fieldSpec: ` +path: a/b/c/d +group: foo +create: true +kind: Bar +`, + input: ` +apiVersion: foo/v1beta1 +kind: Bar +a: {} +`, + expected: ` +apiVersion: foo/v1beta1 +kind: Bar +a: {b: {c: {d: e}}} +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, + + "update-in-sequence": { + fieldSpec: ` +path: a/b[]/c/d +group: foo +kind: Bar +`, + input: ` +apiVersion: foo/v1beta1 +kind: Bar +a: + b: + - c: + d: a +`, + expected: ` +apiVersion: foo/v1beta1 +kind: Bar +a: + b: + - c: + d: e +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + }, + }, + + // Don't create a sequence + "empty-sequence-no-create": { + fieldSpec: ` +path: a/b[]/c/d +group: foo +create: true +kind: Bar +`, + input: ` +apiVersion: foo/v1beta1 +kind: Bar +a: {} +`, + expected: ` +apiVersion: foo/v1beta1 +kind: Bar +a: {} +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, + + // Create a new field for an element in a sequence + "empty-sequence-create": { + fieldSpec: ` +path: a/b[]/c/d +group: foo +create: true +kind: Bar +`, + input: ` +apiVersion: foo/v1beta1 +kind: Bar +a: + b: + - c: {} +`, + expected: ` +apiVersion: foo/v1beta1 +kind: Bar +a: + b: + - c: {d: e} +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, + + "group v1": { + fieldSpec: ` +path: a/b +group: v1 +create: true +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +`, + expected: ` +apiVersion: v1 +kind: Bar +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, + + "version v1": { + fieldSpec: ` +path: a/b +version: v1 +create: true +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +`, + expected: ` +apiVersion: v1 +kind: Bar +a: + b: e +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, + + "successfully set field on array entry no sequence hint": { + fieldSpec: ` +path: spec/containers/image +version: v1 +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +spec: + containers: + - image: foo +`, + expected: ` +apiVersion: v1 +kind: Bar +spec: + containers: + - image: bar +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("bar"), + CreateKind: yaml.ScalarNode, + }, + }, + + "successfully set field on array entry with sequence hint": { + fieldSpec: ` +path: spec/containers[]/image +version: v1 +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +spec: + containers: + - image: foo +`, + expected: ` +apiVersion: v1 +kind: Bar +spec: + containers: + - image: bar +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("bar"), + CreateKind: yaml.ScalarNode, + }, + }, + "failure to set field on array entry with sequence hint in path": { + fieldSpec: ` +path: spec/containers[]/image +version: v1 +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +spec: + containers: +`, + expected: ` +apiVersion: v1 +kind: Bar +spec: + containers: [] +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("bar"), + CreateKind: yaml.ScalarNode, + }, + }, + + "failure to set field on array entry, no sequence hint in path": { + fieldSpec: ` +path: spec/containers/image +version: v1 +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +spec: + containers: +`, + expected: ` +apiVersion: v1 +kind: Bar +spec: + containers: +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("bar"), + CreateKind: yaml.ScalarNode, + }, + }, + "fieldname with slash '/'": { + fieldSpec: ` +path: a/b\/c/d +version: v1 +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +a: + b/c: + d: foo +`, + expected: ` +apiVersion: v1 +kind: Bar +a: + b/c: + d: bar +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("bar"), + CreateKind: yaml.ScalarNode, + }, + }, + "fieldname with multiple '/'": { + fieldSpec: ` +path: a/b\/c/d\/e/f +version: v1 +kind: Bar +`, + input: ` +apiVersion: v1 +kind: Bar +a: + b/c: + d/e: + f: foo +`, + expected: ` +apiVersion: v1 +kind: Bar +a: + b/c: + d/e: + f: bar +`, + filter: fieldspec.Filter{ + SetValue: filtersutil.SetScalar("bar"), + CreateKind: yaml.ScalarNode, + }, + }, + } + + for n := range testCases { + tc := testCases[n] + t.Run(n, func(t *testing.T) { + err := yaml.Unmarshal([]byte(tc.fieldSpec), &tc.filter.FieldSpec) + if !assert.NoError(t, err) { + t.FailNow() + } + + out := &bytes.Buffer{} + rw := &kio.ByteReadWriter{ + Reader: bytes.NewBufferString(tc.input), + Writer: out, + OmitReaderAnnotations: true, + } + + // run the filter + err = kio.Pipeline{ + Inputs: []kio.Reader{rw}, + Filters: []kio.Filter{kio.FilterAll(tc.filter)}, + Outputs: []kio.Writer{rw}, + }.Execute() + if tc.error != "" { + if !assert.EqualError(t, err, tc.error) { + t.FailNow() + } + // stop rest of test + return + } + + if !assert.NoError(t, err) { + t.FailNow() + } + + // check results + if !assert.Equal(t, + strings.TrimSpace(tc.expected), + strings.TrimSpace(out.String())) { + t.FailNow() + } + }) + } +} + +func TestFilter_FieldPaths(t *testing.T) { + testCases := map[string]struct { + input string + fieldSpec string + expected []string + }{ + "fieldpath containing SequenceNode": { + input: ` +apiVersion: v1 +kind: Pod +metadata: + name: app +spec: + containers: + - name: store + image: redis:6.2.6 + - name: server + image: nginx:latest +`, + fieldSpec: ` +path: spec/containers[]/image +kind: Pod +`, + expected: []string{ + "spec.containers.image", + "spec.containers.image", + }, + }, + "fieldpath with MappingNode": { + input: ` +apiVersion: v1 +kind: Pod +metadata: + name: app +spec: + containers: + - name: store + image: redis:6.2.6 + - name: server + image: nginx:latest +`, + fieldSpec: ` +path: metadata/name +kind: Pod +`, + expected: []string{ + "metadata.name", + }, + }, + } + for name, tc := range testCases { + var fieldPaths []string + trackableSetter := filtersutil.TrackableSetter{} + trackableSetter.WithMutationTracker(func(key, value, tag string, node *yaml.RNode) { + fieldPaths = append(fieldPaths, strings.Join(node.FieldPath(), ".")) + }) + filter := fieldspec.Filter{ + SetValue: trackableSetter.SetScalar("foo"), + } + + t.Run(name, func(t *testing.T) { + err := yaml.Unmarshal([]byte(tc.fieldSpec), &filter.FieldSpec) + assert.NoError(t, err) + rw := &kio.ByteReadWriter{ + Reader: bytes.NewBufferString(tc.input), + Writer: &bytes.Buffer{}, + OmitReaderAnnotations: true, + } + + // run the filter + err = kio.Pipeline{ + Inputs: []kio.Reader{rw}, + Filters: []kio.Filter{kio.FilterAll(filter)}, + Outputs: []kio.Writer{rw}, + }.Execute() + + assert.NoError(t, err) + assert.Equal(t, tc.expected, fieldPaths) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/filtersutil/setters.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/filtersutil/setters.go new file mode 100644 index 000000000..fd44f80c5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/filtersutil/setters.go @@ -0,0 +1,69 @@ +package filtersutil + +import ( + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// SetFn is a function that accepts an RNode to possibly modify. +type SetFn func(*yaml.RNode) error + +// SetScalar returns a SetFn to set a scalar value +func SetScalar(value string) SetFn { + return func(node *yaml.RNode) error { + return node.PipeE(yaml.FieldSetter{StringValue: value}) + } +} + +// SetEntry returns a SetFn to set an entry in a map +func SetEntry(key, value, tag string) SetFn { + n := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: value, + Tag: tag, + } + if tag == yaml.NodeTagString && yaml.IsYaml1_1NonString(n) { + n.Style = yaml.DoubleQuotedStyle + } + return func(node *yaml.RNode) error { + return node.PipeE(yaml.FieldSetter{ + Name: key, + Value: yaml.NewRNode(n), + }) + } +} + +type TrackableSetter struct { + // SetValueCallback will be invoked each time a field is set + setValueCallback func(key, value, tag string, node *yaml.RNode) +} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (s *TrackableSetter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + s.setValueCallback = callback +} + +// SetScalar returns a SetFn to set a scalar value +// if a mutation tracker has been registered, the tracker will be invoked each +// time a scalar is set +func (s TrackableSetter) SetScalar(value string) SetFn { + origSetScalar := SetScalar(value) + return func(node *yaml.RNode) error { + if s.setValueCallback != nil { + s.setValueCallback("", value, "", node) + } + return origSetScalar(node) + } +} + +// SetEntry returns a SetFn to set an entry in a map +// if a mutation tracker has been registered, the tracker will be invoked each +// time an entry is set +func (s TrackableSetter) SetEntry(key, value, tag string) SetFn { + origSetEntry := SetEntry(key, value, tag) + return func(node *yaml.RNode) error { + if s.setValueCallback != nil { + s.setValueCallback(key, value, tag, node) + } + return origSetEntry(node) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/doc.go new file mode 100644 index 000000000..b0f197722 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package fsslice contains a yaml.Filter to modify a resource if +// it matches one or more FieldSpec entries. +package fsslice diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/example_test.go new file mode 100644 index 000000000..cbcbbc45e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/example_test.go @@ -0,0 +1,63 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fsslice_test + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/filters/fsslice" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func ExampleFilter() { + in := &kio.ByteReader{ + Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`), + } + fltr := fsslice.Filter{ + CreateKind: yaml.ScalarNode, + SetValue: filtersutil.SetScalar("green"), + FsSlice: []types.FieldSpec{ + {Path: "a/b", CreateIfNotPresent: true}, + }, + } + + err := kio.Pipeline{ + Inputs: []kio.Reader{in}, + Filters: []kio.Filter{kio.FilterAll(fltr)}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // a: + // b: green + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // a: + // b: green +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/fsslice.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/fsslice.go new file mode 100644 index 000000000..9eb5c1313 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/fsslice.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fsslice + +import ( + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ yaml.Filter = Filter{} + +// Filter ranges over an FsSlice to modify fields on a single object. +// An FsSlice is a range of FieldSpecs. A FieldSpec is a GVK plus a path. +type Filter struct { + // FieldSpecList list of FieldSpecs to set + FsSlice types.FsSlice `yaml:"fsSlice"` + + // SetValue is called on each field that matches one of the FieldSpecs + SetValue filtersutil.SetFn + + // CreateKind is used to create fields that do not exist + CreateKind yaml.Kind + + // CreateTag is used to set the tag if encountering a null field + CreateTag string +} + +func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) { + for i := range fltr.FsSlice { + // apply this FieldSpec + // create a new filter for each iteration because they + // store internal state about the field paths + _, err := (&fieldspec.Filter{ + FieldSpec: fltr.FsSlice[i], + SetValue: fltr.SetValue, + CreateKind: fltr.CreateKind, + CreateTag: fltr.CreateTag, + }).Filter(obj) + if err != nil { + return nil, err + } + } + return obj, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/fsslice_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/fsslice_test.go new file mode 100644 index 000000000..1dedbedf9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/fsslice/fsslice_test.go @@ -0,0 +1,121 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fsslice_test + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + . "sigs.k8s.io/kustomize/api/filters/fsslice" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type TestCase struct { + input string + expected string + filter Filter + fsSlice string + error string +} + +var tests = map[string]TestCase{ + "empty": { + fsSlice: ` +`, + input: ` +apiVersion: foo/v1 +kind: Bar +`, + expected: ` +apiVersion: foo/v1 +kind: Bar +`, + filter: Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, + "two": { + fsSlice: ` +- path: a/b + group: foo + version: v1 + create: true + kind: Bar +- path: q/r[]/s/t + group: foo + version: v1 + create: true + kind: Bar +`, + input: ` +apiVersion: foo/v1 +kind: Bar +q: + r: + - s: {} +`, + expected: ` +apiVersion: foo/v1 +kind: Bar +q: + r: + - s: {t: e} +a: + b: e +`, + filter: Filter{ + SetValue: filtersutil.SetScalar("e"), + CreateKind: yaml.ScalarNode, + }, + }, +} + +func TestFilter(t *testing.T) { + for name := range tests { + test := tests[name] + t.Run(name, func(t *testing.T) { + err := yaml.Unmarshal([]byte(test.fsSlice), &test.filter.FsSlice) + if !assert.NoError(t, err) { + t.FailNow() + } + + out := &bytes.Buffer{} + rw := &kio.ByteReadWriter{ + Reader: bytes.NewBufferString(test.input), + Writer: out, + OmitReaderAnnotations: true, + } + + // run the filter + err = kio.Pipeline{ + Inputs: []kio.Reader{rw}, + Filters: []kio.Filter{kio.FilterAll(test.filter)}, + Outputs: []kio.Writer{rw}, + }.Execute() + if test.error != "" { + if !assert.EqualError(t, err, test.error) { + t.FailNow() + } + // stop rest of test + return + } + + if !assert.NoError(t, err) { + t.FailNow() + } + + // check results + if !assert.Equal(t, + strings.TrimSpace(test.expected), + strings.TrimSpace(out.String())) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/doc.go new file mode 100644 index 000000000..d0ac6d91f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/doc.go @@ -0,0 +1,3 @@ +// Package gkesagenerator contains a kio.Filter that that generates a +// iampolicy-related resources for a given cloud provider +package iampolicygenerator diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/example_test.go new file mode 100644 index 000000000..9bff1b527 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/example_test.go @@ -0,0 +1,46 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package iampolicygenerator + +import ( + "log" + "os" + + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func ExampleFilter() { + f := Filter{} + var err = yaml.Unmarshal([]byte(` +cloud: gke +kubernetesService: + namespace: k8s-namespace + name: k8s-sa-name +serviceAccount: + name: gsa-name + projectId: project-id +`), &f) + if err != nil { + log.Fatal(err) + } + + err = kio.Pipeline{ + Inputs: []kio.Reader{}, + Filters: []kio.Filter{f}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: v1 + // kind: ServiceAccount + // metadata: + // annotations: + // iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com + // name: k8s-sa-name + // namespace: k8s-namespace +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/iampolicygenerator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/iampolicygenerator.go new file mode 100644 index 000000000..c1f8593fb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/iampolicygenerator.go @@ -0,0 +1,55 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package iampolicygenerator + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type Filter struct { + IAMPolicyGenerator types.IAMPolicyGeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"` +} + +// Filter adds a GKE service account object to nodes +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + switch f.IAMPolicyGenerator.Cloud { + case types.GKE: + IAMPolicyResources, err := f.generateGkeIAMPolicyResources() + if err != nil { + return nil, err + } + nodes = append(nodes, IAMPolicyResources...) + default: + return nil, fmt.Errorf("cloud provider %s not supported yet", f.IAMPolicyGenerator.Cloud) + } + return nodes, nil +} + +func (f Filter) generateGkeIAMPolicyResources() ([]*yaml.RNode, error) { + var result []*yaml.RNode + input := fmt.Sprintf(` +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: %s@%s.iam.gserviceaccount.com + name: %s +`, f.IAMPolicyGenerator.ServiceAccount.Name, + f.IAMPolicyGenerator.ProjectId, + f.IAMPolicyGenerator.KubernetesService.Name) + + if f.IAMPolicyGenerator.Namespace != "" { + input = input + fmt.Sprintf("\n namespace: %s", f.IAMPolicyGenerator.Namespace) + } + + sa, err := yaml.Parse(input) + if err != nil { + return nil, err + } + + return append(result, sa), nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/iampolicygenerator_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/iampolicygenerator_test.go new file mode 100644 index 000000000..99bcd7bfe --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/iampolicygenerator/iampolicygenerator_test.go @@ -0,0 +1,75 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package iampolicygenerator + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" +) + +func TestFilter(t *testing.T) { + testCases := map[string]struct { + args types.IAMPolicyGeneratorArgs + expected string + }{ + "with namespace": { + args: types.IAMPolicyGeneratorArgs{ + Cloud: types.GKE, + KubernetesService: types.KubernetesService{ + Namespace: "k8s-namespace", + Name: "k8s-sa-name", + }, + ServiceAccount: types.ServiceAccount{ + Name: "gsa-name", + ProjectId: "project-id", + }, + }, + expected: ` +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com + name: k8s-sa-name + namespace: k8s-namespace +`, + }, + "without namespace": { + args: types.IAMPolicyGeneratorArgs{ + Cloud: types.GKE, + KubernetesService: types.KubernetesService{ + Name: "k8s-sa-name", + }, + ServiceAccount: types.ServiceAccount{ + Name: "gsa-name", + ProjectId: "project-id", + }, + }, + expected: ` +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com + name: k8s-sa-name +`, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + f := Filter{ + IAMPolicyGenerator: tc.args, + } + actual := filtertest.RunFilter(t, "", f) + if !assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual)) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/doc.go new file mode 100644 index 000000000..d919491dd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/doc.go @@ -0,0 +1,12 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package imagetag contains two kio.Filter implementations to cover the +// functionality of the kustomize imagetag transformer. +// +// Filter updates fields based on a FieldSpec and an ImageTag. +// +// LegacyFilter doesn't use a FieldSpec, and instead only updates image +// references if the field is name image and it is underneath a field called +// either containers or initContainers. +package imagetag diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/example_test.go new file mode 100644 index 000000000..7c0c8da06 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/example_test.go @@ -0,0 +1,126 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package imagetag + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - name: FooBar + image: nginx +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +spec: + containers: + - name: BarFoo + image: nginx:1.2.1 +`)}}, + Filters: []kio.Filter{Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + Digest: "12345", + }, + FsSlice: []types.FieldSpec{ + { + Path: "spec/containers[]/image", + }, + }, + }}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // spec: + // containers: + // - name: FooBar + // image: apache@12345 + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // spec: + // containers: + // - name: BarFoo + // image: apache@12345 +} + +func ExampleLegacyFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - name: FooBar + image: nginx +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +spec: + containers: + - name: BarFoo + image: nginx:1.2.1 +`)}}, + Filters: []kio.Filter{LegacyFilter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + Digest: "12345", + }, + }}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // spec: + // containers: + // - name: FooBar + // image: apache@12345 + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // spec: + // containers: + // - name: BarFoo + // image: apache@12345 +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/imagetag.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/imagetag.go new file mode 100644 index 000000000..24ab99f74 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/imagetag.go @@ -0,0 +1,72 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package imagetag + +import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/filters/fsslice" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Filter modifies an "image tag", the value used to specify the +// name, tag, version digest etc. of (docker) container images +// used by a pod template. +type Filter struct { + // imageTag is the tag we want to apply to the inputs + // The name of the image is used as a key, and other fields + // can specify a new name, tag, etc. + ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"` + + // FsSlice contains the FieldSpecs to locate an image field, + // e.g. Path: "spec/myContainers[]/image" + FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + _, err := kio.FilterAll(yaml.FilterFunc(f.filter)).Filter(nodes) + return nodes, err +} + +func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) { + // FsSlice is an allowlist, not a denyList, so to deny + // something via configuration a new config mechanism is + // needed. Until then, hardcode it. + if f.isOnDenyList(node) { + return node, nil + } + if err := node.PipeE(fsslice.Filter{ + FsSlice: f.FsSlice, + SetValue: imageTagUpdater{ + ImageTag: f.ImageTag, + trackableSetter: f.trackableSetter, + }.SetImageValue, + }); err != nil { + return nil, err + } + return node, nil +} + +func (f Filter) isOnDenyList(node *yaml.RNode) bool { + meta, err := node.GetMeta() + if err != nil { + // A missing 'meta' field will cause problems elsewhere; + // ignore it here to keep the signature simple. + return false + } + // Ignore CRDs + // https://github.com/kubernetes-sigs/kustomize/issues/890 + return meta.Kind == `CustomResourceDefinition` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/imagetag_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/imagetag_test.go new file mode 100644 index 000000000..d1f0bc1de --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/imagetag_test.go @@ -0,0 +1,769 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package imagetag + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestImageTagUpdater_Filter(t *testing.T) { + mutationTrackerStub := filtertest.MutationTrackerStub{} + testCases := map[string]struct { + input string + expectedOutput string + filter Filter + fsSlice types.FsSlice + setValueCallback func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest.SetValueArg + }{ + "ignore CustomResourceDefinition": { + input: ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: whatever +spec: + containers: + - image: whatever +`, + expectedOutput: ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: whatever +spec: + containers: + - image: whatever +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "whatever", + NewName: "theImageShouldNotChangeInACrd", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/containers/image", + }, + }, + }, + + "legacy multiple images in containers": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: nginx:2.1.2 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache@12345 + - image: apache@12345 +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + Digest: "12345", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/containers/image", + }, + }, + }, + "legacy both containers and initContainers": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: tomcat:1.2.3 + initContainers: + - image: nginx:1.2.1 + - image: apache:1.2.3 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache:3.2.1 + - image: tomcat:1.2.3 + initContainers: + - image: apache:3.2.1 + - image: apache:1.2.3 +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + NewTag: "3.2.1", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/containers/image", + }, + { + Path: "spec/initContainers/image", + }, + }, + }, + "legacy updates at multiple depths": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: tomcat:1.2.3 + template: + spec: + initContainers: + - image: nginx:1.2.1 + - image: apache:1.2.3 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache:3.2.1 + - image: tomcat:1.2.3 + template: + spec: + initContainers: + - image: apache:3.2.1 + - image: apache:1.2.3 +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + NewTag: "3.2.1", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/containers/image", + }, + { + Path: "spec/template/spec/initContainers/image", + }, + }, + }, + "update with digest": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + image: nginx:1.2.1 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + image: apache@12345 +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + Digest: "12345", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/image", + }, + }, + }, + + "multiple matches in sequence": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: not_nginx@54321 + - image: nginx:1.2.1 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache:3.2.1 + - image: not_nginx@54321 + - image: apache:3.2.1 +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + NewTag: "3.2.1", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/containers/image", + }, + }, + }, + + "new Tag": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:v2 + name: nginx-tagged + - image: nginx:v2 + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx:v2 + name: nginx-notag + - image: nginx:v2 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewTag: "v2", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + }, + "newImage": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: busybox:1.7.9 + name: nginx-tagged + - image: busybox:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: busybox + name: nginx-notag + - image: busybox@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "busybox", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + }, + "newImageAndTag": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: busybox:v3 + name: nginx-tagged + - image: busybox:v3 + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: busybox:v3 + name: nginx-notag + - image: busybox:v3 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "busybox", + NewTag: "v3", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + }, + "newDigest": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx@sha256:222222222222222222 + name: nginx-tagged + - image: nginx@sha256:222222222222222222 + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx@sha256:222222222222222222 + name: nginx-notag + - image: nginx@sha256:222222222222222222 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + Digest: "sha256:222222222222222222", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers/image", + }, + { + Path: "spec/template/spec/initContainers/image", + }, + }, + }, + "newImageAndDigest": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: busybox@sha256:222222222222222222 + name: nginx-tagged + - image: busybox@sha256:222222222222222222 + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: busybox@sha256:222222222222222222 + name: nginx-notag + - image: busybox@sha256:222222222222222222 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "busybox", + Digest: "sha256:222222222222222222", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + }, + "emptyContainers": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + containers: +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + containers: [] +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewTag: "v2", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/containers[]/image", + // CreateIfNotPresent: true, + }, + }, + }, + "tagWithBraces": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: some.registry.io/my-image:{GENERATED_TAG} + name: my-image +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: some.registry.io/my-image:my-fixed-tag + name: my-image +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "some.registry.io/my-image", + NewTag: "my-fixed-tag", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + }, + "mutation tracker": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: busybox:v3 + name: nginx-tagged + - image: busybox:v3 + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: busybox:v3 + name: nginx-notag + - image: busybox:v3 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "busybox", + NewTag: "v3", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + setValueCallback: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest.SetValueArg{ + { + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "containers", "image"}, + }, + { + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "containers", "image"}, + }, + { + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "initContainers", "image"}, + }, + { + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "initContainers", "image"}, + }, + }, + }, + } + + for tn, tc := range testCases { + mutationTrackerStub.Reset() + t.Run(tn, func(t *testing.T) { + filter := tc.filter + filter.WithMutationTracker(tc.setValueCallback) + filter.FsSlice = tc.fsSlice + if !assert.Equal(t, + strings.TrimSpace(tc.expectedOutput), + strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) { + t.FailNow() + } + assert.Equal(t, tc.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/legacy.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/legacy.go new file mode 100644 index 000000000..d07080b8e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/legacy.go @@ -0,0 +1,105 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package imagetag + +import ( + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// LegacyFilter is an implementation of the kio.Filter interface +// that scans through the provided kyaml data structure and updates +// any values of any image fields that is inside a sequence under +// a field called either containers or initContainers. The field is only +// update if it has a value that matches and image reference and the name +// of the image is a match with the provided ImageTag. +type LegacyFilter struct { + ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"` +} + +var _ kio.Filter = LegacyFilter{} + +func (lf LegacyFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(lf.filter)).Filter(nodes) +} + +func (lf LegacyFilter) filter(node *yaml.RNode) (*yaml.RNode, error) { + meta, err := node.GetMeta() + if err != nil { + return nil, err + } + + // We do not make any changes if the type of the resource + // is CustomResourceDefinition. + if meta.Kind == `CustomResourceDefinition` { + return node, nil + } + + fff := findFieldsFilter{ + fields: []string{"containers", "initContainers"}, + fieldCallback: checkImageTagsFn(lf.ImageTag), + } + if err := node.PipeE(fff); err != nil { + return nil, err + } + return node, nil +} + +type fieldCallback func(node *yaml.RNode) error + +// findFieldsFilter is an implementation of the kio.Filter +// interface. It will walk the data structure and look for fields +// that matches the provided list of field names. For each match, +// the value of the field will be passed in as a parameter to the +// provided fieldCallback. +// TODO: move this to kyaml/filterutils +type findFieldsFilter struct { + fields []string + + fieldCallback fieldCallback +} + +func (f findFieldsFilter) Filter(obj *yaml.RNode) (*yaml.RNode, error) { + return obj, f.walk(obj) +} + +func (f findFieldsFilter) walk(node *yaml.RNode) error { + switch node.YNode().Kind { + case yaml.MappingNode: + return node.VisitFields(func(n *yaml.MapNode) error { + err := f.walk(n.Value) + if err != nil { + return err + } + key := n.Key.YNode().Value + if utils.StringSliceContains(f.fields, key) { + return f.fieldCallback(n.Value) + } + return nil + }) + case yaml.SequenceNode: + return node.VisitElements(func(n *yaml.RNode) error { + return f.walk(n) + }) + } + return nil +} + +func checkImageTagsFn(imageTag types.Image) fieldCallback { + return func(node *yaml.RNode) error { + if node.YNode().Kind != yaml.SequenceNode { + return nil + } + + return node.VisitElements(func(n *yaml.RNode) error { + // Look up any fields on the provided node that is named + // image. + return n.PipeE(yaml.Get("image"), imageTagUpdater{ + ImageTag: imageTag, + }) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/legacy_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/legacy_test.go new file mode 100644 index 000000000..28796b060 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/legacy_test.go @@ -0,0 +1,136 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package imagetag + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" +) + +func TestLegacyImageTag_Filter(t *testing.T) { + testCases := map[string]struct { + input string + expectedOutput string + filter LegacyFilter + }{ + "updates multiple images inside containers": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: nginx:2.1.2 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache@12345 + - image: apache@12345 +`, + filter: LegacyFilter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + Digest: "12345", + }, + }, + }, + "updates inside both containers and initContainers": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: tomcat:1.2.3 + initContainers: + - image: nginx:1.2.1 + - image: apache:1.2.3 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache:3.2.1 + - image: tomcat:1.2.3 + initContainers: + - image: apache:3.2.1 + - image: apache:1.2.3 +`, + filter: LegacyFilter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + NewTag: "3.2.1", + }, + }, + }, + "updates on multiple depths": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: nginx:1.2.1 + - image: tomcat:1.2.3 + template: + spec: + initContainers: + - image: nginx:1.2.1 + - image: apache:1.2.3 +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + containers: + - image: apache:3.2.1 + - image: tomcat:1.2.3 + template: + spec: + initContainers: + - image: apache:3.2.1 + - image: apache:1.2.3 +`, + filter: LegacyFilter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "apache", + NewTag: "3.2.1", + }, + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + filter := tc.filter + if !assert.Equal(t, + strings.TrimSpace(tc.expectedOutput), + strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/updater.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/updater.go new file mode 100644 index 000000000..50c0dcdc8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/imagetag/updater.go @@ -0,0 +1,52 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package imagetag + +import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/image" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// imageTagUpdater is an implementation of the kio.Filter interface +// that will update the value of the yaml node based on the provided +// ImageTag if the current value matches the format of an image reference. +type imageTagUpdater struct { + Kind string `yaml:"kind,omitempty"` + ImageTag types.Image `yaml:"imageTag,omitempty"` + trackableSetter filtersutil.TrackableSetter +} + +func (u imageTagUpdater) SetImageValue(rn *yaml.RNode) error { + if err := yaml.ErrorIfInvalid(rn, yaml.ScalarNode); err != nil { + return err + } + + value := rn.YNode().Value + + if !image.IsImageMatched(value, u.ImageTag.Name) { + return nil + } + + name, tag := image.Split(value) + if u.ImageTag.NewName != "" { + name = u.ImageTag.NewName + } + if u.ImageTag.NewTag != "" { + tag = ":" + u.ImageTag.NewTag + } + if u.ImageTag.Digest != "" { + tag = "@" + u.ImageTag.Digest + } + + return u.trackableSetter.SetScalar(name + tag)(rn) +} + +func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) { + if err := u.SetImageValue(rn); err != nil { + return nil, err + } + return rn, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/doc.go new file mode 100644 index 000000000..978033c7e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package labels contains a kio.Filter implementation of the kustomize +// labels transformer. +package labels diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/example_test.go new file mode 100644 index 000000000..b419b2810 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/example_test.go @@ -0,0 +1,55 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package labels + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + fss := builtinconfig.MakeDefaultConfig().CommonLabels + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`)}}, + Filters: []kio.Filter{Filter{ + Labels: map[string]string{ + "foo": "bar", + }, + FsSlice: fss, + }}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // labels: + // foo: bar + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // labels: + // foo: bar +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/labels.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/labels.go new file mode 100644 index 000000000..b67d4d4b1 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/labels.go @@ -0,0 +1,53 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package labels + +import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/filters/fsslice" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type labelMap map[string]string + +// Filter sets labels. +type Filter struct { + // Labels is the set of labels to apply to the inputs + Labels labelMap `yaml:"labels,omitempty"` + + // FsSlice identifies the label fields. + FsSlice types.FsSlice + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + keys := yaml.SortedMapKeys(f.Labels) + _, err := kio.FilterAll(yaml.FilterFunc( + func(node *yaml.RNode) (*yaml.RNode, error) { + for _, k := range keys { + if err := node.PipeE(fsslice.Filter{ + FsSlice: f.FsSlice, + SetValue: f.trackableSetter.SetEntry( + k, f.Labels[k], yaml.NodeTagString), + CreateKind: yaml.MappingNode, // Labels are MappingNodes. + CreateTag: yaml.NodeTagMap, + }); err != nil { + return nil, err + } + } + return node, nil + })).Filter(nodes) + return nodes, err +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/labels_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/labels_test.go new file mode 100644 index 000000000..0cdb0d126 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/labels/labels_test.go @@ -0,0 +1,476 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package labels + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestLabels_Filter(t *testing.T) { + mutationTrackerStub := filtertest_test.MutationTrackerStub{} + testCases := map[string]struct { + input string + expectedOutput string + filter Filter + setEntryCallback func(key, value, tag string, node *yaml.RNode) + expectedSetEntryArgs []filtertest_test.SetValueArg + }{ + "add": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler + auto: ford + bean: cannellini + clown: emmett kelley + dragon: smaug +`, + filter: Filter{ + Labels: labelMap{ + "clown": "emmett kelley", + "auto": "ford", + "dragon": "smaug", + "bean": "cannellini", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + }, + "update": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: superman + fiend: luthor + bean: cannellini + clown: emmett kelley +`, + filter: Filter{ + Labels: labelMap{ + "clown": "emmett kelley", + "hero": "superman", + "fiend": "luthor", + "bean": "cannellini", + }, FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + }, + + "data-fieldspecs": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + sleater: kinney +a: + b: + sleater: kinney +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + labels: + sleater: kinney +a: + b: + sleater: kinney +`, + filter: Filter{ + Labels: labelMap{ + "sleater": "kinney", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + { + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + }, + }, + + "fieldSpecWithKind": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v2 +kind: Bar +metadata: + name: instance +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + cheese: cheddar +--- +apiVersion: example.com/v2 +kind: Bar +metadata: + name: instance + labels: + cheese: cheddar +a: + b: + cheese: cheddar +`, + filter: Filter{ + Labels: labelMap{ + "cheese": "cheddar", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + { + Gvk: resid.Gvk{ + Kind: "Bar", + }, + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + }, + }, + + "fieldSpecWithVersion": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v2 +kind: Bar +metadata: + name: instance +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + cheese: cheddar +a: + b: + cheese: cheddar +--- +apiVersion: example.com/v2 +kind: Bar +metadata: + name: instance + labels: + cheese: cheddar +`, + filter: Filter{ + Labels: labelMap{ + "cheese": "cheddar", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + { + Gvk: resid.Gvk{ + Version: "v1", + }, + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + }, + }, + "fieldSpecWithVersionInConfigButNoGroupInData": { + input: ` +apiVersion: v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: v2 +kind: Bar +metadata: + name: instance +`, + expectedOutput: ` +apiVersion: v1 +kind: Foo +metadata: + name: instance + labels: + cheese: cheddar +a: + b: + cheese: cheddar +--- +apiVersion: v2 +kind: Bar +metadata: + name: instance + labels: + cheese: cheddar +`, + filter: Filter{ + Labels: labelMap{ + "cheese": "cheddar", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + { + Gvk: resid.Gvk{ + Version: "v1", + }, + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + }, + }, + + "number": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler + 1: emmett kelley + auto: "2" +`, + filter: Filter{ + Labels: labelMap{ + "1": "emmett kelley", + "auto": "2", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + }, + + // test quoting of values which are not considered strings in yaml 1.1 + "yaml_1_1_compatibility": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + hero: batman + fiend: riddler + a: "y" + b: y1 + c: "yes" + d: yes1 + e: "true" + f: true1 +`, + filter: Filter{ + Labels: labelMap{ + "a": "y", + "b": "y1", + "c": "yes", + "d": "yes1", + "e": "true", + "f": "true1", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + }, + + "null_labels": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: null +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + a: a1 +`, + filter: Filter{ + Labels: labelMap{ + "a": "a1", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + }, + + // test usage of SetEntryCallback + "set_entry_callback": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + witcher: geralt +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + witcher: geralt + mage: yennefer +a: + b: + mage: yennefer +`, + filter: Filter{ + Labels: labelMap{ + "mage": "yennefer", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + { + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + }, + setEntryCallback: mutationTrackerStub.MutationTracker, + expectedSetEntryArgs: []filtertest_test.SetValueArg{ + { + Key: "mage", + Value: "yennefer", + Tag: "!!str", + NodePath: []string{"metadata", "labels"}, + }, + { + Key: "mage", + Value: "yennefer", + Tag: "!!str", + NodePath: []string{"a", "b"}, + }, + }, + }, + } + + for tn, tc := range testCases { + mutationTrackerStub.Reset() + t.Run(tn, func(t *testing.T) { + tc.filter.WithMutationTracker(tc.setEntryCallback) + if !assert.Equal(t, + strings.TrimSpace(tc.expectedOutput), + strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) { + t.FailNow() + } + if !assert.Equal(t, tc.expectedSetEntryArgs, mutationTrackerStub.SetValueArgs()) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/doc.go new file mode 100644 index 000000000..b78499d51 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/doc.go @@ -0,0 +1,3 @@ +// Package nameref contains a kio.Filter implementation of the kustomize +// name reference transformer. +package nameref diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/nameref.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/nameref.go new file mode 100644 index 000000000..15a8515d3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/nameref.go @@ -0,0 +1,396 @@ +package nameref + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Filter updates a name references. +type Filter struct { + // Referrer refers to another resource X by X's name. + // E.g. A Deployment can refer to a ConfigMap. + // The Deployment is the Referrer, + // the ConfigMap is the ReferralTarget. + // This filter seeks to repair the reference in Deployment, given + // that the ConfigMap's name may have changed. + Referrer *resource.Resource + + // NameFieldToUpdate is the field in the Referrer + // that holds the name requiring an update. + // This is the field to write. + NameFieldToUpdate types.FieldSpec + + // ReferralTarget is the source of the new value for + // the name, always in the 'metadata/name' field. + // This is the field to read. + ReferralTarget resid.Gvk + + // Set of resources to scan to find the ReferralTarget. + ReferralCandidates resmap.ResMap +} + +// At time of writing, in practice this is called with a slice with only +// one entry, the node also referred to be the resource in the Referrer field. +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) +} + +// The node passed in here is the same node as held in Referrer; +// that's how the referrer's name field is updated. +// Currently, however, this filter still needs the extra methods on Referrer +// to consult things like the resource Id, its namespace, etc. +// TODO(3455): No filter should use the Resource api; all information +// about names should come from annotations, with helper methods +// on the RNode object. Resource should get stupider, RNode smarter. +func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + if err := f.confirmNodeMatchesReferrer(node); err != nil { + // sanity check. + return nil, err + } + f.NameFieldToUpdate.Gvk = f.Referrer.GetGvk() + if err := node.PipeE(fieldspec.Filter{ + FieldSpec: f.NameFieldToUpdate, + SetValue: f.set, + }); err != nil { + return nil, errors.Wrapf( + err, "updating name reference in '%s' field of '%s'", + f.NameFieldToUpdate.Path, f.Referrer.CurId().String()) + } + return node, nil +} + +// This function is called on the node found at FieldSpec.Path. +// It's some node in the Referrer. +func (f Filter) set(node *yaml.RNode) error { + if yaml.IsMissingOrNull(node) { + return nil + } + switch node.YNode().Kind { + case yaml.ScalarNode: + return f.setScalar(node) + case yaml.MappingNode: + return f.setMapping(node) + case yaml.SequenceNode: + return applyFilterToSeq(seqFilter{ + setScalarFn: f.setScalar, + setMappingFn: f.setMapping, + }, node) + default: + return fmt.Errorf("node must be a scalar, sequence or map") + } +} + +// This method used when NameFieldToUpdate doesn't lead to +// one scalar field (typically called 'name'), but rather +// leads to a map field (called anything). In this case we +// must complete the field path, looking for both a 'name' +// and a 'namespace' field to help select the proper +// ReferralTarget to read the name and namespace from. +func (f Filter) setMapping(node *yaml.RNode) error { + if node.YNode().Kind != yaml.MappingNode { + return fmt.Errorf("expect a mapping node") + } + nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"}) + if err != nil { + return errors.Wrap(err, "trying to match 'name' field") + } + if nameNode == nil { + // This is a _configuration_ error; the field path + // specified in NameFieldToUpdate.Path doesn't resolve + // to a map with a 'name' field, so we have no idea what + // field to update with a new name. + return fmt.Errorf("path config error; no 'name' field in node") + } + candidates, err := f.filterMapCandidatesByNamespace(node) + if err != nil { + return err + } + oldName := nameNode.YNode().Value + referral, err := f.selectReferral(oldName, candidates) + if err != nil || referral == nil { + // Nil referral means nothing to do. + return err + } + f.recordTheReferral(referral) + if referral.GetName() == oldName && referral.GetNamespace() == "" { + // The name has not changed, nothing to do. + return nil + } + if err = node.PipeE(yaml.FieldSetter{ + Name: "name", + StringValue: referral.GetName(), + }); err != nil { + return err + } + if referral.GetNamespace() == "" { + // Don't write an empty string into the namespace field, as + // it should not replace the value "default". The empty + // string is handled as a wild card here, not as an implicit + // specification of the "default" k8s namespace. + return nil + } + return node.PipeE(yaml.FieldSetter{ + Name: "namespace", + StringValue: referral.GetNamespace(), + }) +} + +func (f Filter) filterMapCandidatesByNamespace( + node *yaml.RNode) ([]*resource.Resource, error) { + namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"}) + if err != nil { + return nil, errors.Wrap(err, "trying to match 'namespace' field") + } + if namespaceNode == nil { + return f.ReferralCandidates.Resources(), nil + } + namespace := namespaceNode.YNode().Value + nsMap := f.ReferralCandidates.GroupedByOriginalNamespace() + if candidates, ok := nsMap[namespace]; ok { + return candidates, nil + } + nsMap = f.ReferralCandidates.GroupedByCurrentNamespace() + // This could be nil, or an empty list. + return nsMap[namespace], nil +} + +func (f Filter) setScalar(node *yaml.RNode) error { + referral, err := f.selectReferral( + node.YNode().Value, f.ReferralCandidates.Resources()) + if err != nil || referral == nil { + // Nil referral means nothing to do. + return err + } + f.recordTheReferral(referral) + if referral.GetName() == node.YNode().Value { + // The name has not changed, nothing to do. + return nil + } + return node.PipeE(yaml.FieldSetter{StringValue: referral.GetName()}) +} + +// In the resource, make a note that it is referred to by the Referrer. +func (f Filter) recordTheReferral(referral *resource.Resource) { + referral.AppendRefBy(f.Referrer.CurId()) +} + +// getRoleRefGvk returns a Gvk in the roleRef field. Return error +// if the roleRef, roleRef/apiGroup or roleRef/kind is missing. +func getRoleRefGvk(n *resource.Resource) (*resid.Gvk, error) { + roleRef, err := n.Pipe(yaml.Lookup("roleRef")) + if err != nil { + return nil, err + } + if roleRef.IsNil() { + return nil, fmt.Errorf("roleRef cannot be found in %s", n.MustString()) + } + apiGroup, err := roleRef.Pipe(yaml.Lookup("apiGroup")) + if err != nil { + return nil, err + } + if apiGroup.IsNil() { + return nil, fmt.Errorf( + "apiGroup cannot be found in roleRef %s", roleRef.MustString()) + } + kind, err := roleRef.Pipe(yaml.Lookup("kind")) + if err != nil { + return nil, err + } + if kind.IsNil() { + return nil, fmt.Errorf( + "kind cannot be found in roleRef %s", roleRef.MustString()) + } + return &resid.Gvk{ + Group: apiGroup.YNode().Value, + Kind: kind.YNode().Value, + }, nil +} + +// sieveFunc returns true if the resource argument satisfies some criteria. +type sieveFunc func(*resource.Resource) bool + +// doSieve uses a function to accept or ignore resources from a list. +// If list is nil, returns immediately. +// It's a filter obviously, but that term is overloaded here. +func doSieve(list []*resource.Resource, fn sieveFunc) (s []*resource.Resource) { + for _, r := range list { + if fn(r) { + s = append(s, r) + } + } + return +} + +func acceptAll(r *resource.Resource) bool { + return true +} + +func previousNameMatches(name string) sieveFunc { + return func(r *resource.Resource) bool { + for _, id := range r.PrevIds() { + if id.Name == name { + return true + } + } + return false + } +} + +func previousIdSelectedByGvk(gvk *resid.Gvk) sieveFunc { + return func(r *resource.Resource) bool { + for _, id := range r.PrevIds() { + if id.IsSelected(gvk) { + return true + } + } + return false + } +} + +// If the we are updating a 'roleRef/name' field, the 'apiGroup' and 'kind' +// fields in the same 'roleRef' map must be considered. +// If either object is cluster-scoped, there can be a referral. +// E.g. a RoleBinding (which exists in a namespace) can refer +// to a ClusterRole (cluster-scoped) object. +// https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole +// Likewise, a ClusterRole can refer to a Secret (in a namespace). +// Objects in different namespaces generally cannot refer to other +// with some exceptions (e.g. RoleBinding and ServiceAccount are both +// namespaceable, but the former can refer to accounts in other namespaces). +func (f Filter) roleRefFilter() sieveFunc { + if !strings.HasSuffix(f.NameFieldToUpdate.Path, "roleRef/name") { + return acceptAll + } + roleRefGvk, err := getRoleRefGvk(f.Referrer) + if err != nil { + return acceptAll + } + return previousIdSelectedByGvk(roleRefGvk) +} + +func prefixSuffixEquals(other resource.ResCtx) sieveFunc { + return func(r *resource.Resource) bool { + return r.PrefixesSuffixesEquals(other) + } +} + +func (f Filter) sameCurrentNamespaceAsReferrer() sieveFunc { + referrerCurId := f.Referrer.CurId() + if referrerCurId.IsClusterScoped() { + // If the referrer is cluster-scoped, let anything through. + return acceptAll + } + return func(r *resource.Resource) bool { + if r.CurId().IsClusterScoped() { + // Allow cluster-scoped through. + return true + } + if r.GetKind() == "ServiceAccount" { + // Allow service accounts through, even though they + // are in a namespace. A RoleBinding in another namespace + // can reference them. + return true + } + return referrerCurId.IsNsEquals(r.CurId()) + } +} + +// selectReferral picks the best referral from a list of candidates. +func (f Filter) selectReferral( + // The name referral that may need to be updated. + oldName string, + candidates []*resource.Resource) (*resource.Resource, error) { + candidates = doSieve(candidates, previousNameMatches(oldName)) + candidates = doSieve(candidates, previousIdSelectedByGvk(&f.ReferralTarget)) + candidates = doSieve(candidates, f.roleRefFilter()) + candidates = doSieve(candidates, f.sameCurrentNamespaceAsReferrer()) + if len(candidates) == 1 { + return candidates[0], nil + } + candidates = doSieve(candidates, prefixSuffixEquals(f.Referrer)) + if len(candidates) == 1 { + return candidates[0], nil + } + if len(candidates) == 0 { + return nil, nil + } + if allNamesAreTheSame(candidates) { + // Just take the first one. + return candidates[0], nil + } + ids := getIds(candidates) + f.failureDetails(candidates) + return nil, fmt.Errorf(" found multiple possible referrals: %s", ids) +} + +func (f Filter) failureDetails(resources []*resource.Resource) { + fmt.Printf( + "\n**** Too many possible referral targets to referrer:\n%s\n", + f.Referrer.MustYaml()) + for i, r := range resources { + fmt.Printf( + "--- possible referral %d:\n%s", i, r.MustYaml()) + fmt.Println("------") + } +} + +func allNamesAreTheSame(resources []*resource.Resource) bool { + name := resources[0].GetName() + for i := 1; i < len(resources); i++ { + if name != resources[i].GetName() { + return false + } + } + return true +} + +func getIds(rs []*resource.Resource) string { + var result []string + for _, r := range rs { + result = append(result, r.CurId().String()) + } + return strings.Join(result, ", ") +} + +func checkEqual(k, a, b string) error { + if a != b { + return fmt.Errorf( + "node-referrerOriginal '%s' mismatch '%s' != '%s'", + k, a, b) + } + return nil +} + +func (f Filter) confirmNodeMatchesReferrer(node *yaml.RNode) error { + meta, err := node.GetMeta() + if err != nil { + return err + } + gvk := f.Referrer.GetGvk() + if err = checkEqual( + "APIVersion", meta.APIVersion, gvk.ApiVersion()); err != nil { + return err + } + if err = checkEqual( + "Kind", meta.Kind, gvk.Kind); err != nil { + return err + } + if err = checkEqual( + "Name", meta.Name, f.Referrer.GetName()); err != nil { + return err + } + if err = checkEqual( + "Namespace", meta.Namespace, f.Referrer.GetNamespace()); err != nil { + return err + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/nameref_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/nameref_test.go new file mode 100644 index 000000000..75f8288c4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/nameref_test.go @@ -0,0 +1,789 @@ +package nameref + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestNamerefFilter(t *testing.T) { + testCases := map[string]struct { + referrerOriginal string + candidates string + referrerFinal string + filter Filter + originalNames []string + }{ + "simple scalar": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: NotSecret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "newName2"}, + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: newName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + "sequence": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +seq: +- oldName1 +- oldName2 +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: NotSecret +metadata: + name: newName2 +`, + originalNames: []string{"oldName1", "newName2"}, + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +seq: +- newName +- oldName2 +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "seq"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + "mapping": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +map: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: NotSecret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "newName2"}, + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +map: + name: newName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "map"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + "mapping with namespace": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep + namespace: someNs +map: + name: oldName + namespace: someNs +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName + namespace: someNs +--- +apiVersion: apps/v1 +kind: NotSecret +metadata: + name: newName2 +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: thirdName +`, + originalNames: []string{"oldName", "oldName", "oldName"}, + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep + namespace: someNs +map: + name: newName + namespace: someNs +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "map"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + "null value": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +map: + name: null +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: NotSecret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "newName2"}, + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +map: + name: null +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "map"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + factory := provider.NewDefaultDepProvider().GetResourceFactory() + referrer, err := factory.FromBytes([]byte(tc.referrerOriginal)) + if err != nil { + t.Fatal(err) + } + tc.filter.Referrer = referrer + + resMapFactory := resmap.NewFactory(factory) + candidatesRes, err := factory.SliceFromBytesWithNames( + tc.originalNames, []byte(tc.candidates)) + if err != nil { + t.Fatal(err) + } + + candidates := resMapFactory.FromResourceSlice(candidatesRes) + tc.filter.ReferralCandidates = candidates + + result := filtertest_test.RunFilter(t, tc.referrerOriginal, tc.filter) + if !assert.Equal(t, + strings.TrimSpace(tc.referrerFinal), + strings.TrimSpace(result)) { + t.FailNow() + } + }) + } +} + +func TestNamerefFilterUnhappy(t *testing.T) { + testCases := map[string]struct { + referrerOriginal string + candidates string + referrerFinal string + filter Filter + originalNames []string + }{ + "multiple match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + referrerFinal: "", + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + "no name": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + notName: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + referrerFinal: "", + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + factory := provider.NewDefaultDepProvider().GetResourceFactory() + referrer, err := factory.FromBytes([]byte(tc.referrerOriginal)) + if err != nil { + t.Fatal(err) + } + tc.filter.Referrer = referrer + + resMapFactory := resmap.NewFactory(factory) + candidatesRes, err := factory.SliceFromBytesWithNames( + tc.originalNames, []byte(tc.candidates)) + if err != nil { + t.Fatal(err) + } + + candidates := resMapFactory.FromResourceSlice(candidatesRes) + tc.filter.ReferralCandidates = candidates + + _, err = filtertest_test.RunFilterE(t, tc.referrerOriginal, tc.filter) + if err == nil { + t.Fatalf("expect an error") + } + if tc.referrerFinal != "" && !assert.EqualError(t, err, tc.referrerFinal) { + t.FailNow() + } + }) + } +} + +func TestCandidatesWithDifferentPrefixSuffix(t *testing.T) { + testCases := map[string]struct { + referrerOriginal string + candidates string + referrerFinal string + filter Filter + originalNames []string + prefix []string + suffix []string + inputPrefix string + inputSuffix string + err bool + }{ + "prefix match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix1", "prefix2"}, + suffix: []string{"", "suffix2"}, + inputPrefix: "prefix1", + inputSuffix: "", + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: newName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: false, + }, + "suffix match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"", "prefix2"}, + suffix: []string{"suffix1", "suffix2"}, + inputPrefix: "", + inputSuffix: "suffix1", + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: newName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: false, + }, + "prefix suffix both match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix1", "prefix2"}, + suffix: []string{"suffix1", "suffix2"}, + inputPrefix: "prefix1", + inputSuffix: "suffix1", + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: newName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: false, + }, + "multiple match: both": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix", "prefix"}, + suffix: []string{"suffix", "suffix"}, + inputPrefix: "prefix", + inputSuffix: "suffix", + referrerFinal: "", + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: true, + }, + "multiple match: only prefix": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix", "prefix"}, + suffix: []string{"", ""}, + inputPrefix: "prefix", + inputSuffix: "", + referrerFinal: "", + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: true, + }, + "multiple match: only suffix": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"", ""}, + suffix: []string{"suffix", "suffix"}, + inputPrefix: "", + inputSuffix: "suffix", + referrerFinal: "", + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: true, + }, + "no match: neither match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix1", "prefix2"}, + suffix: []string{"suffix1", "suffix2"}, + inputPrefix: "prefix", + inputSuffix: "suffix", + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: false, + }, + "no match: prefix doesn't match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix1", "prefix2"}, + suffix: []string{"suffix", "suffix"}, + inputPrefix: "prefix", + inputSuffix: "suffix", + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: false, + }, + "no match: suffix doesn't match": { + referrerOriginal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + candidates: ` +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName +--- +apiVersion: apps/v1 +kind: Secret +metadata: + name: newName2 +`, + originalNames: []string{"oldName", "oldName"}, + prefix: []string{"prefix", "prefix"}, + suffix: []string{"suffix1", "suffix2"}, + inputPrefix: "prefix", + inputSuffix: "suffix", + referrerFinal: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +ref: + name: oldName +`, + filter: Filter{ + NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, + ReferralTarget: resid.Gvk{ + Group: "apps", + Version: "v1", + Kind: "Secret", + }, + }, + err: false, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + factory := provider.NewDefaultDepProvider().GetResourceFactory() + referrer, err := factory.FromBytes([]byte(tc.referrerOriginal)) + if err != nil { + t.Fatal(err) + } + if tc.inputPrefix != "" { + referrer.AddNamePrefix(tc.inputPrefix) + } + if tc.inputSuffix != "" { + referrer.AddNameSuffix(tc.inputSuffix) + } + tc.filter.Referrer = referrer + + resMapFactory := resmap.NewFactory(factory) + candidatesRes, err := factory.SliceFromBytesWithNames( + tc.originalNames, []byte(tc.candidates)) + if err != nil { + t.Fatal(err) + } + for i := range candidatesRes { + if tc.prefix[i] != "" { + candidatesRes[i].AddNamePrefix(tc.prefix[i]) + } + if tc.suffix[i] != "" { + candidatesRes[i].AddNameSuffix(tc.suffix[i]) + } + } + + candidates := resMapFactory.FromResourceSlice(candidatesRes) + tc.filter.ReferralCandidates = candidates + + if !tc.err { + if !assert.Equal(t, + strings.TrimSpace(tc.referrerFinal), + strings.TrimSpace( + filtertest_test.RunFilter( + t, tc.referrerOriginal, tc.filter))) { + t.FailNow() + } + } else { + _, err := filtertest_test.RunFilterE( + t, tc.referrerOriginal, tc.filter) + if err == nil { + t.Fatalf("an error is expected") + } + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/seqfilter.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/seqfilter.go new file mode 100644 index 000000000..c880694d1 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/seqfilter.go @@ -0,0 +1,57 @@ +package nameref + +import ( + "fmt" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type setFn func(*yaml.RNode) error + +type seqFilter struct { + setScalarFn setFn + setMappingFn setFn +} + +func (sf seqFilter) Filter(node *yaml.RNode) (*yaml.RNode, error) { + if yaml.IsMissingOrNull(node) { + return node, nil + } + switch node.YNode().Kind { + case yaml.ScalarNode: + // Kind: Role/ClusterRole + // FieldSpec is rules.resourceNames + err := sf.setScalarFn(node) + return node, err + case yaml.MappingNode: + // Kind: RoleBinding/ClusterRoleBinding + // FieldSpec is subjects + // Note: The corresponding fieldSpec had been changed from + // from path: subjects/name to just path: subjects. This is + // what get mutatefield to request the mapping of the whole + // map containing namespace and name instead of just a simple + // string field containing the name + err := sf.setMappingFn(node) + return node, err + default: + return node, fmt.Errorf( + "%#v is expected to be either a string or a map of string", node) + } +} + +// applyFilterToSeq will apply the filter to each element in the sequence node +func applyFilterToSeq(filter yaml.Filter, node *yaml.RNode) error { + if node.YNode().Kind != yaml.SequenceNode { + return fmt.Errorf("expect a sequence node but got %v", node.YNode().Kind) + } + + for _, elem := range node.Content() { + rnode := yaml.NewRNode(elem) + err := rnode.PipeE(filter) + if err != nil { + return err + } + } + + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/seqfilter_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/seqfilter_test.go new file mode 100644 index 000000000..2dbf43c7d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/nameref/seqfilter_test.go @@ -0,0 +1,80 @@ +package nameref + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func SeqFilter(node *yaml.RNode) (*yaml.RNode, error) { + if node.YNode().Value == "aaa" { + node.YNode().SetString("ccc") + } + return node, nil +} + +func TestApplyFilterToSeq(t *testing.T) { + fltr := yaml.FilterFunc(SeqFilter) + + testCases := map[string]struct { + input string + expect string + }{ + "replace in seq": { + input: ` +- aaa +- bbb`, + expect: ` +- ccc +- bbb`, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + node, err := yaml.Parse(tc.input) + if err != nil { + t.Fatal(err) + } + err = applyFilterToSeq(fltr, node) + if err != nil { + t.Fatal(err) + } + if !assert.Equal(t, + strings.TrimSpace(tc.expect), + strings.TrimSpace(node.MustString())) { + t.Fatalf("expect:\n%s\nactual:\n%s", + strings.TrimSpace(tc.expect), + strings.TrimSpace(node.MustString())) + } + }) + } +} + +func TestApplyFilterToSeqUnhappy(t *testing.T) { + fltr := yaml.FilterFunc(SeqFilter) + + testCases := map[string]struct { + input string + }{ + "replace in seq": { + input: ` +aaa`, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + node, err := yaml.Parse(tc.input) + if err != nil { + t.Fatal(err) + } + err = applyFilterToSeq(fltr, node) + if err == nil { + t.Fatalf("expect an error") + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/doc.go new file mode 100644 index 000000000..539758b28 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/doc.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package namespace contains a kio.Filter implementation of the kustomize +// namespace transformer. +// +// Special cases for known Kubernetes resources have been hardcoded in addition +// to those defined by the FsSlice. +package namespace diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/example_test.go new file mode 100644 index 000000000..04afc314e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/example_test.go @@ -0,0 +1,50 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package namespace_test + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/filters/namespace" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + fss := builtinconfig.MakeDefaultConfig().NameSpace + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: bar +`)}}, + Filters: []kio.Filter{namespace.Filter{Namespace: "app", FsSlice: fss}}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // namespace: app + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // namespace: app +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/namespace.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/namespace.go new file mode 100644 index 000000000..85484bf70 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/namespace.go @@ -0,0 +1,174 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package namespace + +import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/filters/fsslice" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type Filter struct { + // Namespace is the namespace to apply to the inputs + Namespace string `yaml:"namespace,omitempty"` + + // FsSlice contains the FieldSpecs to locate the namespace field + FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (ns *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + ns.trackableSetter.WithMutationTracker(callback) +} + +func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes) +} + +// Run runs the filter on a single node rather than a slice +func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + // hacks for hardcoded types -- :( + if err := ns.hacks(node); err != nil { + return nil, err + } + + // Remove the fieldspecs that are for hardcoded fields. The fieldspecs + // exist for backwards compatibility with other implementations + // of this transformation. + // This implementation of the namespace transformation + // Does not use the fieldspecs for implementing cases which + // require hardcoded logic. + ns.FsSlice = ns.removeFieldSpecsForHacks(ns.FsSlice) + + // transformations based on data -- :) + err := node.PipeE(fsslice.Filter{ + FsSlice: ns.FsSlice, + SetValue: ns.trackableSetter.SetScalar(ns.Namespace), + CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode + CreateTag: yaml.NodeTagString, + }) + return node, err +} + +// hacks applies the namespace transforms that are hardcoded rather +// than specified through FieldSpecs. +func (ns Filter) hacks(obj *yaml.RNode) error { + gvk := resid.GvkFromNode(obj) + if err := ns.metaNamespaceHack(obj, gvk); err != nil { + return err + } + return ns.roleBindingHack(obj, gvk) +} + +// metaNamespaceHack is a hack for implementing the namespace transform +// for the metadata.namespace field on namespace scoped resources. +// namespace scoped resources are determined by NOT being present +// in a hard-coded list of cluster-scoped resource types (by apiVersion and kind). +// +// This hack should be updated to allow individual resources to specify +// if they are cluster scoped through either an annotation on the resources, +// or through inlined OpenAPI on the resource as a YAML comment. +func (ns Filter) metaNamespaceHack(obj *yaml.RNode, gvk resid.Gvk) error { + if gvk.IsClusterScoped() { + return nil + } + f := fsslice.Filter{ + FsSlice: []types.FieldSpec{ + {Path: types.MetadataNamespacePath, CreateIfNotPresent: true}, + }, + SetValue: ns.trackableSetter.SetScalar(ns.Namespace), + CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode + } + _, err := f.Filter(obj) + return err +} + +// roleBindingHack is a hack for implementing the namespace transform +// for RoleBinding and ClusterRoleBinding resource types. +// RoleBinding and ClusterRoleBinding have namespace set on +// elements of the "subjects" field if and only if the subject elements +// "name" is "default". Otherwise the namespace is not set. +// +// Example: +// +// kind: RoleBinding +// subjects: +// - name: "default" # this will have the namespace set +// ... +// - name: "something-else" # this will not have the namespace set +// ... +func (ns Filter) roleBindingHack(obj *yaml.RNode, gvk resid.Gvk) error { + if gvk.Kind != roleBindingKind && gvk.Kind != clusterRoleBindingKind { + return nil + } + + // Lookup the namespace field on all elements. + // We should change the fieldspec so this isn't necessary. + obj, err := obj.Pipe(yaml.Lookup(subjectsField)) + if err != nil || yaml.IsMissingOrNull(obj) { + return err + } + + // add the namespace to each "subject" with name: default + err = obj.VisitElements(func(o *yaml.RNode) error { + // The only case we need to force the namespace + // if for the "service account". "default" is + // kind of hardcoded here for right now. + name, err := o.Pipe( + yaml.Lookup("name"), yaml.Match("default"), + ) + if err != nil || yaml.IsMissingOrNull(name) { + return err + } + + // set the namespace for the default account + node, err := o.Pipe( + yaml.LookupCreate(yaml.ScalarNode, "namespace"), + ) + if err != nil { + return err + } + + return ns.trackableSetter.SetScalar(ns.Namespace)(node) + + }) + + return err +} + +// removeFieldSpecsForHacks removes from the list fieldspecs that +// have hardcoded implementations +func (ns Filter) removeFieldSpecsForHacks(fs types.FsSlice) types.FsSlice { + var val types.FsSlice + for i := range fs { + // implemented by metaNamespaceHack + if fs[i].Path == types.MetadataNamespacePath { + continue + } + // implemented by roleBindingHack + if fs[i].Kind == roleBindingKind && fs[i].Path == subjectsField { + continue + } + // implemented by roleBindingHack + if fs[i].Kind == clusterRoleBindingKind && fs[i].Path == subjectsField { + continue + } + val = append(val, fs[i]) + } + return val +} + +const ( + subjectsField = "subjects" + roleBindingKind = "RoleBinding" + clusterRoleBindingKind = "ClusterRoleBinding" +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/namespace_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/namespace_test.go new file mode 100644 index 000000000..302abe035 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/namespace/namespace_test.go @@ -0,0 +1,385 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package namespace_test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/filters/namespace" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var mutationTrackerStub = filtertest_test.MutationTrackerStub{} + +var tests = []TestCase{ + { + name: "add", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: foo +`, + filter: namespace.Filter{Namespace: "foo"}, + }, + + { + name: "null_ns", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: null +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: null +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: foo +`, + filter: namespace.Filter{Namespace: "foo"}, + }, + + { + name: "add-recurse", + input: ` +apiVersion: example.com/v1 +kind: Foo +--- +apiVersion: example.com/v1 +kind: Bar +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + namespace: foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + namespace: foo +`, + filter: namespace.Filter{Namespace: "foo"}, + }, + + { + name: "update", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + # update this namespace + namespace: bar +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: bar +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + # update this namespace + namespace: foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: foo +`, + filter: namespace.Filter{Namespace: "foo"}, + }, + + { + name: "update-rolebinding", + input: ` +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default + namespace: foo +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: something +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: something + namespace: foo +`, + expected: ` +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default + namespace: bar +metadata: + namespace: bar +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default + namespace: bar +metadata: + namespace: bar +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: something +metadata: + namespace: bar +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: something + namespace: foo +metadata: + namespace: bar +`, + filter: namespace.Filter{Namespace: "bar"}, + }, + + { + name: "update-clusterrolebinding", + input: ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: default +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: default + namespace: foo +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: something +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: something + namespace: foo +`, + expected: ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: default + namespace: bar +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: default + namespace: bar +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: something +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +subjects: +- name: something + namespace: foo +`, + filter: namespace.Filter{Namespace: "bar"}, + }, + + { + name: "data-fieldspecs", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: foo +a: + b: + c: foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: foo +a: + b: + c: foo +`, + filter: namespace.Filter{Namespace: "foo"}, + fsslice: []types.FieldSpec{ + { + Path: "a/b/c", + CreateIfNotPresent: true, + }, + }, + }, + + { + name: "mutation tracker", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: bar +a: + b: + c: bar +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default + namespace: bar +metadata: + namespace: bar +a: + b: + c: bar +`, + filter: namespace.Filter{Namespace: "bar"}, + fsslice: []types.FieldSpec{ + { + Path: "a/b/c", + CreateIfNotPresent: true, + }, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "bar", + NodePath: []string{"metadata", "namespace"}, + }, + { + Value: "bar", + NodePath: []string{"a", "b", "c"}, + }, + { + Value: "bar", + NodePath: []string{"metadata", "namespace"}, + }, + { + Value: "bar", + NodePath: []string{"namespace"}, + }, + { + Value: "bar", + NodePath: []string{"a", "b", "c"}, + }, + }, + }, +} + +type TestCase struct { + name string + input string + expected string + filter namespace.Filter + fsslice types.FsSlice + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg +} + +var config = builtinconfig.MakeDefaultConfig() + +func TestNamespace_Filter(t *testing.T) { + for i := range tests { + mutationTrackerStub.Reset() + test := tests[i] + test.filter.WithMutationTracker(test.mutationTracker) + t.Run(test.name, func(t *testing.T) { + test.filter.FsSlice = append(config.NameSpace, test.fsslice...) + if !assert.Equal(t, + strings.TrimSpace(test.expected), + strings.TrimSpace( + filtertest_test.RunFilter(t, test.input, test.filter))) { + t.FailNow() + } + assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/doc.go new file mode 100644 index 000000000..ec4cfa821 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package namespace contains a kio.Filter implementation of the kustomize +// patchjson6902 transformer +package patchjson6902 diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/example_test.go new file mode 100644 index 000000000..994326590 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/example_test.go @@ -0,0 +1,55 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package patchjson6902 + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: bar +`)}}, + Filters: []kio.Filter{ + Filter{ + Patch: ` +- op: replace + path: /metadata/namespace + value: "ns" +`, + }, + }, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // namespace: ns + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // namespace: ns +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/patchjson6902.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/patchjson6902.go new file mode 100644 index 000000000..5749d6ddf --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/patchjson6902.go @@ -0,0 +1,65 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package patchjson6902 + +import ( + "strings" + + jsonpatch "github.com/evanphx/json-patch" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" + k8syaml "sigs.k8s.io/yaml" +) + +type Filter struct { + Patch string + + decodedPatch jsonpatch.Patch +} + +var _ kio.Filter = Filter{} + +func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + decodedPatch, err := pf.decodePatch() + if err != nil { + return nil, err + } + pf.decodedPatch = decodedPatch + return kio.FilterAll(yaml.FilterFunc(pf.run)).Filter(nodes) +} + +func (pf Filter) decodePatch() (jsonpatch.Patch, error) { + patch := pf.Patch + // If the patch doesn't look like a JSON6902 patch, we + // try to parse it to json. + if !strings.HasPrefix(pf.Patch, "[") { + p, err := k8syaml.YAMLToJSON([]byte(patch)) + if err != nil { + return nil, err + } + patch = string(p) + } + decodedPatch, err := jsonpatch.DecodePatch([]byte(patch)) + if err != nil { + return nil, err + } + return decodedPatch, nil +} + +func (pf Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + // We don't actually use the kyaml library for manipulating the + // yaml here. We just marshal it to json and rely on the + // jsonpatch library to take care of applying the patch. + // This means ordering might not be preserved with this filter. + b, err := node.MarshalJSON() + if err != nil { + return nil, err + } + res, err := pf.decodedPatch.Apply(b) + if err != nil { + return nil, err + } + err = node.UnmarshalJSON(res) + return node, err +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/patchjson6902_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/patchjson6902_test.go new file mode 100644 index 000000000..47e7edf80 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchjson6902/patchjson6902_test.go @@ -0,0 +1,173 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package patchjson6902 + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" +) + +const input = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + replica: 2 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - image: nginx + name: nginx +` + +func TestSomething(t *testing.T) { + testCases := []struct { + testName string + input string + filter Filter + expectedOutput string + }{ + { + testName: "single operation, json", + input: input, + filter: Filter{ + Patch: `[ +{"op": "replace", "path": "/spec/replica", "value": 5} +]`, + }, + expectedOutput: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + replica: 5 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - image: nginx + name: nginx +`, + }, + { + testName: "multiple operations, json", + input: input, + filter: Filter{ + Patch: `[ +{"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"}, +{"op": "add", "path": "/spec/replica", "value": 999}, +{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]} +]`, + }, + expectedOutput: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + replica: 999 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - command: + - arg1 + - arg2 + - arg3 + image: nginx + name: my-nginx +`, + }, + { + testName: "single operation, yaml", + input: input, + filter: Filter{ + Patch: ` +- op: replace + path: /spec/replica + value: 5 +`, + }, + expectedOutput: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + replica: 5 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - image: nginx + name: nginx +`, + }, + { + testName: "multiple operations, yaml", + input: input, + filter: Filter{ + Patch: ` +- op: replace + path: /spec/template/spec/containers/0/name + value: my-nginx +- op: add + path: /spec/replica + value: 999 +- op: add + path: /spec/template/spec/containers/0/command + value: + - arg1 + - arg2 + - arg3 +`, + }, + expectedOutput: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + replica: 999 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - command: + - arg1 + - arg2 + - arg3 + image: nginx + name: my-nginx +`, + }, + } + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + if !assert.Equal(t, + strings.TrimSpace(tc.expectedOutput), + strings.TrimSpace( + filtertest.RunFilter(t, tc.input, tc.filter))) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/doc.go new file mode 100644 index 000000000..1733fd8a2 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package patchstrategicmerge contains a kio.Filter implementation of the +// kustomize strategic merge patch transformer. +package patchstrategicmerge diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/example_test.go new file mode 100644 index 000000000..56bd0b2d6 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/example_test.go @@ -0,0 +1,49 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package patchstrategicmerge + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func ExampleFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + replicas: 3 +`)}}, + Filters: []kio.Filter{Filter{ + Patch: yaml.MustParse(` +spec: + template: + containers: + - image: nginx +`), + }}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // spec: + // replicas: 3 + // template: + // containers: + // - image: nginx +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge.go new file mode 100644 index 000000000..1a70d19aa --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package patchstrategicmerge + +import ( + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" + "sigs.k8s.io/kustomize/kyaml/yaml/merge2" +) + +type Filter struct { + Patch *yaml.RNode +} + +var _ kio.Filter = Filter{} + +// Filter does a strategic merge patch, which can delete nodes. +func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + var result []*yaml.RNode + for i := range nodes { + r, err := merge2.Merge( + pf.Patch, nodes[i], + yaml.MergeOptions{ + ListIncreaseDirection: yaml.MergeOptionsListPrepend, + }, + ) + if err != nil { + return nil, err + } + if r != nil { + result = append(result, r) + } + } + return result, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge_test.go new file mode 100644 index 000000000..1f96c8658 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge_test.go @@ -0,0 +1,749 @@ +package patchstrategicmerge + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestFilter(t *testing.T) { + testCases := map[string]struct { + input string + patch *yaml.RNode + expected string + }{ + "simple": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`, + patch: yaml.MustParse(`apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`), + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`, + }, + "nullMapEntry1": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + B: + C: Z +`, + patch: yaml.MustParse(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +`), + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +`, + }, + "nullMapEntry2": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +`, + patch: yaml.MustParse(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + B: + C: Z +`), + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +`, + }, + "simple patch": { + input: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +`, + patch: yaml.MustParse(` +metadata: + name: yourDeploy +`), + expected: ` +apiVersion: apps/v1 +metadata: + name: yourDeploy +kind: Deployment +`, + }, + "container patch": { + input: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + template: + spec: + containers: + - name: foo1 + - name: foo2 + - name: foo3 +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + template: + spec: + containers: + - name: foo0 +`), + expected: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + template: + spec: + containers: + - name: foo0 + - name: foo1 + - name: foo2 + - name: foo3 +`, + }, + "volumes patch": { + input: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + template: + spec: + volumes: + - name: foo1 + - name: foo2 + - name: foo3 +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + template: + spec: + volumes: + - name: foo0 +`), + expected: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + template: + spec: + volumes: + - name: foo0 + - name: foo1 + - name: foo2 + - name: foo3 +`, + }, + "nested patch": { + input: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + containers: + - name: nginx + args: + - abc +`, + patch: yaml.MustParse(` +spec: + containers: + - name: nginx + args: + - def +`), + expected: ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + containers: + - name: nginx + args: + - def +`, + }, + "remove mapping - directive": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test + $patch: delete +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: [] +`, + }, + "replace mapping - directive": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + $patch: replace + containers: + - name: new +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: new +`, + }, + "merge mapping - directive": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test1 + $patch: merge +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test1 +`, + }, + "remove list - directive": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - whatever + - $patch: delete +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: {} +`, + }, + "replace list - directive": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: replace + image: replace + - $patch: replace +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: replace + image: replace +`, + }, + "merge list - directive": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test + image: test +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test2 + image: test2 + - $patch: merge +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + template: + spec: + containers: + - name: test2 + image: test2 + - name: test + image: test +`, + }, + "list map keys - add a port, no names": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: TCP +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + - containerPort: 80 + protocol: UDP +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + - containerPort: 80 + protocol: UDP + - containerPort: 8080 + protocol: TCP +`, + }, + "list map keys - add name to port": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + - containerPort: 8080 + protocol: TCP +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + name: UDP-name-patch +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + name: UDP-name-patch + - containerPort: 8080 + protocol: TCP +`, + }, + "list map keys - replace port name": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + name: UDP-name-original + - containerPort: 8080 + protocol: TCP + name: TCP-name-original +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + name: UDP-name-patch +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 + protocol: UDP + name: UDP-name-patch + - containerPort: 8080 + protocol: TCP + name: TCP-name-original +`, + }, + "list map keys - add a port, no protocol": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 8080 +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 80 +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - image: test-image + name: test-deployment + ports: + - containerPort: 80 + - containerPort: 8080 +`, + }, + + // Test for issue #3513 + // Currently broken; when one port has only containerPort, the output + // should not merge containerPort 8301 together + // This occurs because when protocol is missing on the first port, + // the merge code uses [containerPort] as the merge key rather than + // [containerPort, protocol] + "list map keys - protocol only present on some ports": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + template: + spec: + containers: + - name: consul + image: "dashicorp/consul:1.9.1" + ports: + - containerPort: 8500 + name: http + - containerPort: 8301 + protocol: "TCP" + - containerPort: 8301 + protocol: "UDP" +`, + patch: yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment + labels: + test: label +`), + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment + labels: + test: label +spec: + template: + spec: + containers: + - name: consul + image: "dashicorp/consul:1.9.1" + ports: + - containerPort: 8500 + name: http + - containerPort: 8301 + protocol: "TCP" + - containerPort: 8301 + protocol: "UDP" +`, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + f := Filter{ + Patch: tc.patch, + } + if !assert.Equal(t, + strings.TrimSpace(tc.expected), + strings.TrimSpace( + filtertest.RunFilter(t, tc.input, f))) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/doc.go new file mode 100644 index 000000000..95236859f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package prefix contains a kio.Filter implementation of the kustomize +// PrefixTransformer. +package prefix diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/example_test.go new file mode 100644 index 000000000..eccd58837 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/example_test.go @@ -0,0 +1,47 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package prefix_test + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/filters/prefix" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`)}}, + Filters: []kio.Filter{prefix.Filter{ + Prefix: "baz-", FieldSpec: types.FieldSpec{Path: "metadata/name"}}}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: baz-instance + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: baz-instance +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/prefix.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/prefix.go new file mode 100644 index 000000000..daa375d1f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/prefix.go @@ -0,0 +1,50 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package prefix + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Filter applies resource name prefix's using the fieldSpecs +type Filter struct { + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + + FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) +} + +func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + err := node.PipeE(fieldspec.Filter{ + FieldSpec: f.FieldSpec, + SetValue: f.evaluateField, + CreateKind: yaml.ScalarNode, // Name is a ScalarNode + CreateTag: yaml.NodeTagString, + }) + return node, err +} + +func (f Filter) evaluateField(node *yaml.RNode) error { + return f.trackableSetter.SetScalar(fmt.Sprintf( + "%s%s", f.Prefix, node.YNode().Value))(node) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/prefix_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/prefix_test.go new file mode 100644 index 000000000..607aa757d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/prefix/prefix_test.go @@ -0,0 +1,154 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package prefix_test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/filters/prefix" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var mutationTrackerStub = filtertest_test.MutationTrackerStub{} + +var tests = map[string]TestCase{ + "prefix": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: foo-instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: foo-instance +`, + filter: prefix.Filter{ + Prefix: "foo-", + FieldSpec: types.FieldSpec{Path: "metadata/name"}, + }, + }, + + "data-fieldspecs": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +a: + b: + c: d +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +a: + b: + c: d +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +a: + b: + c: foo-d +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +a: + b: + c: foo-d +`, + filter: prefix.Filter{ + Prefix: "foo-", + FieldSpec: types.FieldSpec{Path: "a/b/c"}, + }, + }, + + "mutation tracker": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: foo-instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: foo-instance +`, + filter: prefix.Filter{ + Prefix: "foo-", + FieldSpec: types.FieldSpec{Path: "metadata/name"}, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "foo-instance", + NodePath: []string{"metadata", "name"}, + }, + { + Value: "foo-instance", + NodePath: []string{"metadata", "name"}, + }, + }, + }, +} + +type TestCase struct { + input string + expected string + filter prefix.Filter + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg +} + +func TestFilter(t *testing.T) { + for name := range tests { + mutationTrackerStub.Reset() + test := tests[name] + test.filter.WithMutationTracker(test.mutationTracker) + t.Run(name, func(t *testing.T) { + if !assert.Equal(t, + strings.TrimSpace(test.expected), + strings.TrimSpace( + filtertest_test.RunFilter(t, test.input, test.filter))) { + t.FailNow() + } + assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/doc.go new file mode 100644 index 000000000..ab3a01d54 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/doc.go @@ -0,0 +1,3 @@ +// Package refvar contains a kio.Filter implementation of the kustomize +// refvar transformer (find and replace $(FOO) style variables in strings). +package refvar diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/expand.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/expand.go new file mode 100644 index 000000000..3bcbd7a53 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/expand.go @@ -0,0 +1,147 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package refvar + +import ( + "fmt" + "log" + "strings" +) + +const ( + operator = '$' + referenceOpener = '(' + referenceCloser = ')' +) + +// syntaxWrap returns the input string wrapped by the expansion syntax. +func syntaxWrap(input string) string { + var sb strings.Builder + sb.WriteByte(operator) + sb.WriteByte(referenceOpener) + sb.WriteString(input) + sb.WriteByte(referenceCloser) + return sb.String() +} + +// MappingFunc maps a string to anything. +type MappingFunc func(string) interface{} + +// MakePrimitiveReplacer returns a MappingFunc that uses a map to do +// replacements, and a histogram to count map hits. +// +// Func behavior: +// +// If the input key is NOT found in the map, the key is wrapped up as +// as a variable declaration string and returned, e.g. key FOO becomes $(FOO). +// This string is presumably put back where it was found, and might get replaced +// later. +// +// If the key is found in the map, the value is returned if it is a primitive +// type (string, bool, number), and the hit is counted. +// +// If it's not a primitive type (e.g. a map, struct, func, etc.) then this +// function doesn't know what to do with it and it returns the key wrapped up +// again as if it had not been replaced. This should probably be an error. +func MakePrimitiveReplacer( + counts map[string]int, someMap map[string]interface{}) MappingFunc { + return func(key string) interface{} { + if value, ok := someMap[key]; ok { + switch typedV := value.(type) { + case string, int, int32, int64, float32, float64, bool: + counts[key]++ + return typedV + default: + // If the value is some complicated type (e.g. a map or struct), + // this function doesn't know how to jam it into a string, + // so just pretend it was a cache miss. + // Likely this should be an error instead of a silent failure, + // since the programmer passed an impossible value. + log.Printf( + "MakePrimitiveReplacer: bad replacement type=%T val=%v", + typedV, typedV) + return syntaxWrap(key) + } + } + // If unable to return the mapped variable, return it + // as it was found, and a later mapping might be able to + // replace it. + return syntaxWrap(key) + } +} + +// DoReplacements replaces variable references in the input string +// using the mapping function. +func DoReplacements(input string, mapping MappingFunc) interface{} { + var buf strings.Builder + checkpoint := 0 + for cursor := 0; cursor < len(input); cursor++ { + if input[cursor] == operator && cursor+1 < len(input) { + // Copy the portion of the input string since the last + // checkpoint into the buffer + buf.WriteString(input[checkpoint:cursor]) + + // Attempt to read the variable name as defined by the + // syntax from the input string + read, isVar, advance := tryReadVariableName(input[cursor+1:]) + + if isVar { + // We were able to read a variable name correctly; + // apply the mapping to the variable name and copy the + // bytes into the buffer + mapped := mapping(read) + if input == syntaxWrap(read) { + // Preserve the type of variable + return mapped + } + + // Variable is used in a middle of a string + buf.WriteString(fmt.Sprintf("%v", mapped)) + } else { + // Not a variable name; copy the read bytes into the buffer + buf.WriteString(read) + } + + // Advance the cursor in the input string to account for + // bytes consumed to read the variable name expression + cursor += advance + + // Advance the checkpoint in the input string + checkpoint = cursor + 1 + } + } + + // Return the buffer and any remaining unwritten bytes in the + // input string. + return buf.String() + input[checkpoint:] +} + +// tryReadVariableName attempts to read a variable name from the input +// string and returns the content read from the input, whether that content +// represents a variable name to perform mapping on, and the number of bytes +// consumed in the input string. +// +// The input string is assumed not to contain the initial operator. +func tryReadVariableName(input string) (string, bool, int) { + switch input[0] { + case operator: + // Escaped operator; return it. + return input[0:1], false, 1 + case referenceOpener: + // Scan to expression closer + for i := 1; i < len(input); i++ { + if input[i] == referenceCloser { + return input[1:i], true, i + 1 + } + } + + // Incomplete reference; return it. + return string(operator) + string(referenceOpener), false, 1 + default: + // Not the beginning of an expression, ie, an operator + // that doesn't begin an expression. Return the operator + // and the first rune in the string. + return string(operator) + string(input[0]), false, 1 + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/expand_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/expand_test.go new file mode 100644 index 000000000..badba341c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/expand_test.go @@ -0,0 +1,388 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package refvar_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/filters/refvar" +) + +type expected struct { + count int + edited string +} + +func TestPrimitiveReplacer(t *testing.T) { + varCounts := make(map[string]int) + f := MakePrimitiveReplacer( + varCounts, + map[string]interface{}{ + "FOO": "bar", + "ZOO": "$(FOO)-1", + "BLU": "$(ZOO)-2", + "EIGHT": 8, + "PI": 3.14159, + "ZINT": "$(INT)", + "BOOL": "true", + "HUGENUMBER": int64(9223372036854775807), + "CRAZYMAP": map[string]int{"crazy": 200}, + "ZBOOL": "$(BOOL)", + }) + assert.Equal(t, "$()", f("")) + assert.Equal(t, "$( )", f(" ")) + assert.Equal(t, "$(florida)", f("florida")) + assert.Equal(t, "$(0)", f("0")) + assert.Equal(t, "bar", f("FOO")) + assert.Equal(t, "bar", f("FOO")) + assert.Equal(t, "bar", f("FOO")) + assert.Equal(t, 8, f("EIGHT")) + assert.Equal(t, 8, f("EIGHT")) + assert.Equal(t, 3.14159, f("PI")) + assert.Equal(t, "true", f("BOOL")) + assert.Equal(t, int64(9223372036854775807), f("HUGENUMBER")) + assert.Equal(t, "$(FOO)-1", f("ZOO")) + assert.Equal(t, "$(CRAZYMAP)", f("CRAZYMAP")) + assert.Equal(t, + map[string]int{ + "FOO": 3, + "EIGHT": 2, + "BOOL": 1, + "PI": 1, + "ZOO": 1, + "HUGENUMBER": 1, + }, + varCounts) +} + +func TestMapReference(t *testing.T) { + type env struct { + Name string + Value interface{} + } + envs := []env{ + { + Name: "FOO", + Value: "bar", + }, + { + Name: "ZOO", + Value: "$(FOO)-1", + }, + { + Name: "BLU", + Value: "$(ZOO)-2", + }, + { + Name: "INT", + Value: 2, + }, + { + Name: "ZINT", + Value: "$(INT)", + }, + { + Name: "BOOL", + Value: true, + }, + { + Name: "ZBOOL", + Value: "$(BOOL)", + }, + } + + varMap := map[string]interface{}{ + "FOO": "bar", + "ZOO": "$(FOO)-1", + "BLU": "$(ZOO)-2", + "INT": "2", + "ZINT": "$(INT)", + "BOOL": "true", + "ZBOOL": "$(BOOL)", + } + + varCounts := make(map[string]int) + for _, env := range envs { + varMap[env.Name] = DoReplacements( + fmt.Sprintf("%v", env.Value), + MakePrimitiveReplacer(varCounts, varMap)) + } + + expectedEnv := map[string]expected{ + "FOO": {count: 1, edited: "bar"}, + "ZOO": {count: 1, edited: "bar-1"}, + "BLU": {count: 0, edited: "bar-1-2"}, + "INT": {count: 1, edited: "2"}, + "ZINT": {count: 0, edited: "2"}, + "BOOL": {count: 1, edited: "true"}, + "ZBOOL": {count: 0, edited: "true"}, + } + + for k, v := range expectedEnv { + if e, a := v, varMap[k]; e.edited != a || e.count != varCounts[k] { + t.Errorf("Expected %v count=%d, got %v count=%d", + e.edited, e.count, a, varCounts[k]) + } else { + delete(varMap, k) + } + } + + if len(varMap) != 0 { + t.Errorf("Unexpected keys in declared env: %v", varMap) + } +} + +func TestMapping(t *testing.T) { + cases := []struct { + name string + input string + expected string + counts map[string]int + }{ + { + name: "whole string", + input: "$(VAR_A)", + expected: "A", + counts: map[string]int{"VAR_A": 1}, + }, + { + name: "repeat", + input: "$(VAR_A)-$(VAR_A)", + expected: "A-A", + counts: map[string]int{"VAR_A": 2}, + }, + { + name: "multiple repeats", + input: "$(VAR_A)-$(VAR_B)-$(VAR_B)-$(VAR_B)-$(VAR_A)", + expected: "A-B-B-B-A", + counts: map[string]int{"VAR_A": 2, "VAR_B": 3}, + }, + { + name: "beginning", + input: "$(VAR_A)-1", + expected: "A-1", + counts: map[string]int{"VAR_A": 1}, + }, + { + name: "middle", + input: "___$(VAR_B)___", + expected: "___B___", + counts: map[string]int{"VAR_B": 1}, + }, + { + name: "end", + input: "___$(VAR_C)", + expected: "___C", + counts: map[string]int{"VAR_C": 1}, + }, + { + name: "compound", + input: "$(VAR_A)_$(VAR_B)_$(VAR_C)", + expected: "A_B_C", + counts: map[string]int{"VAR_A": 1, "VAR_B": 1, "VAR_C": 1}, + }, + { + name: "escape & expand", + input: "$$(VAR_B)_$(VAR_A)", + expected: "$(VAR_B)_A", + counts: map[string]int{"VAR_A": 1}, + }, + { + name: "compound escape", + input: "$$(VAR_A)_$$(VAR_B)", + expected: "$(VAR_A)_$(VAR_B)", + }, + { + name: "mixed in escapes", + input: "f000-$$VAR_A", + expected: "f000-$VAR_A", + }, + { + name: "backslash escape ignored", + input: "foo\\$(VAR_C)bar", + expected: "foo\\Cbar", + counts: map[string]int{"VAR_C": 1}, + }, + { + name: "backslash escape ignored", + input: "foo\\\\$(VAR_C)bar", + expected: "foo\\\\Cbar", + counts: map[string]int{"VAR_C": 1}, + }, + { + name: "lots of backslashes", + input: "foo\\\\\\\\$(VAR_A)bar", + expected: "foo\\\\\\\\Abar", + counts: map[string]int{"VAR_A": 1}, + }, + { + name: "nested var references", + input: "$(VAR_A$(VAR_B))", + expected: "$(VAR_A$(VAR_B))", + }, + { + name: "nested var references second type", + input: "$(VAR_A$(VAR_B)", + expected: "$(VAR_A$(VAR_B)", + }, + { + name: "value is a reference", + input: "$(VAR_REF)", + expected: "$(VAR_A)", + counts: map[string]int{"VAR_REF": 1}, + }, + { + name: "value is a reference x 2", + input: "%%$(VAR_REF)--$(VAR_REF)%%", + expected: "%%$(VAR_A)--$(VAR_A)%%", + counts: map[string]int{"VAR_REF": 2}, + }, + { + name: "empty var", + input: "foo$(VAR_EMPTY)bar", + expected: "foobar", + counts: map[string]int{"VAR_EMPTY": 1}, + }, + { + name: "unterminated expression", + input: "foo$(VAR_Awhoops!", + expected: "foo$(VAR_Awhoops!", + }, + { + name: "expression without operator", + input: "f00__(VAR_A)__", + expected: "f00__(VAR_A)__", + }, + { + name: "shell special vars pass through", + input: "$?_boo_$!", + expected: "$?_boo_$!", + }, + { + name: "bare operators are ignored", + input: "$VAR_A", + expected: "$VAR_A", + }, + { + name: "undefined vars are passed through", + input: "$(VAR_DNE)", + expected: "$(VAR_DNE)", + }, + { + name: "multiple (even) operators, var undefined", + input: "$$$$$$(BIG_MONEY)", + expected: "$$$(BIG_MONEY)", + }, + { + name: "multiple (even) operators, var defined", + input: "$$$$$$(VAR_A)", + expected: "$$$(VAR_A)", + }, + { + name: "multiple (odd) operators, var undefined", + input: "$$$$$$$(GOOD_ODDS)", + expected: "$$$$(GOOD_ODDS)", + }, + { + name: "multiple (odd) operators, var defined", + input: "$$$$$$$(VAR_A)", + expected: "$$$A", + counts: map[string]int{"VAR_A": 1}, + }, + { + name: "missing open expression", + input: "$VAR_A)", + expected: "$VAR_A)", + }, + { + name: "shell syntax ignored", + input: "${VAR_A}", + expected: "${VAR_A}", + }, + { + name: "trailing incomplete expression not consumed", + input: "$(VAR_B)_______$(A", + expected: "B_______$(A", + counts: map[string]int{"VAR_B": 1}, + }, + { + name: "trailing incomplete expression, no content, is not consumed", + input: "$(VAR_C)_______$(", + expected: "C_______$(", + counts: map[string]int{"VAR_C": 1}, + }, + { + name: "operator at end of input string is preserved", + input: "$(VAR_A)foobarzab$", + expected: "Afoobarzab$", + counts: map[string]int{"VAR_A": 1}, + }, + { + name: "shell escaped incomplete expr", + input: "foo-\\$(VAR_A", + expected: "foo-\\$(VAR_A", + }, + { + name: "lots of $( in middle", + input: "--$($($($($--", + expected: "--$($($($($--", + }, + { + name: "lots of $( in beginning", + input: "$($($($($--foo$(", + expected: "$($($($($--foo$(", + }, + { + name: "lots of $( at end", + input: "foo0--$($($($(", + expected: "foo0--$($($($(", + }, + { + name: "escaped operators in variable names are not escaped", + input: "$(foo$$var)", + expected: "$(foo$$var)", + }, + { + name: "newline not expanded", + input: "\n", + expected: "\n", + }, + } + for _, tc := range cases { + counts := make(map[string]int) + expanded := DoReplacements( + fmt.Sprintf("%v", tc.input), + MakePrimitiveReplacer(counts, map[string]interface{}{ + "VAR_A": "A", + "VAR_B": "B", + "VAR_C": "C", + "VAR_REF": "$(VAR_A)", + "VAR_EMPTY": "", + })) + if e, a := tc.expected, expanded; e != a { + t.Errorf("%v: expected %q, got %q", tc.name, e, a) + } + if len(counts) != len(tc.counts) { + t.Errorf("%v: len(counts)=%d != len(tc.counts)=%d", + tc.name, len(counts), len(tc.counts)) + } + if len(tc.counts) > 0 { + for k, expectedCount := range tc.counts { + if c, ok := counts[k]; ok { + if c != expectedCount { + t.Errorf( + "%v: k=%s, expected count %d, got %d", + tc.name, k, expectedCount, c) + } + } else { + t.Errorf( + "%v: k=%s, expected count %d, got zero", + tc.name, k, expectedCount) + } + } + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/refvar.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/refvar.go new file mode 100644 index 000000000..016c574ec --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/refvar.go @@ -0,0 +1,110 @@ +package refvar + +import ( + "fmt" + "strconv" + + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Filter updates $(VAR) style variables with values. +// The fieldSpecs are the places to look for occurrences of $(VAR). +type Filter struct { + MappingFunc MappingFunc `json:"mappingFunc,omitempty" yaml:"mappingFunc,omitempty"` + FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) +} + +func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + err := node.PipeE(fieldspec.Filter{ + FieldSpec: f.FieldSpec, + SetValue: f.set, + }) + return node, err +} + +func (f Filter) set(node *yaml.RNode) error { + if yaml.IsMissingOrNull(node) { + return nil + } + switch node.YNode().Kind { + case yaml.ScalarNode: + return f.setScalar(node) + case yaml.MappingNode: + return f.setMap(node) + case yaml.SequenceNode: + return f.setSeq(node) + default: + return fmt.Errorf("invalid type encountered %v", node.YNode().Kind) + } +} + +func updateNodeValue(node *yaml.Node, newValue interface{}) { + switch newValue := newValue.(type) { + case int: + node.Value = strconv.FormatInt(int64(newValue), 10) + node.Tag = yaml.NodeTagInt + case int32: + node.Value = strconv.FormatInt(int64(newValue), 10) + node.Tag = yaml.NodeTagInt + case int64: + node.Value = strconv.FormatInt(newValue, 10) + node.Tag = yaml.NodeTagInt + case bool: + node.SetString(strconv.FormatBool(newValue)) + node.Tag = yaml.NodeTagBool + case float32: + node.SetString(strconv.FormatFloat(float64(newValue), 'f', -1, 32)) + node.Tag = yaml.NodeTagFloat + case float64: + node.SetString(strconv.FormatFloat(newValue, 'f', -1, 64)) + node.Tag = yaml.NodeTagFloat + default: + node.SetString(newValue.(string)) + node.Tag = yaml.NodeTagString + } + node.Style = 0 +} + +func (f Filter) setScalar(node *yaml.RNode) error { + if !yaml.IsYNodeString(node.YNode()) { + return nil + } + v := DoReplacements(node.YNode().Value, f.MappingFunc) + updateNodeValue(node.YNode(), v) + return nil +} + +func (f Filter) setMap(node *yaml.RNode) error { + contents := node.YNode().Content + for i := 0; i < len(contents); i += 2 { + if !yaml.IsYNodeString(contents[i]) { + return fmt.Errorf( + "invalid map key: value='%s', tag='%s'", + contents[i].Value, contents[i].Tag) + } + if !yaml.IsYNodeString(contents[i+1]) { + continue + } + newValue := DoReplacements(contents[i+1].Value, f.MappingFunc) + updateNodeValue(contents[i+1], newValue) + } + return nil +} + +func (f Filter) setSeq(node *yaml.RNode) error { + for _, item := range node.YNode().Content { + if !yaml.IsYNodeString(item) { + return fmt.Errorf("invalid value type expect a string") + } + newValue := DoReplacements(item.Value, f.MappingFunc) + updateNodeValue(item, newValue) + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/refvar_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/refvar_test.go new file mode 100644 index 000000000..c80dc79bd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/refvar/refvar_test.go @@ -0,0 +1,280 @@ +package refvar_test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/filters/refvar" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var makeMf = func(theMap map[string]interface{}) MappingFunc { + ignored := make(map[string]int) + return MakePrimitiveReplacer(ignored, theMap) +} + +func TestFilter(t *testing.T) { + + testCases := map[string]struct { + input string + expected string + filter Filter + }{ + "simple scalar": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: $(VAR)`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 5`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "VAR": int64(5), + }), + FieldSpec: types.FieldSpec{Path: "spec/replicas"}, + }, + }, + "non-string scalar": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 1`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 1`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "VAR": int64(5), + }), + FieldSpec: types.FieldSpec{Path: "spec/replicas"}, + }, + }, + "wrong path": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 1`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 1`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "VAR": int64(5), + }), + FieldSpec: types.FieldSpec{Path: "a/b/c"}, + }, + }, + "sequence": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: +- $(FOO) +- $(BAR) +- $(BAZ) +- $(FOO)+$(BAR) +- $(BOOL) +- $(FLOAT)`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: +- foo +- bar +- $(BAZ) +- foo+bar +- false +- 1.23`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "FOO": "foo", + "BAR": "bar", + "BOOL": false, + "FLOAT": 1.23, + }), + FieldSpec: types.FieldSpec{Path: "data"}, + }, + }, + "maps": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + FOO: $(FOO) + BAR: $(BAR) + BAZ: $(BAZ) + PLUS: $(FOO)+$(BAR)`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + FOO: foo + BAR: bar + BAZ: $(BAZ) + PLUS: foo+bar`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "FOO": "foo", + "BAR": "bar", + }), + FieldSpec: types.FieldSpec{Path: "data"}, + }, + }, + "complicated case": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + slice1: + - $(FOO) + slice2: + FOO: $(FOO) + BAR: $(BAR) + BOOL: false + INT: 0 + SLICE: + - $(FOO)`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + slice1: + - $(FOO) + slice2: + FOO: foo + BAR: bar + BOOL: false + INT: 0 + SLICE: + - $(FOO)`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "FOO": "foo", + "BAR": "bar", + }), + FieldSpec: types.FieldSpec{Path: "data/slice2"}, + }, + }, + "null value": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + FOO: null`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + FOO: null`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + // no replacements! + }), + FieldSpec: types.FieldSpec{Path: "data/FOO"}, + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + if !assert.Equal(t, + strings.TrimSpace(tc.expected), + strings.TrimSpace( + filtertest_test.RunFilter(t, tc.input, tc.filter))) { + t.FailNow() + } + }) + } +} + +func TestFilterUnhappy(t *testing.T) { + testCases := map[string]struct { + input string + expectedError string + filter Filter + }{ + "non-string in sequence": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + slice: + - false`, + expectedError: `considering field 'data/slice' of object Deployment.v1.apps/dep.[noNs]: invalid value type expect a string`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "VAR": int64(5), + }), + FieldSpec: types.FieldSpec{Path: "data/slice"}, + }, + }, + "invalid key in map": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +data: + 1: str`, + expectedError: `considering field 'data' of object Deployment.v1.apps/dep.[noNs]: invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`, + filter: Filter{ + MappingFunc: makeMf(map[string]interface{}{ + "VAR": int64(5), + }), + FieldSpec: types.FieldSpec{Path: "data"}, + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + _, err := filtertest_test.RunFilterE(t, tc.input, tc.filter) + if !assert.EqualError(t, err, tc.expectedError) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/doc.go new file mode 100644 index 000000000..9d9357905 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/doc.go @@ -0,0 +1,4 @@ +// Package replacement contains a kio.Filter implementation of the kustomize +// replacement transformer (accepts sources and looks for targets to replace +// their values with values from the sources). +package replacement diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/example_test.go new file mode 100644 index 000000000..c55956337 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/example_test.go @@ -0,0 +1,68 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package replacement + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func ExampleFilter() { + f := Filter{} + err := yaml.Unmarshal([]byte(` +replacements: +- source: + kind: Foo2 + fieldPath: spec.replicas + targets: + - select: + kind: Foo1 + fieldPaths: + - spec.replicas`), &f) + if err != nil { + log.Fatal(err) + } + + err = kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo1 +metadata: + name: instance +spec: + replicas: 3 +--- +apiVersion: example.com/v1 +kind: Foo2 +metadata: + name: instance +spec: + replicas: 99 +`)}}, + Filters: []kio.Filter{f}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo1 + // metadata: + // name: instance + // spec: + // replicas: 99 + // --- + // apiVersion: example.com/v1 + // kind: Foo2 + // metadata: + // name: instance + // spec: + // replicas: 99 +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/replacement.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/replacement.go new file mode 100644 index 000000000..bff8bba41 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/replacement.go @@ -0,0 +1,221 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package replacement + +import ( + "fmt" + "strings" + + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type Filter struct { + Replacements []types.Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"` +} + +// Filter replaces values of targets with values from sources +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + for _, r := range f.Replacements { + if r.Source == nil || r.Targets == nil { + return nil, fmt.Errorf("replacements must specify a source and at least one target") + } + value, err := getReplacement(nodes, &r) + if err != nil { + return nil, err + } + nodes, err = applyReplacement(nodes, value, r.Targets) + if err != nil { + return nil, err + } + } + return nodes, nil +} + +func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.TargetSelector) ([]*yaml.RNode, error) { + for _, t := range targets { + if t.Select == nil { + return nil, fmt.Errorf("target must specify resources to select") + } + if len(t.FieldPaths) == 0 { + t.FieldPaths = []string{types.DefaultReplacementFieldPath} + } + for _, n := range nodes { + ids, err := utils.MakeResIds(n) + if err != nil { + return nil, err + } + + // filter targets by label and annotation selectors + selectByAnnoAndLabel, err := selectByAnnoAndLabel(n, t) + if err != nil { + return nil, err + } + if !selectByAnnoAndLabel { + continue + } + + // filter targets by matching resource IDs + for _, id := range ids { + if id.IsSelectedBy(t.Select.ResId) && !rejectId(t.Reject, &id) { + err := applyToNode(n, value, t) + if err != nil { + return nil, err + } + break + } + } + } + } + return nodes, nil +} + +func selectByAnnoAndLabel(n *yaml.RNode, t *types.TargetSelector) (bool, error) { + if matchesSelect, err := matchesAnnoAndLabelSelector(n, t.Select); !matchesSelect || err != nil { + return false, err + } + for _, reject := range t.Reject { + if reject.AnnotationSelector == "" && reject.LabelSelector == "" { + continue + } + if m, err := matchesAnnoAndLabelSelector(n, reject); m || err != nil { + return false, err + } + } + return true, nil +} + +func matchesAnnoAndLabelSelector(n *yaml.RNode, selector *types.Selector) (bool, error) { + r := resource.Resource{RNode: *n} + annoMatch, err := r.MatchesAnnotationSelector(selector.AnnotationSelector) + if err != nil { + return false, err + } + labelMatch, err := r.MatchesLabelSelector(selector.LabelSelector) + if err != nil { + return false, err + } + return annoMatch && labelMatch, nil +} + +func rejectId(rejects []*types.Selector, id *resid.ResId) bool { + for _, r := range rejects { + if !r.ResId.IsEmpty() && id.IsSelectedBy(r.ResId) { + return true + } + } + return false +} + +func applyToNode(node *yaml.RNode, value *yaml.RNode, target *types.TargetSelector) error { + for _, fp := range target.FieldPaths { + fieldPath := utils.SmarterPathSplitter(fp, ".") + var t *yaml.RNode + var err error + if target.Options != nil && target.Options.Create { + t, err = node.Pipe(yaml.LookupCreate(value.YNode().Kind, fieldPath...)) + } else { + t, err = node.Pipe(yaml.Lookup(fieldPath...)) + } + if err != nil { + return err + } + if t != nil { + if err = setTargetValue(target.Options, t, value); err != nil { + return err + } + } + } + return nil +} + +func setTargetValue(options *types.FieldOptions, t *yaml.RNode, value *yaml.RNode) error { + value = value.Copy() + if options != nil && options.Delimiter != "" { + if t.YNode().Kind != yaml.ScalarNode { + return fmt.Errorf("delimiter option can only be used with scalar nodes") + } + tv := strings.Split(t.YNode().Value, options.Delimiter) + v := yaml.GetValue(value) + // TODO: Add a way to remove an element + switch { + case options.Index < 0: // prefix + tv = append([]string{v}, tv...) + case options.Index >= len(tv): // suffix + tv = append(tv, v) + default: // replace an element + tv[options.Index] = v + } + value.YNode().Value = strings.Join(tv, options.Delimiter) + } + t.SetYNode(value.YNode()) + return nil +} + +func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, error) { + source, err := selectSourceNode(nodes, r.Source) + if err != nil { + return nil, err + } + + if r.Source.FieldPath == "" { + r.Source.FieldPath = types.DefaultReplacementFieldPath + } + fieldPath := utils.SmarterPathSplitter(r.Source.FieldPath, ".") + + rn, err := source.Pipe(yaml.Lookup(fieldPath...)) + if err != nil { + return nil, err + } + if rn.IsNilOrEmpty() { + return nil, fmt.Errorf("fieldPath `%s` is missing for replacement source %s", r.Source.FieldPath, r.Source.ResId) + } + + return getRefinedValue(r.Source.Options, rn) +} + +func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, error) { + if options == nil || options.Delimiter == "" { + return rn, nil + } + if rn.YNode().Kind != yaml.ScalarNode { + return nil, fmt.Errorf("delimiter option can only be used with scalar nodes") + } + value := strings.Split(yaml.GetValue(rn), options.Delimiter) + if options.Index >= len(value) || options.Index < 0 { + return nil, fmt.Errorf("options.index %d is out of bounds for value %s", options.Index, yaml.GetValue(rn)) + } + n := rn.Copy() + n.YNode().Value = value[options.Index] + return n, nil +} + +// selectSourceNode finds the node that matches the selector, returning +// an error if multiple or none are found +func selectSourceNode(nodes []*yaml.RNode, selector *types.SourceSelector) (*yaml.RNode, error) { + var matches []*yaml.RNode + for _, n := range nodes { + ids, err := utils.MakeResIds(n) + if err != nil { + return nil, err + } + for _, id := range ids { + if id.IsSelectedBy(selector.ResId) { + if len(matches) > 0 { + return nil, fmt.Errorf( + "multiple matches for selector %s", selector) + } + matches = append(matches, n) + break + } + } + } + if len(matches) == 0 { + return nil, fmt.Errorf("nothing selected by %s", selector) + } + return matches[0], nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/replacement_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/replacement_test.go new file mode 100644 index 000000000..b06b074f4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replacement/replacement_test.go @@ -0,0 +1,1839 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package replacement + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/yaml" +) + +func TestFilter(t *testing.T) { + testCases := map[string]struct { + input string + replacements string + expected string + expectedErr string + }{ + "simple": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + name: deploy + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +`, + }, + "complex type": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: {} +`, + replacements: `replacements: +- source: + kind: Pod + name: pod + fieldPath: spec.containers + targets: + - select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers +`, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: + - image: busybox + name: myapp-container +`, + }, + "from ConfigMap": { + input: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy + labels: + foo: bar +spec: + template: + metadata: + labels: + foo: bar + spec: + containers: + - name: command-demo-container + image: debian + command: ["printenv"] + args: + - HOSTNAME + - PORT + - name: busybox + image: busybox:latest + args: + - echo + - HOSTNAME + - PORT +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm +data: + HOSTNAME: example.com + PORT: 8080 +`, + replacements: `replacements: +- source: + kind: ConfigMap + name: cm + fieldPath: data.HOSTNAME + targets: + - select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers.0.args.0 + - spec.template.spec.containers.1.args.1 +- source: + kind: ConfigMap + name: cm + fieldPath: data.PORT + targets: + - select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers.0.args.1 + - spec.template.spec.containers.1.args.2 +`, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy + labels: + foo: bar +spec: + template: + metadata: + labels: + foo: bar + spec: + containers: + - name: command-demo-container + image: debian + command: ["printenv"] + args: + - example.com + - 8080 + - name: busybox + image: busybox:latest + args: + - echo + - example.com + - 8080 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm +data: + HOSTNAME: example.com + PORT: 8080 +`, + }, + "multiple matches for source select": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + targets: + - select: + kind: Deployment +`, + expectedErr: "multiple matches for selector Deployment.[noVer].[noGrp]/[noName].[noNs]", + }, + "replacement has no source": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- targets: + - select: + kind: Deployment +`, + expectedErr: "replacements must specify a source and at least one target", + }, + "field paths with key-value pairs": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy2 + fieldPath: spec.template.spec.containers.[name=nginx-tagged].image + targets: + - select: + kind: Deployment + name: deploy1 + fieldPaths: + - spec.template.spec.containers.[name=postgresdb].image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "select by group and version": { + input: `apiVersion: my-group-1/v1 +kind: Custom +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group-2/v2 +kind: Custom +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group-3/v3 +kind: Custom +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + group: my-group-2 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + version: v3 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: my-group-1/v1 +kind: Custom +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group-2/v2 +kind: Custom +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group-3/v3 +kind: Custom +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +`, + }, + // regression test for missing metadata handling + "missing metadata": { + input: `spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group/v1 +kind: Custom +metadata: + name: my-name-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group/v1 +kind: Custom +metadata: + name: my-name-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + name: my-name-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + name: my-name-2 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group/v1 +kind: Custom +metadata: + name: my-name-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: my-group/v1 +kind: Custom +metadata: + name: my-name-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +`, + }, + "reject 1": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy2 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + reject: + - name: deploy2 + - name: deploy3 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "reject 2": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: StatefulSet +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + version: v1 + reject: + - kind: Deployment + name: my-name + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: StatefulSet +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +`, + }, + // the only difference in the inputs between this and the previous test + // is the dash before `name: my-name` on line 733 + "reject 3": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: StatefulSet +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + version: v1 + reject: + - kind: Deployment + - name: my-name + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: StatefulSet +metadata: + name: my-name +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "partial string replacement - replace": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy2 + fieldPath: spec.template.spec.containers.0.image + options: + delimiter: ':' + targets: + - select: + kind: Deployment + name: deploy1 + fieldPaths: + - spec.template.spec.containers.1.image + options: + delimiter: ':' +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "partial string replacement - prefix": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group/config +`, + replacements: `replacements: +- source: + kind: Pod + name: pod1 + fieldPath: spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 0 + targets: + - select: + kind: Pod + name: pod2 + fieldPaths: + - spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: -1 +`, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group/config +`, + }, + "partial string replacement - suffix": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group/config +`, + replacements: `replacements: +- source: + kind: Pod + name: pod2 + fieldPath: spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 1 + targets: + - select: + kind: Pod + name: pod1 + fieldPaths: + - spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 2 +`, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group/config +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group/config +`, + }, + "partial string replacement - last element": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group1 +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group2 +`, + replacements: `replacements: +- source: + kind: Pod + name: pod2 + fieldPath: spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 0 + targets: + - select: + kind: Pod + name: pod1 + fieldPaths: + - spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 1 +`, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group2 +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group2 +`, + }, + "partial string replacement - first element": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group1/config +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group2 +`, + replacements: `replacements: +- source: + kind: Pod + name: pod2 + fieldPath: spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 0 + targets: + - select: + kind: Pod + name: pod1 + fieldPaths: + - spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 0 +`, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group2/config +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group2 +`, + }, + "options.index out of bounds": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod1 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: my/group1 +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod2 +spec: + volumes: + - projected: + sources: + - configMap: + name: myconfigmap + items: + - key: config + path: group2 +`, + replacements: `replacements: +- source: + kind: Pod + name: pod2 + fieldPath: spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: -1 + targets: + - select: + kind: Pod + name: pod1 + fieldPaths: + - spec.volumes.0.projected.sources.0.configMap.items.0.path + options: + delimiter: '/' + index: 1 +`, + expectedErr: "options.index -1 is out of bounds for value group2", + }, + "create": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +`, + replacements: `replacements: +- source: + kind: Pod + name: pod + fieldPath: spec.containers + targets: + - select: + name: deploy1 + fieldPaths: + - spec.template.spec.containers + options: + create: true +- source: + kind: Pod + name: pod + fieldPath: spec.containers + targets: + - select: + name: deploy2 + fieldPaths: + - spec.template.spec.containers +`, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +`, + }, + "complex type with delimiter in source": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: {} +`, + replacements: `replacements: +- source: + kind: Pod + name: pod + fieldPath: spec.containers + options: + delimiter: "/" + targets: + - select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers +`, + expectedErr: "delimiter option can only be used with scalar nodes", + }, + "complex type with delimiter in target": { + input: `apiVersion: v1 +kind: Pod +metadata: + name: pod +spec: + containers: + - image: busybox + name: myapp-container +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy2 +spec: + template: + spec: + containers: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy3 +spec: + template: + spec: + containers: {} +`, + replacements: `replacements: +- source: + kind: Pod + name: pod + fieldPath: spec.containers + targets: + - select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers + options: + delimiter: "/" +`, + expectedErr: "delimiter option can only be used with scalar nodes", + }, + "mapping value contains '.' character": { + input: `apiVersion: v1 +kind: Custom +metadata: + name: custom + annotations: + a.b.c/d-e: source + f.g.h/i-j: target +`, + replacements: `replacements: +- source: + name: custom + fieldPath: metadata.annotations.[a.b.c/d-e] + targets: + - select: + name: custom + fieldPaths: + - metadata.annotations.[f.g.h/i-j] +`, + expected: `apiVersion: v1 +kind: Custom +metadata: + name: custom + annotations: + a.b.c/d-e: source + f.g.h/i-j: source +`, + }, + "list index contains '.' character": { + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: source +data: + value: example +--- +apiVersion: kubernetes-client.io/v1 +kind: ExternalSecret +metadata: + name: some-secret +spec: + backendType: secretsManager + data: + - key: some-prefix-replaceme + name: .first + version: latest + property: first + - key: some-prefix-replaceme + name: second + version: latest + property: second +`, + replacements: `replacements: +- source: + kind: ConfigMap + version: v1 + name: source + fieldPath: data.value + targets: + - select: + group: kubernetes-client.io + version: v1 + kind: ExternalSecret + name: some-secret + fieldPaths: + - spec.data.[name=.first].key + - spec.data.[name=second].key + options: + delimiter: "-" + index: 2 +`, + expected: `apiVersion: v1 +kind: ConfigMap +metadata: + name: source +data: + value: example +--- +apiVersion: kubernetes-client.io/v1 +kind: ExternalSecret +metadata: + name: some-secret +spec: + backendType: secretsManager + data: + - key: some-prefix-example + name: .first + version: latest + property: first + - key: some-prefix-example + name: second + version: latest + property: second`, + }, + "multiple field paths in target": { + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: source +data: + value: example +--- +apiVersion: kubernetes-client.io/v1 +kind: ExternalSecret +metadata: + name: some-secret +spec: + backendType: secretsManager + data: + - key: some-prefix-replaceme + name: first + version: latest + property: first + - key: some-prefix-replaceme + name: second + version: latest + property: second + - key: some-prefix-replaceme + name: third + version: latest + property: third +`, + replacements: `replacements: +- source: + kind: ConfigMap + version: v1 + name: source + fieldPath: data.value + targets: + - select: + group: kubernetes-client.io + version: v1 + kind: ExternalSecret + name: some-secret + fieldPaths: + - spec.data.0.key + - spec.data.1.key + - spec.data.2.key + options: + delimiter: "-" + index: 2 +`, + expected: `apiVersion: v1 +kind: ConfigMap +metadata: + name: source +data: + value: example +--- +apiVersion: kubernetes-client.io/v1 +kind: ExternalSecret +metadata: + name: some-secret +spec: + backendType: secretsManager + data: + - key: some-prefix-example + name: first + version: latest + property: first + - key: some-prefix-example + name: second + version: latest + property: second + - key: some-prefix-example + name: third + version: latest + property: third +`, + }, + "using a previous ID": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: pre-deploy + annotations: + internal.config.kubernetes.io/previousNames: deploy,deploy + internal.config.kubernetes.io/previousKinds: CronJob,Deployment + internal.config.kubernetes.io/previousNamespaces: default,default +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + replacements: `replacements: +- source: + kind: CronJob + name: deploy + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + name: deploy + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: pre-deploy + annotations: + internal.config.kubernetes.io/previousNames: deploy,deploy + internal.config.kubernetes.io/previousKinds: CronJob,Deployment + internal.config.kubernetes.io/previousNamespaces: default,default +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +`, + }, + + "replacement source.fieldPath does not exist": { + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: ports-from +data: + grpcPort: 8080 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: ports-to +data: + grpcPort: 8081 +`, + replacements: `replacements: +- source: + kind: ConfigMap + name: ports-from + fieldPath: data.httpPort + targets: + - select: + kind: ConfigMap + name: ports-to + fieldPaths: + - data.grpcPort + options: + create: true +`, + expectedErr: "fieldPath `data.httpPort` is missing for replacement source ConfigMap.[noVer].[noGrp]/ports-from.[noNs]", + }, + "annotationSelector": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + annotations: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + annotations: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb + +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + annotationSelector: foo=bar-1 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + annotations: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + annotations: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "labelSelector": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb + +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + labelSelector: foo=bar-1 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + "reject via labelSelector": { + input: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb + +`, + replacements: `replacements: +- source: + kind: Deployment + name: deploy-1 + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + reject: + - labelSelector: foo=bar-2 + fieldPaths: + - spec.template.spec.containers.1.image +`, + expected: `apiVersion: v1 +kind: Deployment +metadata: + name: deploy-1 + labels: + foo: bar-1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:1.7.9 + name: postgresdb +--- +apiVersion: v1 +kind: Deployment +metadata: + name: deploy-2 + labels: + foo: bar-2 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: postgres:1.8.0 + name: postgresdb +`, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + f := Filter{} + err := yaml.Unmarshal([]byte(tc.replacements), &f) + if !assert.NoError(t, err) { + t.FailNow() + } + actual, err := filtertest.RunFilterE(t, tc.input, f) + if err != nil { + if tc.expectedErr == "" { + t.Errorf("unexpected error: %s\n", err.Error()) + t.FailNow() + } + if !assert.Contains(t, err.Error(), tc.expectedErr) { + t.FailNow() + } + } + if !assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual)) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/doc.go new file mode 100644 index 000000000..a22d13034 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/doc.go @@ -0,0 +1,6 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package replicacount contains a kio.Filter implementation of the kustomize +// ReplicaCountTransformer. +package replicacount diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/example_test.go new file mode 100644 index 000000000..b72d03f57 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/example_test.go @@ -0,0 +1,62 @@ +package replicacount + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +spec: + template: + replicas: 5 +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +spec: + template: + replicas: 5 +`)}}, + Filters: []kio.Filter{Filter{ + Replica: types.Replica{ + Count: 42, + Name: "instance", + }, + FieldSpec: types.FieldSpec{ + Path: "spec/template/replicas", + }, + }}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance + // spec: + // template: + // replicas: 42 + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance + // spec: + // template: + // replicas: 42 +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/replicacount.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/replicacount.go new file mode 100644 index 000000000..84005f26a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/replicacount.go @@ -0,0 +1,45 @@ +package replicacount + +import ( + "strconv" + + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Filter updates/sets replicas fields using the fieldSpecs +type Filter struct { + Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` + FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (rc *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + rc.trackableSetter.WithMutationTracker(callback) +} + +func (rc Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(rc.run)).Filter(nodes) +} + +func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + err := node.PipeE(fieldspec.Filter{ + FieldSpec: rc.FieldSpec, + SetValue: rc.set, + CreateKind: yaml.ScalarNode, // replicas is a ScalarNode + CreateTag: yaml.NodeTagInt, + }) + return node, err +} + +func (rc Filter) set(node *yaml.RNode) error { + return rc.trackableSetter.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/replicacount_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/replicacount_test.go new file mode 100644 index 000000000..e690192fc --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/replicacount/replicacount_test.go @@ -0,0 +1,214 @@ +package replicacount + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestFilter(t *testing.T) { + mutationTrackerStub := filtertest_test.MutationTrackerStub{} + testCases := map[string]struct { + input string + expected string + filter Filter + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg + }{ + "update field": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 5 +`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "dep", + Count: 42, + }, + FieldSpec: types.FieldSpec{Path: "spec/replicas"}, + }, + }, + "add field": { + input: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something +`, + expected: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "cus", + Count: 42, + }, + FieldSpec: types.FieldSpec{ + Path: "spec/template/replicas", + CreateIfNotPresent: true, + }, + }, + }, + + "add_field_null": { + input: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something + replicas: null +`, + expected: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "cus", + Count: 42, + }, + FieldSpec: types.FieldSpec{ + Path: "spec/template/replicas", + CreateIfNotPresent: true, + }, + }, + }, + "no update if CreateIfNotPresent is false": { + input: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something +`, + expected: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something +`, + filter: Filter{ + Replica: types.Replica{ + Name: "cus", + Count: 42, + }, + FieldSpec: types.FieldSpec{ + Path: "spec/template/replicas", + }, + }, + }, + "update multiple fields": { + input: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + replicas: 5 +`, + expected: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "cus", + Count: 42, + }, + FieldSpec: types.FieldSpec{Path: "spec/template/replicas"}, + }, + }, + "mutation tracker": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 5 +`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "dep", + Count: 42, + }, + FieldSpec: types.FieldSpec{Path: "spec/replicas"}, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "42", + NodePath: []string{"spec", "replicas"}, + }, + }, + }, + } + + for tn, tc := range testCases { + mutationTrackerStub.Reset() + tc.filter.WithMutationTracker(tc.mutationTracker) + t.Run(tn, func(t *testing.T) { + if !assert.Equal(t, + strings.TrimSpace(tc.expected), + strings.TrimSpace( + filtertest_test.RunFilter(t, tc.input, tc.filter))) { + t.FailNow() + } + assert.Equal(t, tc.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/doc.go new file mode 100644 index 000000000..18be62dfd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/doc.go @@ -0,0 +1,6 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package suffix contains a kio.Filter implementation of the kustomize +// SuffixTransformer. +package suffix diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/example_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/example_test.go new file mode 100644 index 000000000..bc95b8397 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/example_test.go @@ -0,0 +1,47 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package suffix_test + +import ( + "bytes" + "log" + "os" + + "sigs.k8s.io/kustomize/api/filters/suffix" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func ExampleFilter() { + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`)}}, + Filters: []kio.Filter{suffix.Filter{ + Suffix: "-baz", FieldSpec: types.FieldSpec{Path: "metadata/name"}}}, + Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, + }.Execute() + if err != nil { + log.Fatal(err) + } + + // Output: + // apiVersion: example.com/v1 + // kind: Foo + // metadata: + // name: instance-baz + // --- + // apiVersion: example.com/v1 + // kind: Bar + // metadata: + // name: instance-baz +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/suffix.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/suffix.go new file mode 100644 index 000000000..babc257be --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/suffix.go @@ -0,0 +1,50 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package suffix + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/filters/fieldspec" + "sigs.k8s.io/kustomize/api/filters/filtersutil" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Filter applies resource name suffix's using the fieldSpecs +type Filter struct { + Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` + + FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` + + trackableSetter filtersutil.TrackableSetter +} + +var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) +} + +func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { + err := node.PipeE(fieldspec.Filter{ + FieldSpec: f.FieldSpec, + SetValue: f.evaluateField, + CreateKind: yaml.ScalarNode, // Name is a ScalarNode + CreateTag: yaml.NodeTagString, + }) + return node, err +} + +func (f Filter) evaluateField(node *yaml.RNode) error { + return f.trackableSetter.SetScalar(fmt.Sprintf( + "%s%s", node.YNode().Value, f.Suffix))(node) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/suffix_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/suffix_test.go new file mode 100644 index 000000000..53489c746 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/suffix/suffix_test.go @@ -0,0 +1,154 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package suffix_test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/filters/suffix" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var mutationTrackerStub = filtertest_test.MutationTrackerStub{} + +var tests = map[string]TestCase{ + "suffix": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance-foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance-foo +`, + filter: suffix.Filter{ + Suffix: "-foo", + FieldSpec: types.FieldSpec{Path: "metadata/name"}, + }, + }, + + "data-fieldspecs": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +a: + b: + c: d +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +a: + b: + c: d +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +a: + b: + c: d-foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +a: + b: + c: d-foo +`, + filter: suffix.Filter{ + Suffix: "-foo", + FieldSpec: types.FieldSpec{Path: "a/b/c"}, + }, + }, + + "mutation tracker": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance-foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance-foo +`, + filter: suffix.Filter{ + Suffix: "-foo", + FieldSpec: types.FieldSpec{Path: "metadata/name"}, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "instance-foo", + NodePath: []string{"metadata", "name"}, + }, + { + Value: "instance-foo", + NodePath: []string{"metadata", "name"}, + }, + }, + }, +} + +type TestCase struct { + input string + expected string + filter suffix.Filter + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg +} + +func TestFilter(t *testing.T) { + for name := range tests { + mutationTrackerStub.Reset() + test := tests[name] + test.filter.WithMutationTracker(test.mutationTracker) + t.Run(name, func(t *testing.T) { + if !assert.Equal(t, + strings.TrimSpace(test.expected), + strings.TrimSpace( + filtertest_test.RunFilter(t, test.input, test.filter))) { + t.FailNow() + } + assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/valueadd/valueadd.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/valueadd/valueadd.go new file mode 100644 index 000000000..f8e6b2f82 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/valueadd/valueadd.go @@ -0,0 +1,134 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package valueadd + +import ( + "strings" + + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// An 'Add' operation aspiring to IETF RFC 6902 JSON. +// +// The filter tries to add a value to a node at a particular field path. +// +// Kinds of target fields: +// +// - Non-existent target field. +// +// The field will be added and the value inserted. +// +// - Existing field, scalar or map. +// +// E.g. 'spec/template/spec/containers/[name:nginx]/image' +// +// This behaves like an IETF RFC 6902 Replace operation would; +// the existing value is replaced without complaint, even though +// this is an Add operation. In contrast, a Replace operation +// must fail (report an error) if the field doesn't exist. +// +// - Existing field, list (array) +// Not supported yet. +// TODO: Honor fields with RFC-6902-style array indices +// TODO: like 'spec/template/spec/containers/2' +// TODO: Modify kyaml/yaml/PathGetter to allow this. +// The value will be inserted into the array at the given position, +// shifting other contents. To instead replace an array entry, use +// an implementation of an IETF RFC 6902 Replace operation. +// +// For the common case of a filepath in the field value, and a desire +// to add the value to the filepath (rather than replace the filepath), +// use a non-zero value of FilePathPosition (see below). +type Filter struct { + // Value is the value to add. + // + // Empty values are disallowed, i.e. this filter isn't intended + // for use in erasing or removing fields. For that, use a filter + // more aligned with the IETF RFC 6902 JSON Remove operation. + // + // At the time of writing, Value's value should be a simple string, + // not a JSON document. This particular filter focuses on easing + // injection of a single-sourced cloud project and/or cluster name + // into various fields, especially namespace and various filepath + // specifications. + Value string + + // FieldPath is a JSON-style path to the field intended to hold the value. + FieldPath string + + // FilePathPosition is a filepath field index. + // + // Call the value of this field _i_. + // + // If _i_ is zero, negative or unspecified, this field has no effect. + // + // If _i_ is > 0, then it's assumed that + // - 'Value' is a string that can work as a directory or file name, + // - the field value intended for replacement holds a filepath. + // + // The filepath is split into a string slice, the value is inserted + // at position [i-1], shifting the rest of the path to the right. + // A value of i==1 puts the new value at the start of the path. + // This change never converts an absolute path to a relative path, + // meaning adding a new field at position i==1 will preserve a + // leading slash. E.g. if Value == 'PEACH' + // + // OLD : NEW : FilePathPosition + // -------------------------------------------------------- + // {empty} : PEACH : irrelevant + // / : /PEACH : irrelevant + // pie : PEACH/pie : 1 (or less to prefix) + // /pie : /PEACH/pie : 1 (or less to prefix) + // raw : raw/PEACH : 2 (or more to postfix) + // /raw : /raw/PEACH : 2 (or more to postfix) + // a/nice/warm/pie : a/nice/warm/PEACH/pie : 4 + // /a/nice/warm/pie : /a/nice/warm/PEACH/pie : 4 + // + // For robustness (liberal input, conservative output) FilePathPosition + // values that that are too large to index the split filepath result in a + // postfix rather than an error. So use 1 to prefix, 9999 to postfix. + FilePathPosition int `json:"filePathPosition,omitempty" yaml:"filePathPosition,omitempty"` +} + +var _ kio.Filter = Filter{} + +func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + _, err := kio.FilterAll(yaml.FilterFunc( + func(node *yaml.RNode) (*yaml.RNode, error) { + var fields []string + // if there is forward slash '/' in the field name, a back slash '\' + // will be used to escape it. + for _, f := range strings.Split(f.FieldPath, "/") { + if len(fields) > 0 && strings.HasSuffix(fields[len(fields)-1], "\\") { + concatField := strings.TrimSuffix(fields[len(fields)-1], "\\") + "/" + f + fields = append(fields[:len(fields)-1], concatField) + } else { + fields = append(fields, f) + } + } + // TODO: support SequenceNode. + // Presumably here one could look for array indices (digits) at + // the end of the field path (as described in IETF RFC 6902 JSON), + // and if found, take it as a signal that this should be a + // SequenceNode instead of a ScalarNode, and insert the value + // into the proper slot, shifting every over. + n, err := node.Pipe(yaml.LookupCreate(yaml.ScalarNode, fields...)) + if err != nil { + return node, err + } + // TODO: allow more kinds + if err := yaml.ErrorIfInvalid(n, yaml.ScalarNode); err != nil { + return nil, err + } + newValue := f.Value + if f.FilePathPosition > 0 { + newValue = filesys.InsertPathPart( + n.YNode().Value, f.FilePathPosition-1, newValue) + } + return n.Pipe(yaml.FieldSetter{StringValue: newValue}) + })).Filter(nodes) + return nodes, err +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/valueadd/valueadd_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/valueadd/valueadd_test.go new file mode 100644 index 000000000..8c66a72a9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/filters/valueadd/valueadd_test.go @@ -0,0 +1,123 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package valueadd + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" +) + +const someResource = ` +kind: SomeKind +spec: + resourceRef: + external: projects/whatever +` + +func TestValueAddFilter(t *testing.T) { + testCases := map[string]struct { + input string + expectedOutput string + filter Filter + }{ + "simpleAdd": { + input: ` +kind: SomeKind +`, + expectedOutput: ` +kind: SomeKind +spec: + resourceRef: + external: valueAdded +`, + filter: Filter{ + Value: "valueAdded", + FieldPath: "spec/resourceRef/external", + }, + }, + "replaceExisting": { + input: someResource, + expectedOutput: ` +kind: SomeKind +spec: + resourceRef: + external: valueAdded +`, + filter: Filter{ + Value: "valueAdded", + FieldPath: "spec/resourceRef/external", + }, + }, + "prefixExisting": { + input: someResource, + expectedOutput: ` +kind: SomeKind +spec: + resourceRef: + external: valueAdded/projects/whatever +`, + filter: Filter{ + Value: "valueAdded", + FieldPath: "spec/resourceRef/external", + FilePathPosition: 1, + }, + }, + "postfixExisting": { + input: someResource, + expectedOutput: ` +kind: SomeKind +spec: + resourceRef: + external: projects/whatever/valueAdded +`, + filter: Filter{ + Value: "valueAdded", + FieldPath: "spec/resourceRef/external", + FilePathPosition: 99, + }, + }, + "placeInMiddleOfExisting": { + input: someResource, + expectedOutput: ` +kind: SomeKind +spec: + resourceRef: + external: projects/valueAdded/whatever +`, + filter: Filter{ + Value: "valueAdded", + FieldPath: "spec/resourceRef/external", + FilePathPosition: 2, + }, + }, + "backSlash": { + input: ` +kind: SomeKind +`, + expectedOutput: ` +kind: SomeKind +spec: + resourceRef/external: valueAdded +`, + filter: Filter{ + Value: "valueAdded", + FieldPath: "spec/resourceRef\\/external", + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + filter := tc.filter + if !assert.Equal(t, + strings.TrimSpace(tc.expectedOutput), + strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/go.mod b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/go.mod new file mode 100644 index 000000000..b732d526e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/go.mod @@ -0,0 +1,16 @@ +module sigs.k8s.io/kustomize/api + +go 1.16 + +require ( + github.com/evanphx/json-patch v4.11.0+incompatible + github.com/go-errors/errors v1.0.1 + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 + github.com/imdario/mergo v0.3.5 + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.7.0 + gopkg.in/yaml.v2 v2.4.0 + k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e + sigs.k8s.io/kustomize/kyaml v0.13.3 + sigs.k8s.io/yaml v1.2.0 +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/go.sum b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/go.sum new file mode 100644 index 000000000..7cc673e2b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/go.sum @@ -0,0 +1,641 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +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/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.3/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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/kustomize/kyaml v0.13.3 h1:tNNQIC+8cc+aXFTVg+RtQAOsjwUdYBZRAgYOVI3RBc4= +sigs.k8s.io/kustomize/kyaml v0.13.3/go.mod h1:/ya3Gk4diiQzlE4mBh7wykyLRFZNvqlbh+JnwQ9Vhrc= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/hasher/hasher.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/hasher/hasher.go new file mode 100644 index 000000000..aef436d91 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/hasher/hasher.go @@ -0,0 +1,155 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package hasher + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "sort" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// SortArrayAndComputeHash sorts a string array and +// returns a hash for it +func SortArrayAndComputeHash(s []string) (string, error) { + sort.Strings(s) + data, err := json.Marshal(s) + if err != nil { + return "", err + } + return encode(hex256(string(data))) +} + +// Copied from https://github.com/kubernetes/kubernetes +// /blob/master/pkg/kubectl/util/hash/hash.go +func encode(hex string) (string, error) { + if len(hex) < 10 { + return "", fmt.Errorf( + "input length must be at least 10") + } + enc := []rune(hex[:10]) + for i := range enc { + switch enc[i] { + case '0': + enc[i] = 'g' + case '1': + enc[i] = 'h' + case '3': + enc[i] = 'k' + case 'a': + enc[i] = 'm' + case 'e': + enc[i] = 't' + } + } + return string(enc), nil +} + +// hex256 returns the hex form of the sha256 of the argument. +func hex256(data string) string { + return fmt.Sprintf("%x", sha256.Sum256([]byte(data))) +} + +// Hasher computes the hash of an RNode. +type Hasher struct{} + +// Hash returns a hash of the argument. +func (h *Hasher) Hash(node *yaml.RNode) (r string, err error) { + var encoded string + switch node.GetKind() { + case "ConfigMap": + encoded, err = encodeConfigMap(node) + case "Secret": + encoded, err = encodeSecret(node) + default: + var encodedBytes []byte + encodedBytes, err = json.Marshal(node.YNode()) + encoded = string(encodedBytes) + } + if err != nil { + return "", err + } + return encode(hex256(encoded)) +} + +func getNodeValues( + node *yaml.RNode, paths []string) (map[string]interface{}, error) { + values := make(map[string]interface{}) + for _, p := range paths { + vn, err := node.Pipe(yaml.Lookup(p)) + if err != nil { + return map[string]interface{}{}, err + } + if vn == nil { + values[p] = "" + continue + } + if vn.YNode().Kind != yaml.ScalarNode { + vs, err := vn.MarshalJSON() + if err != nil { + return map[string]interface{}{}, err + } + // data, binaryData and stringData are all maps + var v map[string]interface{} + json.Unmarshal(vs, &v) + values[p] = v + } else { + values[p] = vn.YNode().Value + } + } + return values, nil +} + +// encodeConfigMap encodes a ConfigMap. +// Data, Kind, and Name are taken into account. +// BinaryData is included if it's not empty to avoid useless key in output. +func encodeConfigMap(node *yaml.RNode) (string, error) { + // get fields + paths := []string{"metadata/name", "data", "binaryData"} + values, err := getNodeValues(node, paths) + if err != nil { + return "", err + } + m := map[string]interface{}{ + "kind": "ConfigMap", + "name": values["metadata/name"], + "data": values["data"], + } + if _, ok := values["binaryData"].(map[string]interface{}); ok { + m["binaryData"] = values["binaryData"] + } + + // json.Marshal sorts the keys in a stable order in the encoding + data, err := json.Marshal(m) + if err != nil { + return "", err + } + return string(data), nil +} + +// encodeSecret encodes a Secret. +// Data, Kind, Name, and Type are taken into account. +// StringData is included if it's not empty to avoid useless key in output. +func encodeSecret(node *yaml.RNode) (string, error) { + // get fields + paths := []string{"type", "metadata/name", "data", "stringData"} + values, err := getNodeValues(node, paths) + if err != nil { + return "", err + } + m := map[string]interface{}{"kind": "Secret", "type": values["type"], + "name": values["metadata/name"], "data": values["data"]} + if _, ok := values["stringData"].(map[string]interface{}); ok { + m["stringData"] = values["stringData"] + } + + // json.Marshal sorts the keys in a stable order in the encoding + data, err := json.Marshal(m) + if err != nil { + return "", err + } + return string(data), nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/hasher/hasher_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/hasher/hasher_test.go new file mode 100644 index 000000000..0235d409e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/hasher/hasher_test.go @@ -0,0 +1,356 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package hasher + +import ( + "strings" + "testing" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestSortArrayAndComputeHash(t *testing.T) { + array1 := []string{"a", "b", "c", "d"} + array2 := []string{"c", "b", "d", "a"} + h1, err := SortArrayAndComputeHash(array1) + if err != nil { + t.Errorf("unexpected error %v", err) + } + if h1 == "" { + t.Errorf("failed to hash %v", array1) + } + h2, err := SortArrayAndComputeHash(array2) + if err != nil { + t.Errorf("unexpected error %v", err) + } + if h2 == "" { + t.Errorf("failed to hash %v", array2) + } + if h1 != h2 { + t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2) + } +} + +func Test_hex256(t *testing.T) { + // hash the empty string to be sure that sha256 is being used + expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + sum := hex256("") + if expect != sum { + t.Errorf("expected hash %q but got %q", expect, sum) + } +} + +func TestConfigMapHash(t *testing.T) { + cases := []struct { + desc string + cmYaml string + hash string + err string + }{ + // empty map + {"empty data", ` +apiVersion: v1 +kind: ConfigMap`, "6ct58987ht", ""}, + // one key + {"one key", ` +apiVersion: v1 +kind: ConfigMap +data: + one: ""`, "9g67k2htb6", ""}, + // three keys (tests sorting order) + {"three keys", ` +apiVersion: v1 +kind: ConfigMap +data: + two: 2 + one: "" + three: 3`, "7757f9kkct", ""}, + // empty binary data map + {"empty binary data", ` +apiVersion: v1 +kind: ConfigMap`, "6ct58987ht", ""}, + // one key with binary data + {"one key with binary data", ` +apiVersion: v1 +kind: ConfigMap +binaryData: + one: ""`, "6mtk2m274t", ""}, + // three keys with binary data (tests sorting order) + {"three keys with binary data", ` +apiVersion: v1 +kind: ConfigMap +binaryData: + two: 2 + one: "" + three: 3`, "9th7kc28dg", ""}, + // two keys, one with string and another with binary data + {"two keys with one each", ` +apiVersion: v1 +kind: ConfigMap +data: + one: "" +binaryData: + two: ""`, "698h7c7t9m", ""}, + } + h := &Hasher{} + for _, c := range cases { + node, err := yaml.Parse(c.cmYaml) + if err != nil { + t.Fatal(err) + } + hashed, err := h.Hash(node) + if SkipRest(t, c.desc, err, c.err) { + continue + } + if c.hash != hashed { + t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h) + } + } +} + +func TestSecretHash(t *testing.T) { + cases := []struct { + desc string + secretYaml string + hash string + err string + }{ + // empty map + {"empty data", ` +apiVersion: v1 +kind: Secret +type: my-type`, "5gmgkf8578", ""}, + // one key + {"one key", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + one: ""`, "74bd68bm66", ""}, + // three keys (tests sorting order) + {"three keys", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + two: 2 + one: "" + three: 3`, "4gf75c7476", ""}, + // with stringdata + {"stringdata", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + one: "" +stringData: + two: 2`, "c4h4264gdb", ""}, + // empty stringdata + {"empty stringdata", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + one: ""`, "74bd68bm66", ""}, + } + h := &Hasher{} + for _, c := range cases { + node, err := yaml.Parse(c.secretYaml) + if err != nil { + t.Fatal(err) + } + hashed, err := h.Hash(node) + if SkipRest(t, c.desc, err, c.err) { + continue + } + if c.hash != hashed { + t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h) + } + } +} + +func TestBasicHash(t *testing.T) { + cases := map[string]struct { + res string + hash string + err string + }{ + "minimal": {` +apiVersion: test/v1 +kind: TestResource +metadata: + name: my-resource`, "244782mkb7", ""}, + "with spec": {` +apiVersion: test/v1 +kind: TestResource +metadata: + name: my-resource +spec: + foo: 1 + bar: abc`, "59m2mdccg4", ""}, + } + h := &Hasher{} + for n := range cases { + c := cases[n] + t.Run(n, func(t *testing.T) { + node, err := yaml.Parse(c.res) + if err != nil { + t.Fatal(err) + } + hashed, err := h.Hash(node) + if SkipRest(t, n, err, c.err) { + return + } + if c.hash != hashed { + t.Errorf("case %q, expect hash %q but got %q", n, c.hash, h) + } + }) + } +} + +func TestEncodeConfigMap(t *testing.T) { + cases := []struct { + desc string + cmYaml string + expect string + err string + }{ + // empty map + {"empty data", ` +apiVersion: v1 +kind: ConfigMap`, `{"data":"","kind":"ConfigMap","name":""}`, ""}, + // one key + {"one key", ` +apiVersion: v1 +kind: ConfigMap +data: + one: ""`, `{"data":{"one":""},"kind":"ConfigMap","name":""}`, ""}, + // three keys (tests sorting order) + {"three keys", ` +apiVersion: v1 +kind: ConfigMap +data: + two: 2 + one: "" + three: 3`, `{"data":{"one":"","three":3,"two":2},"kind":"ConfigMap","name":""}`, ""}, + // empty binary map + {"empty data", ` +apiVersion: v1 +kind: ConfigMap`, `{"data":"","kind":"ConfigMap","name":""}`, ""}, + // one key with binary data + {"one key", ` +apiVersion: v1 +kind: ConfigMap +binaryData: + one: ""`, `{"binaryData":{"one":""},"data":"","kind":"ConfigMap","name":""}`, ""}, + // three keys with binary data (tests sorting order) + {"three keys", ` +apiVersion: v1 +kind: ConfigMap +binaryData: + two: 2 + one: "" + three: 3`, `{"binaryData":{"one":"","three":3,"two":2},"data":"","kind":"ConfigMap","name":""}`, ""}, + // two keys, one string and one binary values + {"two keys with one each", ` +apiVersion: v1 +kind: ConfigMap +data: + one: "" +binaryData: + two: ""`, `{"binaryData":{"two":""},"data":{"one":""},"kind":"ConfigMap","name":""}`, ""}, + } + for _, c := range cases { + node, err := yaml.Parse(c.cmYaml) + if err != nil { + t.Fatal(err) + } + s, err := encodeConfigMap(node) + if SkipRest(t, c.desc, err, c.err) { + continue + } + if s != c.expect { + t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.cmYaml) + } + } +} + +func TestEncodeSecret(t *testing.T) { + cases := []struct { + desc string + secretYaml string + expect string + err string + }{ + // empty map + {"empty data", ` +apiVersion: v1 +kind: Secret +type: my-type`, `{"data":"","kind":"Secret","name":"","type":"my-type"}`, ""}, + // one key + {"one key", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + one: ""`, `{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""}, + // three keys (tests sorting order) - note json.Marshal base64 encodes the values because they come in as []byte + {"three keys", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + two: 2 + one: "" + three: 3`, `{"data":{"one":"","three":3,"two":2},"kind":"Secret","name":"","type":"my-type"}`, ""}, + // with stringdata + {"stringdata", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + one: "" +stringData: + two: 2`, `{"data":{"one":""},"kind":"Secret","name":"","stringData":{"two":2},"type":"my-type"}`, ""}, + // empty stringdata + {"empty stringdata", ` +apiVersion: v1 +kind: Secret +type: my-type +data: + one: ""`, `{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""}, + } + for _, c := range cases { + node, err := yaml.Parse(c.secretYaml) + if err != nil { + t.Fatal(err) + } + s, err := encodeSecret(node) + if SkipRest(t, c.desc, err, c.err) { + continue + } + if s != c.expect { + t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.secretYaml) + } + } +} + +// SkipRest returns true if there was a non-nil error or if we expected an +// error that didn't happen, and logs the appropriate error on the test object. +// The return value indicates whether we should skip the rest of the test case +// due to the error result. +func SkipRest(t *testing.T, desc string, err error, contains string) bool { + if err != nil { + if len(contains) == 0 { + t.Errorf("case %q, expect nil error but got %q", desc, err.Error()) + } else if !strings.Contains(err.Error(), contains) { + t.Errorf("case %q, expect error to contain %q but got %q", desc, contains, err.Error()) + } + return true + } else if len(contains) > 0 { + t.Errorf("case %q, expect error to contain %q but got nil error", desc, contains) + return true + } + return false +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/ifc/ifc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/ifc/ifc.go new file mode 100644 index 000000000..9fd297478 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/ifc/ifc.go @@ -0,0 +1,48 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package ifc holds miscellaneous interfaces used by kustomize. +package ifc + +import ( + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Validator provides functions to validate annotations and labels +type Validator interface { + MakeAnnotationValidator() func(map[string]string) error + MakeAnnotationNameValidator() func([]string) error + MakeLabelValidator() func(map[string]string) error + MakeLabelNameValidator() func([]string) error + ValidateNamespace(string) []string + ErrIfInvalidKey(string) error + IsEnvVarName(k string) error +} + +// KvLoader reads and validates KV pairs. +type KvLoader interface { + Validator() Validator + Load(args types.KvPairSources) (all []types.Pair, err error) +} + +// Loader interface exposes methods to read bytes. +type Loader interface { + // Root returns the root location for this Loader. + Root() string + // New returns Loader located at newRoot. + New(newRoot string) (Loader, error) + // Load returns the bytes read from the location or an error. + Load(location string) ([]byte, error) + // Cleanup cleans the loader + Cleanup() error +} + +// KustHasher returns a hash of the argument +// or an error. +type KustHasher interface { + Hash(*yaml.RNode) (string, error) +} + +// See core.v1.SecretTypeOpaque +const SecretTypeOpaque = "Opaque" diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/image/image.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/image/image.go new file mode 100644 index 000000000..059999062 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/image/image.go @@ -0,0 +1,50 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package image + +import ( + "regexp" + "strings" +) + +// IsImageMatched returns true if the value of t is identical to the +// image name in the full image name and tag as given by s. +func IsImageMatched(s, t string) bool { + // Tag values are limited to [a-zA-Z0-9_.{}-]. + // Some tools like Bazel rules_k8s allow tag patterns with {} characters. + // More info: https://github.com/bazelbuild/rules_k8s/pull/423 + pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.{}-]*)?$") + return pattern.MatchString(s) +} + +// Split separates and returns the name and tag parts +// from the image string using either colon `:` or at `@` separators. +// Note that the returned tag keeps its separator. +func Split(imageName string) (name string, tag string) { + // check if image name contains a domain + // if domain is present, ignore domain and check for `:` + ic := -1 + if slashIndex := strings.Index(imageName, "/"); slashIndex < 0 { + ic = strings.LastIndex(imageName, ":") + } else { + lastIc := strings.LastIndex(imageName[slashIndex:], ":") + // set ic only if `:` is present + if lastIc > 0 { + ic = slashIndex + lastIc + } + } + ia := strings.LastIndex(imageName, "@") + if ic < 0 && ia < 0 { + return imageName, "" + } + + i := ic + if ia > 0 { + i = ia + } + + name = imageName[:i] + tag = imageName[i:] + return +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/image/image_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/image/image_test.go new file mode 100644 index 000000000..c3526490e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/image/image_test.go @@ -0,0 +1,80 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package image + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsImageMatched(t *testing.T) { + testCases := []struct { + testName string + value string + name string + isMatched bool + }{ + { + testName: "identical", + value: "nginx", + name: "nginx", + isMatched: true, + }, + { + testName: "name is match", + value: "nginx:12345", + name: "nginx", + isMatched: true, + }, + { + testName: "name is not a match", + value: "apache:12345", + name: "nginx", + isMatched: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + assert.Equal(t, tc.isMatched, IsImageMatched(tc.value, tc.name)) + }) + } +} + +func TestSplit(t *testing.T) { + testCases := []struct { + testName string + value string + name string + tag string + }{ + { + testName: "no tag", + value: "nginx", + name: "nginx", + tag: "", + }, + { + testName: "with tag", + value: "nginx:1.2.3", + name: "nginx", + tag: ":1.2.3", + }, + { + testName: "with digest", + value: "nginx@12345", + name: "nginx", + tag: "@12345", + }, + } + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + name, tag := Split(tc.value) + assert.Equal(t, tc.name, name) + assert.Equal(t, tc.tag, tag) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/loadconfigfromcrds.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/loadconfigfromcrds.go new file mode 100644 index 000000000..cac47c0de --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/loadconfigfromcrds.go @@ -0,0 +1,198 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator + +import ( + "encoding/json" + "strings" + + "github.com/pkg/errors" + "k8s.io/kube-openapi/pkg/validation/spec" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/yaml" +) + +// OpenAPIDefinition describes single type. +// Normally these definitions are auto-generated using gen-openapi. +// Same as in k8s.io / kube-openapi / pkg / common. +type OpenAPIDefinition struct { + Schema spec.Schema + Dependencies []string +} + +type myProperties = map[string]spec.Schema +type nameToApiMap map[string]OpenAPIDefinition + +// LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig +func LoadConfigFromCRDs( + ldr ifc.Loader, paths []string) (*builtinconfig.TransformerConfig, error) { + tc := builtinconfig.MakeEmptyConfig() + for _, path := range paths { + content, err := ldr.Load(path) + if err != nil { + return nil, err + } + m, err := makeNameToApiMap(content) + if err != nil { + return nil, errors.Wrapf(err, "unable to parse open API definition from '%s'", path) + } + otherTc, err := makeConfigFromApiMap(m) + if err != nil { + return nil, err + } + tc, err = tc.Merge(otherTc) + if err != nil { + return nil, err + } + } + return tc, nil +} + +func makeNameToApiMap(content []byte) (result nameToApiMap, err error) { + if content[0] == '{' { + err = json.Unmarshal(content, &result) + } else { + err = yaml.Unmarshal(content, &result) + } + return +} + +func makeConfigFromApiMap(m nameToApiMap) (*builtinconfig.TransformerConfig, error) { + result := builtinconfig.MakeEmptyConfig() + for name, api := range m { + if !looksLikeAk8sType(api.Schema.SchemaProps.Properties) { + continue + } + tc := builtinconfig.MakeEmptyConfig() + err := loadCrdIntoConfig( + tc, makeGvkFromTypeName(name), m, name, []string{}) + if err != nil { + return result, err + } + result, err = result.Merge(tc) + if err != nil { + return result, err + } + } + return result, nil +} + +// TODO: Get Group and Version for CRD from the +// openAPI definition once +// "x-kubernetes-group-version-kind" is available in CRD +func makeGvkFromTypeName(n string) resid.Gvk { + names := strings.Split(n, filesys.SelfDir) + kind := names[len(names)-1] + return resid.Gvk{Kind: kind} +} + +func looksLikeAk8sType(properties myProperties) bool { + _, ok := properties["kind"] + if !ok { + return false + } + _, ok = properties["apiVersion"] + if !ok { + return false + } + _, ok = properties["metadata"] + return ok +} + +const ( + // "x-kubernetes-annotation": "" + xAnnotation = "x-kubernetes-annotation" + + // "x-kubernetes-label-selector": "" + xLabelSelector = "x-kubernetes-label-selector" + + // "x-kubernetes-identity": "" + xIdentity = "x-kubernetes-identity" + + // "x-kubernetes-object-ref-api-version": + xVersion = "x-kubernetes-object-ref-api-version" + + // "x-kubernetes-object-ref-kind": + xKind = "x-kubernetes-object-ref-kind" + + // "x-kubernetes-object-ref-name-key": "name" + // default is "name" + xNameKey = "x-kubernetes-object-ref-name-key" +) + +// loadCrdIntoConfig loads a CRD spec into a TransformerConfig +func loadCrdIntoConfig( + theConfig *builtinconfig.TransformerConfig, theGvk resid.Gvk, theMap nameToApiMap, + typeName string, path []string) (err error) { + api, ok := theMap[typeName] + if !ok { + return nil + } + for propName, property := range api.Schema.SchemaProps.Properties { + _, annotate := property.Extensions.GetString(xAnnotation) + if annotate { + err = theConfig.AddAnnotationFieldSpec( + makeFs(theGvk, append(path, propName))) + if err != nil { + return + } + } + _, label := property.Extensions.GetString(xLabelSelector) + if label { + err = theConfig.AddLabelFieldSpec( + makeFs(theGvk, append(path, propName))) + if err != nil { + return + } + } + _, identity := property.Extensions.GetString(xIdentity) + if identity { + err = theConfig.AddPrefixFieldSpec( + makeFs(theGvk, append(path, propName))) + if err != nil { + return + } + } + version, ok := property.Extensions.GetString(xVersion) + if ok { + kind, ok := property.Extensions.GetString(xKind) + if ok { + nameKey, ok := property.Extensions.GetString(xNameKey) + if !ok { + nameKey = "name" + } + err = theConfig.AddNamereferenceFieldSpec( + builtinconfig.NameBackReferences{ + Gvk: resid.Gvk{Kind: kind, Version: version}, + Referrers: []types.FieldSpec{ + makeFs(theGvk, append(path, propName, nameKey))}, + }) + if err != nil { + return + } + } + } + if property.Ref.GetURL() != nil { + err = loadCrdIntoConfig( + theConfig, theGvk, theMap, + property.Ref.String(), append(path, propName)) + if err != nil { + return + } + } + } + return nil +} + +func makeFs(in resid.Gvk, path []string) types.FieldSpec { + return types.FieldSpec{ + CreateIfNotPresent: false, + Gvk: in, + Path: strings.Join(path, "/"), + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/loadconfigfromcrds_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/loadconfigfromcrds_test.go new file mode 100644 index 000000000..9c180cbf9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/loadconfigfromcrds_test.go @@ -0,0 +1,176 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator_test + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" + . "sigs.k8s.io/kustomize/api/internal/accumulator" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// This defines two CRD's: Bee and MyKind. +// +// Bee is boring, it's spec has no dependencies. +// +// MyKind, however, has a spec that contains +// a Bee and a (k8s native) Secret. +const ( + crdContent = ` +{ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee": { + "Schema": { + "description": "Bee", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert +recognized schemas to the latest internal value, and may reject unrecognized values. +More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer +this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. +More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": { + "Schema": { + "description": "BeeSpec defines the desired state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": { + "Schema": { + "description": "BeeStatus defines the observed state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKind": { + "Schema": { + "description": "MyKind", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. +Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. +More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. +Servers may infer this from the endpoint the client submits requests to. Cannot be updated. +In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": { + "Schema": { + "description": "MyKindSpec defines the desired state of MyKind", + "properties": { + "beeRef": { + "x-kubernetes-object-ref-api-version": "v1beta1", + "x-kubernetes-object-ref-kind": "Bee", + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee" + }, + "secretRef": { + "description": "If defined, we use this secret for configuring the MYSQL_ROOT_PASSWORD +If it is not set we generate a secret dynamically", + "x-kubernetes-object-ref-api-version": "v1", + "x-kubernetes-object-ref-kind": "Secret", + "$ref": "k8s.io/api/core/v1.LocalObjectReference" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee", + "k8s.io/api/core/v1.LocalObjectReference" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": { + "Schema": { + "description": "MyKindStatus defines the observed state of MyKind" + }, + "Dependencies": [] + } +} +` +) + +func TestLoadCRDs(t *testing.T) { + nbrs := []builtinconfig.NameBackReferences{ + { + Gvk: resid.Gvk{Kind: "Secret", Version: "v1"}, + Referrers: []types.FieldSpec{ + { + Gvk: resid.Gvk{Kind: "MyKind"}, + Path: "spec/secretRef/name", + }, + }, + }, + { + Gvk: resid.Gvk{Kind: "Bee", Version: "v1beta1"}, + Referrers: []types.FieldSpec{ + { + Gvk: resid.Gvk{Kind: "MyKind"}, + Path: "spec/beeRef/name", + }, + }, + }, + } + + expectedTc := &builtinconfig.TransformerConfig{ + NameReference: nbrs, + } + + fSys := filesys.MakeFsInMemory() + err := fSys.WriteFile("/testpath/crd.json", []byte(crdContent)) + require.NoError(t, err) + ldr, err := loader.NewLoader(loader.RestrictionRootOnly, "/testpath", fSys) + require.NoError(t, err) + + actualTc, err := LoadConfigFromCRDs(ldr, []string{"crd.json"}) + require.NoError(t, err) + if !reflect.DeepEqual(actualTc, expectedTc) { + t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/namereferencetransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/namereferencetransformer.go new file mode 100644 index 000000000..e039db4b1 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/namereferencetransformer.go @@ -0,0 +1,165 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator + +import ( + "fmt" + "log" + + "sigs.k8s.io/kustomize/api/filters/nameref" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +type nameReferenceTransformer struct { + backRefs []builtinconfig.NameBackReferences +} + +const doDebug = false + +var _ resmap.Transformer = &nameReferenceTransformer{} + +type filterMap map[*resource.Resource][]nameref.Filter + +// newNameReferenceTransformer constructs a nameReferenceTransformer +// with a given slice of NameBackReferences. +func newNameReferenceTransformer( + br []builtinconfig.NameBackReferences) resmap.Transformer { + if br == nil { + log.Fatal("backrefs not expected to be nil") + } + return &nameReferenceTransformer{backRefs: br} +} + +// Transform updates name references in resource A that +// refer to resource B, given that B's name may have +// changed. +// +// For example, a HorizontalPodAutoscaler (HPA) +// necessarily refers to a Deployment, the thing that +// an HPA scales. In this case: +// +// - the HPA instance is the Referrer, +// - the Deployment instance is the ReferralTarget. +// +// If the Deployment's name changes, e.g. a prefix is added, +// then the HPA's reference to the Deployment must be fixed. +// +func (t *nameReferenceTransformer) Transform(m resmap.ResMap) error { + fMap := t.determineFilters(m.Resources()) + debug(fMap) + for r, fList := range fMap { + c, err := m.SubsetThatCouldBeReferencedByResource(r) + if err != nil { + return err + } + for _, f := range fList { + f.Referrer = r + f.ReferralCandidates = c + if err := f.Referrer.ApplyFilter(f); err != nil { + return err + } + } + } + return nil +} + +func debug(fMap filterMap) { + if !doDebug { + return + } + fmt.Printf("filterMap has %d entries:\n", len(fMap)) + rCount := 0 + for r, fList := range fMap { + yml, _ := r.AsYAML() + rCount++ + fmt.Printf(` +---- %3d. possible referrer ------------- +%s +---------`, rCount, string(yml), + ) + for i, f := range fList { + fmt.Printf(` +%3d/%3d update: %s + from: %s +`, rCount, i+1, f.NameFieldToUpdate.Path, f.ReferralTarget, + ) + } + } +} + +// Produce a map from referrer resources that might need to be fixed +// to filters that might fix them. The keys to this map are potential +// referrers, so won't include resources like ConfigMap or Secret. +// +// In the inner loop over the resources below, say we +// encounter an HPA instance. Then, in scanning the set +// of all known backrefs, we encounter an entry like +// +// - kind: Deployment +// fieldSpecs: +// - kind: HorizontalPodAutoscaler +// path: spec/scaleTargetRef/name +// +// This entry says that an HPA, via its +// 'spec/scaleTargetRef/name' field, may refer to a +// Deployment. +// +// This means that a filter will need to hunt for the right Deployment, +// obtain it's new name, and write that name into the HPA's +// 'spec/scaleTargetRef/name' field. Return a filter that can do that. +func (t *nameReferenceTransformer) determineFilters( + resources []*resource.Resource) (fMap filterMap) { + + // We cache the resource OrgId values because they don't change and otherwise are very visible in a memory pprof + resourceOrgIds := make([]resid.ResId, len(resources)) + for i, resource := range resources { + resourceOrgIds[i] = resource.OrgId() + } + + fMap = make(filterMap) + for _, backReference := range t.backRefs { + for _, referrerSpec := range backReference.Referrers { + for i, res := range resources { + if resourceOrgIds[i].IsSelected(&referrerSpec.Gvk) { + // If this is true, the res might be a referrer, and if + // so, the name reference it holds might need an update. + if resHasField(res, referrerSpec.Path) { + // Optimization - the referrer has the field + // that might need updating. + fMap[res] = append(fMap[res], nameref.Filter{ + // Name field to write in the Referrer. + // If the path specified here isn't found in + // the Referrer, nothing happens (no error, + // no field creation). + NameFieldToUpdate: referrerSpec, + // Specification of object class to read from. + // Always read from metadata/name field. + ReferralTarget: backReference.Gvk, + }) + } + } + } + } + } + return fMap +} + +// TODO: check res for field existence here to avoid extra work. +// res.GetFieldValue, which uses yaml.Lookup under the hood, doesn't know +// how to parse fieldspec-style paths that make no distinction +// between maps and sequences. This means it cannot lookup commonly +// used "indeterminate" paths like +// spec/containers/env/valueFrom/configMapKeyRef/name +// ('containers' is a list, not a map). +// However, the fieldspec filter does know how to handle this; +// extract that code and call it here? +func resHasField(res *resource.Resource, path string) bool { + return true + // fld := strings.Join(utils.PathSplitter(path), ".") + // _, e := res.GetFieldValue(fld) + // return e == nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/namereferencetransformer_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/namereferencetransformer_test.go new file mode 100644 index 000000000..49169db91 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/namereferencetransformer_test.go @@ -0,0 +1,1063 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator + +import ( + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +const notEqualErrFmt = "expected (self) doesn't match actual (other): %v" + +func TestNameReferenceHappyRun(t *testing.T) { + m := resmaptest_test.NewRmBuilderDefault(t).AddWithName( + "cm1", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "someprefix-cm1-somehash", + }, + }).AddWithName( + "cm2", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "someprefix-cm2-somehash", + }, + }).AddWithName( + "secret1", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "someprefix-secret1-somehash", + }, + }).AddWithName( + "claim1", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": map[string]interface{}{ + "name": "someprefix-claim1", + }, + }).Add( + map[string]interface{}{ + "group": "networking.k8s.io", + "apiVersion": "v1beta1", + "kind": "Ingress", + "metadata": map[string]interface{}{ + "name": "ingress1", + "annotations": map[string]interface{}{ + "ingress.kubernetes.io/auth-secret": "secret1", + "nginx.ingress.kubernetes.io/auth-secret": "secret1", + "nginx.ingress.kubernetes.io/auth-tls-secret": "secret1", + }, + }, + "spec": map[string]interface{}{ + "backend": map[string]interface{}{ + "serviceName": "testsvc", + "servicePort": "80", + }, + }, + }, + ).Add( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + "env": []interface{}{ + map[string]interface{}{ + "name": "CM_FOO", + "valueFrom": map[string]interface{}{ + "configMapKeyRef": map[string]interface{}{ + "name": "cm1", + "key": "somekey", + }, + }, + }, + map[string]interface{}{ + "name": "SECRET_FOO", + "valueFrom": map[string]interface{}{ + "secretKeyRef": map[string]interface{}{ + "name": "secret1", + "key": "somekey", + }, + }, + }, + }, + "envFrom": []interface{}{ + map[string]interface{}{ + "configMapRef": map[string]interface{}{ + "name": "cm1", + "key": "somekey", + }, + }, + map[string]interface{}{ + "secretRef": map[string]interface{}{ + "name": "secret1", + "key": "somekey", + }, + }, + }, + }, + }, + "imagePullSecrets": []interface{}{ + map[string]interface{}{ + "name": "secret1", + }, + }, + "volumes": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "cm1", + }, + "projected": map[string]interface{}{ + "sources": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "cm2", + }, + "secret": map[string]interface{}{ + "name": "secret1", + }, + }, + }, + "secret": map[string]interface{}{ + "secretName": "secret1", + }, + "persistentVolumeClaim": map[string]interface{}{ + "claimName": "claim1", + }, + }, + }, + }, + }, + }).Add( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "StatefulSet", + "metadata": map[string]interface{}{ + "name": "statefulset1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + }, + }, + "volumes": map[string]interface{}{ + "projected": map[string]interface{}{ + "sources": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "cm2", + }, + "secret": map[string]interface{}{ + "name": "secret1", + }, + }, + }, + }, + }, + }, + }, + }).AddWithName("sa", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": map[string]interface{}{ + "name": "someprefix-sa", + "namespace": "test", + }, + }).Add( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": "crb", + }, + "subjects": []interface{}{ + map[string]interface{}{ + "kind": "ServiceAccount", + "name": "sa", + "namespace": "test", + }, + }, + }).Add( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": "cr", + }, + "rules": []interface{}{ + map[string]interface{}{ + "resources": []interface{}{ + "secrets", + }, + "resourceNames": []interface{}{ + "secret1", + "secret1", + "secret2", + "cm1", + }, + }, + }, + }).Add( + map[string]interface{}{ + "apiVersion": "batch/v1beta1", + "kind": "CronJob", + "metadata": map[string]interface{}{ + "name": "cronjob1", + }, + "spec": map[string]interface{}{ + "schedule": "0 14 * * *", + "jobTemplate": map[string]interface{}{ + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "main", + "image": "myimage", + }, + }, + "volumes": map[string]interface{}{ + "projected": map[string]interface{}{ + "sources": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "cm2", + }, + "secret": map[string]interface{}{ + "name": "secret1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }).ResMap() + + expected := resmaptest_test.NewSeededRmBuilderDefault( + t, m.ShallowCopy()).ReplaceResource( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + "env": []interface{}{ + map[string]interface{}{ + "name": "CM_FOO", + "valueFrom": map[string]interface{}{ + "configMapKeyRef": map[string]interface{}{ + "name": "someprefix-cm1-somehash", + "key": "somekey", + }, + }, + }, + map[string]interface{}{ + "name": "SECRET_FOO", + "valueFrom": map[string]interface{}{ + "secretKeyRef": map[string]interface{}{ + "name": "someprefix-secret1-somehash", + "key": "somekey", + }, + }, + }, + }, + "envFrom": []interface{}{ + map[string]interface{}{ + "configMapRef": map[string]interface{}{ + "name": "someprefix-cm1-somehash", + "key": "somekey", + }, + }, + map[string]interface{}{ + "secretRef": map[string]interface{}{ + "name": "someprefix-secret1-somehash", + "key": "somekey", + }, + }, + }, + }, + }, + "imagePullSecrets": []interface{}{ + map[string]interface{}{ + "name": "someprefix-secret1-somehash", + }, + }, + "volumes": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "someprefix-cm1-somehash", + }, + "projected": map[string]interface{}{ + "sources": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "someprefix-cm2-somehash", + }, + "secret": map[string]interface{}{ + "name": "someprefix-secret1-somehash", + }, + }, + }, + "secret": map[string]interface{}{ + "secretName": "someprefix-secret1-somehash", + }, + "persistentVolumeClaim": map[string]interface{}{ + "claimName": "someprefix-claim1", + }, + }, + }, + }, + }, + }).ReplaceResource( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "StatefulSet", + "metadata": map[string]interface{}{ + "name": "statefulset1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + }, + }, + "volumes": map[string]interface{}{ + "projected": map[string]interface{}{ + "sources": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "someprefix-cm2-somehash", + }, + "secret": map[string]interface{}{ + "name": "someprefix-secret1-somehash", + }, + }, + }, + }, + }, + }, + }, + }).ReplaceResource( + map[string]interface{}{ + "group": "networking.k8s.io", + "apiVersion": "v1beta1", + "kind": "Ingress", + "metadata": map[string]interface{}{ + "name": "ingress1", + "annotations": map[string]interface{}{ + "ingress.kubernetes.io/auth-secret": "someprefix-secret1-somehash", + "nginx.ingress.kubernetes.io/auth-secret": "someprefix-secret1-somehash", + "nginx.ingress.kubernetes.io/auth-tls-secret": "someprefix-secret1-somehash", + }, + }, + "spec": map[string]interface{}{ + "backend": map[string]interface{}{ + "serviceName": "testsvc", + "servicePort": "80", + }, + }, + }).ReplaceResource( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": "crb", + }, + "subjects": []interface{}{ + map[string]interface{}{ + "kind": "ServiceAccount", + "name": "someprefix-sa", + "namespace": "test", + }, + }, + }).ReplaceResource( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": "cr", + }, + "rules": []interface{}{ + map[string]interface{}{ + "resources": []interface{}{ + "secrets", + }, + "resourceNames": []interface{}{ + "someprefix-secret1-somehash", + "someprefix-secret1-somehash", + "secret2", + "someprefix-cm1-somehash", + }, + }, + }, + }).ReplaceResource( + map[string]interface{}{ + "apiVersion": "batch/v1beta1", + "kind": "CronJob", + "metadata": map[string]interface{}{ + "name": "cronjob1", + }, + "spec": map[string]interface{}{ + "schedule": "0 14 * * *", + "jobTemplate": map[string]interface{}{ + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "main", + "image": "myimage", + }, + }, + "volumes": map[string]interface{}{ + "projected": map[string]interface{}{ + "sources": map[string]interface{}{ + "configMap": map[string]interface{}{ + "name": "someprefix-cm2-somehash", + }, + "secret": map[string]interface{}{ + "name": "someprefix-secret1-somehash", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }).ResMap() + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + err := nrt.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if err = expected.ErrorIfNotEqualLists(m); err != nil { + t.Fatalf(notEqualErrFmt, err) + } +} + +func TestNameReferenceUnhappyRun(t *testing.T) { + tests := []struct { + resMap resmap.ResMap + expectedErr string + }{ + { + resMap: resmaptest_test.NewRmBuilderDefault(t).Add( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": "cr", + }, + "rules": []interface{}{ + map[string]interface{}{ + "resources": []interface{}{ + "secrets", + }, + "resourceNames": []interface{}{ + []interface{}{}, + }, + }, + }, + }).ResMap(), + expectedErr: "is expected to be"}, + { + resMap: resmaptest_test.NewRmBuilderDefault(t).Add( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": "cr", + }, + "rules": []interface{}{ + map[string]interface{}{ + "resources": []interface{}{ + "secrets", + }, + "resourceNames": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }).ResMap(), + expectedErr: `updating name reference in 'rules/resourceNames' field of 'ClusterRole.v1.rbac.authorization.k8s.io/cr.[noNs]': ` + + `considering field 'rules/resourceNames' of object ClusterRole.v1.rbac.authorization.k8s.io/cr.[noNs]: visit traversal on ` + + `path: [resourceNames]: path config error; no 'name' field in node`, + }, + } + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + for _, test := range tests { + err := nrt.Transform(test.resMap) + if err == nil { + t.Fatalf("expected error to happen") + } + + if !strings.Contains(err.Error(), test.expectedErr) { + t.Fatalf("Incorrect error.\nExpected:\n %s\nGot:\n%v", + test.expectedErr, err) + } + } +} + +func TestNameReferencePersistentVolumeHappyRun(t *testing.T) { + rf := provider.NewDefaultDepProvider().GetResourceFactory() + + v1 := rf.FromMapWithName( + "volume1", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": map[string]interface{}{ + "name": "someprefix-volume1", + }, + }) + c1 := rf.FromMapWithName( + "claim1", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": map[string]interface{}{ + "name": "someprefix-claim1", + "namespace": "some-namespace", + }, + "spec": map[string]interface{}{ + "volumeName": "volume1", + }, + }) + + v2 := v1.DeepCopy() + c2 := rf.FromMapWithName( + "claim1", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": map[string]interface{}{ + "name": "someprefix-claim1", + "namespace": "some-namespace", + }, + "spec": map[string]interface{}{ + "volumeName": "someprefix-volume1", + }, + }) + + m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(v1).AddR(c1).ResMap() + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + if err := nrt.Transform(m1); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(v2).AddR(c2).ResMap() + v2.AppendRefBy(c2.CurId()) + + if err := m1.ErrorIfNotEqualLists(m2); err != nil { + t.Fatalf(notEqualErrFmt, err) + } +} + +// utility map to create a deployment object +// with (metadatanamespace, metadataname) as key +// and pointing to "refname" secret and configmap +func deploymentMap(metadatanamespace string, metadataname string, + configmapref string, secretref string) map[string]interface{} { + deployment := map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": metadataname, + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + "env": []interface{}{ + map[string]interface{}{ + "name": "CM_FOO", + "valueFrom": map[string]interface{}{ + "configMapKeyRef": map[string]interface{}{ + "name": configmapref, + "key": "somekey", + }, + }, + }, + map[string]interface{}{ + "name": "SECRET_FOO", + "valueFrom": map[string]interface{}{ + "secretKeyRef": map[string]interface{}{ + "name": secretref, + "key": "somekey", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + if metadatanamespace != "" { + metadata := deployment["metadata"].(map[string]interface{}) + metadata["namespace"] = metadatanamespace + } + return deployment +} + +const ( + defaultNs = "default" + ns1 = "ns1" + ns2 = "ns2" + ns3 = "ns3" + ns4 = "ns4" + + orgname = "uniquename" + prefixedname = "prefix-uniquename" + suffixedname = "uniquename-suffix" + modifiedname = "modifiedname" +) + +// TestNameReferenceNamespace creates serviceAccount and clusterRoleBinding +// object with the same original names (uniquename) in different namespaces +// and with different current Id. +func TestNameReferenceNamespace(t *testing.T) { + m := resmaptest_test.NewRmBuilderDefault(t). + // Add ConfigMap with the same org name in noNs, "ns1" and "ns2" namespaces + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": modifiedname, + }}). + AddWithNsAndName(ns1, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": prefixedname, + "namespace": ns1, + }}). + AddWithNsAndName(ns2, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": suffixedname, + "namespace": ns2, + }}). + // Add Secret with the same org name in noNs, "ns1" and "ns2" namespaces + AddWithNsAndName(defaultNs, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": modifiedname, + "namespace": defaultNs, + }}). + AddWithNsAndName(ns1, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": prefixedname, + "namespace": ns1, + }}). + AddWithNsAndName(ns2, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": suffixedname, + "namespace": ns2, + }}). + // Add Deployment with the same org name in noNs, "ns1" and "ns2" namespaces + AddWithNsAndName(defaultNs, orgname, deploymentMap(defaultNs, modifiedname, modifiedname, modifiedname)). + AddWithNsAndName(ns1, orgname, deploymentMap(ns1, prefixedname, orgname, orgname)). + AddWithNsAndName(ns2, orgname, deploymentMap(ns2, suffixedname, orgname, orgname)).ResMap() + + expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()). + ReplaceResource(deploymentMap(defaultNs, modifiedname, modifiedname, modifiedname)). + ReplaceResource(deploymentMap(ns1, prefixedname, prefixedname, prefixedname)). + ReplaceResource(deploymentMap(ns2, suffixedname, suffixedname, suffixedname)).ResMap() + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + err := nrt.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m.RemoveBuildAnnotations() + if err = expected.ErrorIfNotEqualLists(m); err != nil { + t.Fatalf(notEqualErrFmt, err) + } +} + +// TestNameReferenceNamespace creates serviceAccount and clusterRoleBinding +// object with the same original names (uniquename) in different namespaces +// and with different current Id. +func TestNameReferenceClusterWide(t *testing.T) { + m := resmaptest_test.NewRmBuilderDefault(t). + // Add ServiceAccount with the same org name in noNs, "ns1" and "ns2" namespaces + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": map[string]interface{}{ + "name": modifiedname, + }}). + AddWithNsAndName(ns1, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": map[string]interface{}{ + "name": prefixedname, + "namespace": ns1, + }}). + AddWithNsAndName(ns2, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": map[string]interface{}{ + "name": suffixedname, + "namespace": ns2, + }}). + // Add a PersistentVolume to have a clusterwide resource + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": map[string]interface{}{ + "name": modifiedname, + }}). + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": modifiedname, + }, + "rules": []interface{}{ + map[string]interface{}{ + "resources": []interface{}{ + "persistentvolumes", + }, + "resourceNames": []interface{}{ + orgname, + }, + }, + }}). + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": modifiedname, + }, + "roleRef": map[string]interface{}{ + "apiGroup": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "name": orgname, + }, + "subjects": []interface{}{ + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": defaultNs, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": ns1, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": ns2, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": "random", + }, + }}).ResMap() + + expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()). + ReplaceResource( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": modifiedname, + }, + // Behavior of the transformer is still imperfect + // It should use the (resources,apigroup,resourceNames) as + // combination to select the candidates. + "rules": []interface{}{ + map[string]interface{}{ + "resources": []interface{}{ + "persistentvolumes", + }, + "resourceNames": []interface{}{ + modifiedname, + }, + }, + }}). + ReplaceResource( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": modifiedname, + }, + "roleRef": map[string]interface{}{ + "apiGroup": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "name": modifiedname, + }, + // The following tests required a change in + // getNameFunc implementation in order to leverage + // the namespace field. + "subjects": []interface{}{ + map[string]interface{}{ + "kind": "ServiceAccount", + "name": modifiedname, + "namespace": defaultNs, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": prefixedname, + "namespace": ns1, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": suffixedname, + "namespace": ns2, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": "random", + }, + }, + }).ResMap() + + clusterRoleId := resid.NewResId( + resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRole"), modifiedname) + clusterRoleBindingId := resid.NewResId( + resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), modifiedname) + clusterRole, _ := expected.GetByCurrentId(clusterRoleId) + clusterRole.AppendRefBy(clusterRoleBindingId) + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + err := nrt.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expected.RemoveBuildAnnotations() + m.RemoveBuildAnnotations() + + if err = expected.ErrorIfNotEqualLists(m); err != nil { + t.Fatalf(notEqualErrFmt, err) + } +} + +// TestNameReferenceNamespaceTransformation creates serviceAccount and clusterRoleBinding +// object with the same original names (uniquename) in different namespaces +// and with different current Id. +func TestNameReferenceNamespaceTransformation(t *testing.T) { + m := resmaptest_test.NewRmBuilderDefault(t). + AddWithNsAndName(ns4, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": orgname, + "namespace": ns4, + }}). + // Add ServiceAccount with the same org name in "ns1" namespaces + AddWithNsAndName(ns1, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": map[string]interface{}{ + "name": prefixedname, + "namespace": ns1, + }}). + // Simulate NamespaceTransformer effect (ns3 transformed in ns2) + AddWithNsAndName(ns3, orgname, map[string]interface{}{ + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": map[string]interface{}{ + "name": suffixedname, + "namespace": ns2, + }}). + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": modifiedname, + }}). + AddWithName(orgname, map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": modifiedname, + }, + "roleRef": map[string]interface{}{ + "apiGroup": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "name": orgname, + }, + "subjects": []interface{}{ + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": ns1, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": ns3, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": "random", + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": ns4, + }, + }}).ResMap() + + expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()). + ReplaceResource( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": modifiedname, + }, + "roleRef": map[string]interface{}{ + "apiGroup": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "name": modifiedname, + }, + // The following tests required a change in + // getNameFunc implementation in order to leverage + // the namespace field. + "subjects": []interface{}{ + map[string]interface{}{ + "kind": "ServiceAccount", + "name": prefixedname, + "namespace": ns1, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": suffixedname, + "namespace": ns2, + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": "random", + }, + map[string]interface{}{ + "kind": "ServiceAccount", + "name": orgname, + "namespace": ns4, + }, + }, + }).ResMap() + + clusterRoleId := resid.NewResId( + resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRole"), + modifiedname) + clusterRoleBindingId := resid.NewResId( + resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), + modifiedname) + clusterRole, _ := expected.GetByCurrentId(clusterRoleId) + clusterRole.AppendRefBy(clusterRoleBindingId) + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + err := nrt.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m.RemoveBuildAnnotations() + if err = expected.ErrorIfNotEqualLists(m); err != nil { + t.Fatalf(notEqualErrFmt, err) + } +} + +// TestNameReferenceNamespace creates configmap, secret, deployment +// It validates the change done is IsSameFuzzyNamespace which +// uses the IsNsEquals method instead of the simple == operator. +func TestNameReferenceCandidateSelection(t *testing.T) { + m := resmaptest_test.NewRmBuilderDefault(t). + AddWithName("cm1", map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "p1-cm1-hash", + }}). + AddWithNsAndName("default", "secret1", map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "p1-secret1-hash", + "namespace": "default", + }}). + AddWithName("deploy1", deploymentMap("", "p1-deploy1", "cm1", "secret1")). + ResMap() + + expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()). + ReplaceResource(deploymentMap("", "p1-deploy1", "p1-cm1-hash", "p1-secret1-hash")). + ResMap() + + nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) + err := nrt.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m.RemoveBuildAnnotations() + if err = expected.ErrorIfNotEqualLists(m); err != nil { + t.Fatalf(notEqualErrFmt, err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/refvartransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/refvartransformer.go new file mode 100644 index 000000000..a02edc4fb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/refvartransformer.go @@ -0,0 +1,57 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator + +import ( + "sigs.k8s.io/kustomize/api/filters/refvar" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" +) + +type refVarTransformer struct { + varMap map[string]interface{} + replacementCounts map[string]int + fieldSpecs []types.FieldSpec +} + +// newRefVarTransformer returns a new refVarTransformer +// that replaces $(VAR) style variables with values. +// The fieldSpecs are the places to look for occurrences of $(VAR). +func newRefVarTransformer( + varMap map[string]interface{}, fs []types.FieldSpec) *refVarTransformer { + return &refVarTransformer{ + varMap: varMap, + fieldSpecs: fs, + } +} + +// UnusedVars returns slice of Var names that were unused +// after a Transform run. +func (rv *refVarTransformer) UnusedVars() []string { + var unused []string + for k := range rv.varMap { + if _, ok := rv.replacementCounts[k]; !ok { + unused = append(unused, k) + } + } + return unused +} + +// Transform replaces $(VAR) style variables with values. +func (rv *refVarTransformer) Transform(m resmap.ResMap) error { + rv.replacementCounts = make(map[string]int) + mf := refvar.MakePrimitiveReplacer(rv.replacementCounts, rv.varMap) + for _, res := range m.Resources() { + for _, fieldSpec := range rv.fieldSpecs { + err := res.ApplyFilter(refvar.Filter{ + MappingFunc: mf, + FieldSpec: fieldSpec, + }) + if err != nil { + return err + } + } + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/refvartransformer_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/refvartransformer_test.go new file mode 100644 index 000000000..05b48ffb4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/refvartransformer_test.go @@ -0,0 +1,189 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator + +import ( + "reflect" + "testing" + + "sigs.k8s.io/kustomize/api/resmap" + resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestRefVarTransformer(t *testing.T) { + type given struct { + varMap map[string]interface{} + fs []types.FieldSpec + res resmap.ResMap + } + type expected struct { + res resmap.ResMap + unused []string + } + testCases := map[string]struct { + given given + expected expected + errMessage string + }{ + "var replacement in map[string]": { + given: given{ + varMap: map[string]interface{}{ + "FOO": "replacementForFoo", + "BAR": "replacementForBar", + "BAZ": int64(5), + "BOO": true, + }, + fs: []types.FieldSpec{ + {Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/map"}, + {Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"}, + {Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/interface"}, + {Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/num"}, + }, + res: resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + "data": map[string]interface{}{ + "map": map[string]interface{}{ + "item1": "$(FOO)", + "item2": "bla", + "item3": "$(BAZ)", + "item4": "$(BAZ)+$(BAZ)", + "item5": "$(BOO)", + "item6": "if $(BOO)", + "item7": int64(2019), + }, + "slice": []interface{}{ + "$(FOO)", + "bla", + "$(BAZ)", + "$(BAZ)+$(BAZ)", + "$(BOO)", + "if $(BOO)", + }, + "interface": "$(FOO)", + "num": int64(2019), + }}).ResMap(), + }, + expected: expected{ + res: resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + "data": map[string]interface{}{ + "map": map[string]interface{}{ + "item1": "replacementForFoo", + "item2": "bla", + "item3": int64(5), + "item4": "5+5", + "item5": true, + "item6": "if true", + "item7": int64(2019), + }, + "slice": []interface{}{ + "replacementForFoo", + "bla", + int64(5), + "5+5", + true, + "if true", + }, + "interface": "replacementForFoo", + "num": int64(2019), + }}).ResMap(), + unused: []string{"BAR"}, + }, + }, + "var replacement panic in map[string]": { + given: given{ + varMap: map[string]interface{}{}, + fs: []types.FieldSpec{ + {Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"}, + }, + res: resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + "data": map[string]interface{}{ + "slice": []interface{}{5}, // noticeably *not* a []string + }}).ResMap(), + }, + errMessage: `considering field 'data/slice' of object ConfigMap.v1.[noGrp]/cm1.[noNs]: invalid value type expect a string`, + }, + "var replacement in nil": { + given: given{ + varMap: map[string]interface{}{}, + fs: []types.FieldSpec{ + {Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/nil"}, + }, + res: resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + "data": map[string]interface{}{ + "nil": nil, // noticeably *not* a []string + }}).ResMap(), + }, + expected: expected{ + res: resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + "data": map[string]interface{}{ + "nil": nil, // noticeably *not* a []string + }}).ResMap(), + }, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + tr := newRefVarTransformer(tc.given.varMap, tc.given.fs) + err := tr.Transform(tc.given.res) + if tc.errMessage != "" { + if err == nil { + t.Fatalf("missing expected error %v", tc.errMessage) + } else if err.Error() != tc.errMessage { + t.Fatalf(`actual error doesn't match expected error: +ACTUAL: %v +EXPECTED: %v`, + err.Error(), tc.errMessage) + } + } else { + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + a, e := tc.given.res, tc.expected.res + if !reflect.DeepEqual(a, e) { + err = e.ErrorIfNotEqualLists(a) + t.Fatalf(`actual doesn't match expected: +ACTUAL: +%v +EXPECTED: +%v +ERR: %v`, + a, e, err) + } + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/resaccumulator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/resaccumulator.go new file mode 100644 index 000000000..934553757 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/resaccumulator.go @@ -0,0 +1,190 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator + +import ( + "fmt" + "log" + "strings" + + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// ResAccumulator accumulates resources and the rules +// used to customize those resources. It's a ResMap +// plus stuff needed to modify the ResMap. +type ResAccumulator struct { + resMap resmap.ResMap + tConfig *builtinconfig.TransformerConfig + varSet types.VarSet +} + +func MakeEmptyAccumulator() *ResAccumulator { + ra := &ResAccumulator{} + ra.resMap = resmap.New() + ra.tConfig = &builtinconfig.TransformerConfig{} + ra.varSet = types.NewVarSet() + return ra +} + +// ResMap returns a copy of the internal resMap. +func (ra *ResAccumulator) ResMap() resmap.ResMap { + return ra.resMap.ShallowCopy() +} + +// Vars returns a copy of underlying vars. +func (ra *ResAccumulator) Vars() []types.Var { + return ra.varSet.AsSlice() +} + +func (ra *ResAccumulator) AppendAll(resources resmap.ResMap) error { + return ra.resMap.AppendAll(resources) +} + +func (ra *ResAccumulator) AbsorbAll(resources resmap.ResMap) error { + return ra.resMap.AbsorbAll(resources) +} + +func (ra *ResAccumulator) MergeConfig( + tConfig *builtinconfig.TransformerConfig) (err error) { + ra.tConfig, err = ra.tConfig.Merge(tConfig) + return err +} + +func (ra *ResAccumulator) GetTransformerConfig() *builtinconfig.TransformerConfig { + return ra.tConfig +} + +// MergeVars accumulates vars into ResAccumulator. +// A Var is a tuple of name, object reference and field reference. +// This func takes a list of vars from the current kustomization file and +// annotates the accumulated resources with the names of the vars that match +// those resources. E.g. if there's a var named "sam" that wants to get +// its data from a ConfigMap named "james", and the resource list contains a +// ConfigMap named "james", then that ConfigMap will be annotated with the +// var name "sam". Later this annotation is used to find the data for "sam" +// by digging into a particular fieldpath of "james". +func (ra *ResAccumulator) MergeVars(incoming []types.Var) error { + for _, v := range incoming { + targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace) + idMatcher := targetId.GvknEquals + if targetId.Namespace != "" || targetId.IsClusterScoped() { + // Preserve backward compatibility. An empty namespace means + // wildcard search on the namespace hence we still use GvknEquals + idMatcher = targetId.Equals + } + matched := ra.resMap.GetMatchingResourcesByAnyId(idMatcher) + if len(matched) > 1 { + return fmt.Errorf( + "found %d resId matches for var %s "+ + "(unable to disambiguate)", + len(matched), v) + } + if len(matched) == 1 { + matched[0].AppendRefVarName(v) + } + } + return ra.varSet.MergeSlice(incoming) +} + +func (ra *ResAccumulator) MergeAccumulator(other *ResAccumulator) (err error) { + err = ra.AppendAll(other.resMap) + if err != nil { + return err + } + err = ra.MergeConfig(other.tConfig) + if err != nil { + return err + } + return ra.varSet.MergeSet(other.varSet) +} + +func (ra *ResAccumulator) findVarValueFromResources(v types.Var) (interface{}, error) { + for _, res := range ra.resMap.Resources() { + for _, varName := range res.GetRefVarNames() { + if varName == v.Name { + //nolint: staticcheck + s, err := res.GetFieldValue(v.FieldRef.FieldPath) + if err != nil { + return "", fmt.Errorf( + "field specified in var '%v' "+ + "not found in corresponding resource", v) + } + return s, nil + } + } + } + return "", fmt.Errorf( + "var '%v' cannot be mapped to a field "+ + "in the set of known resources", v) +} + +// makeVarReplacementMap returns a map of Var names to +// their final values. The values are strings intended +// for substitution wherever the $(var.Name) occurs. +func (ra *ResAccumulator) makeVarReplacementMap() (map[string]interface{}, error) { + result := map[string]interface{}{} + for _, v := range ra.Vars() { + s, err := ra.findVarValueFromResources(v) + if err != nil { + return nil, err + } + result[v.Name] = s + } + return result, nil +} + +func (ra *ResAccumulator) Transform(t resmap.Transformer) error { + return t.Transform(ra.resMap) +} + +func (ra *ResAccumulator) ResolveVars() error { + replacementMap, err := ra.makeVarReplacementMap() + if err != nil { + return err + } + if len(replacementMap) == 0 { + return nil + } + t := newRefVarTransformer( + replacementMap, ra.tConfig.VarReference) + err = ra.Transform(t) + if len(t.UnusedVars()) > 0 { + log.Printf( + "well-defined vars that were never replaced: %s\n", + strings.Join(t.UnusedVars(), ",")) + } + return err +} + +func (ra *ResAccumulator) FixBackReferences() (err error) { + if ra.tConfig.NameReference == nil { + return nil + } + return ra.Transform( + newNameReferenceTransformer(ra.tConfig.NameReference)) +} + +// Intersection drops the resources which "other" does not have. +func (ra *ResAccumulator) Intersection(other resmap.ResMap) error { + for _, curId := range ra.resMap.AllIds() { + toDelete := true + for _, otherId := range other.AllIds() { + if otherId == curId { + toDelete = false + break + } + } + if toDelete { + err := ra.resMap.Remove(curId) + if err != nil { + return err + } + } + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/resaccumulator_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/resaccumulator_test.go new file mode 100644 index 000000000..1d2140c1e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/accumulator/resaccumulator_test.go @@ -0,0 +1,426 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package accumulator_test + +import ( + "bytes" + "log" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" + . "sigs.k8s.io/kustomize/api/internal/accumulator" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func makeResAccumulator(t *testing.T) *ResAccumulator { + ra := MakeEmptyAccumulator() + err := ra.MergeConfig(builtinconfig.MakeDefaultConfig()) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = ra.AppendAll( + resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "command": []interface{}{ + "myserver", + "--somebackendService $(SERVICE_ONE)", + "--yetAnother $(SERVICE_TWO)", + }, + }, + }, + }, + }, + }}). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Service", + "metadata": map[string]interface{}{ + "name": "backendOne", + }}). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Service", + "metadata": map[string]interface{}{ + "name": "backendTwo", + }}).ResMap()) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + return ra +} + +func TestResolveVarsHappy(t *testing.T) { + ra := makeResAccumulator(t) + err := ra.MergeVars([]types.Var{ + { + Name: "SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne"}, + }, + { + Name: "SERVICE_TWO", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendTwo"}, + }, + }) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = ra.ResolveVars() + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + c := getCommand(find("deploy1", ra.ResMap())) + if c != "myserver --somebackendService backendOne --yetAnother backendTwo" { + t.Fatalf("unexpected command: %s", c) + } +} + +func TestResolveVarsOneUnused(t *testing.T) { + ra := makeResAccumulator(t) + err := ra.MergeVars([]types.Var{ + { + Name: "SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne"}, + }, + { + Name: "SERVICE_UNUSED", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendTwo"}, + }, + }) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + var buf bytes.Buffer + log.SetOutput(&buf) + defer func() { + log.SetOutput(os.Stderr) + }() + err = ra.ResolveVars() + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + expectLog(t, buf, "well-defined vars that were never replaced: SERVICE_UNUSED") + c := getCommand(find("deploy1", ra.ResMap())) + if c != "myserver --somebackendService backendOne --yetAnother $(SERVICE_TWO)" { + t.Fatalf("unexpected command: %s", c) + } +} + +func expectLog(t *testing.T, log bytes.Buffer, expect string) { + if !strings.Contains(log.String(), expect) { + t.Fatalf("expected log containing '%s', got '%s'", expect, log.String()) + } +} + +func TestResolveVarsVarNeedsDisambiguation(t *testing.T) { + ra := makeResAccumulator(t) + rm0 := resmap.New() + err := rm0.Append( + provider.NewDefaultDepProvider().GetResourceFactory().FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Service", + "metadata": map[string]interface{}{ + "name": "backendOne", + "namespace": "fooNamespace", + }, + })) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = ra.AppendAll(rm0) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + err = ra.MergeVars([]types.Var{ + { + Name: "SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne", + }, + }, + }) + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains( + err.Error(), "unable to disambiguate") { + t.Fatalf("unexpected err: %v", err) + } +} + +func makeNamespacedConfigMapWithDataProviderValue( + namespace string, + value string, +) map[string]interface{} { + return map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "environment", + "namespace": namespace, + }, + "data": map[string]interface{}{ + "provider": value, + }, + } +} + +func makeVarToNamepaceAndPath( + name string, + namespace string, + path string, +) types.Var { + return types.Var{ + Name: name, + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, + Name: "environment", + Namespace: namespace, + }, + FieldRef: types.FieldSelector{FieldPath: path}, + } +} + +func TestResolveVarConflicts(t *testing.T) { + rf := provider.NewDefaultDepProvider().GetResourceFactory() + // create configmaps in foo and bar namespaces with `data.provider` values. + fooAws := makeNamespacedConfigMapWithDataProviderValue("foo", "aws") + barAws := makeNamespacedConfigMapWithDataProviderValue("bar", "aws") + barGcp := makeNamespacedConfigMapWithDataProviderValue("bar", "gcp") + + // create two variables with (apparently) conflicting names that point to + // fieldpaths that could be generalized. + varFoo := makeVarToNamepaceAndPath("PROVIDER", "foo", "data.provider") + varBar := makeVarToNamepaceAndPath("PROVIDER", "bar", "data.provider") + + // create accumulators holding apparently conflicting vars that are not + // actually in conflict because they point to the same concrete value. + rm0 := resmap.New() + err := rm0.Append(rf.FromMap(fooAws)) + require.NoError(t, err) + ac0 := MakeEmptyAccumulator() + err = ac0.AppendAll(rm0) + require.NoError(t, err) + err = ac0.MergeVars([]types.Var{varFoo}) + require.NoError(t, err) + + rm1 := resmap.New() + err = rm1.Append(rf.FromMap(barAws)) + require.NoError(t, err) + ac1 := MakeEmptyAccumulator() + err = ac1.AppendAll(rm1) + require.NoError(t, err) + err = ac1.MergeVars([]types.Var{varBar}) + require.NoError(t, err) + + // validate that two vars of the same name which reference the same concrete + // value do not produce a conflict. + err = ac0.MergeAccumulator(ac1) + if err == nil { + t.Fatalf("see bug gh-1600") + } + + // create an accumulator will have an actually conflicting value with the + // two above (because it contains a variable whose name is used in the other + // accumulators AND whose concrete values are different). + rm2 := resmap.New() + err = rm2.Append(rf.FromMap(barGcp)) + require.NoError(t, err) + ac2 := MakeEmptyAccumulator() + err = ac2.AppendAll(rm2) + require.NoError(t, err) + err = ac2.MergeVars([]types.Var{varBar}) + require.NoError(t, err) + err = ac1.MergeAccumulator(ac2) + if err == nil { + t.Fatalf("dupe vars w/ different concrete values should conflict") + } +} + +func TestResolveVarsGoodResIdBadField(t *testing.T) { + ra := makeResAccumulator(t) + err := ra.MergeVars([]types.Var{ + { + Name: "SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne"}, + FieldRef: types.FieldSelector{FieldPath: "nope_nope_nope"}, + }, + }) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = ra.ResolveVars() + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains( + err.Error(), + "not found in corresponding resource") { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestResolveVarsUnmappableVar(t *testing.T) { + ra := makeResAccumulator(t) + err := ra.MergeVars([]types.Var{ + { + Name: "SERVICE_THREE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "doesNotExist"}, + }, + }) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = ra.ResolveVars() + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains( + err.Error(), + "cannot be mapped to a field in the set of known resources") { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestResolveVarsWithNoambiguation(t *testing.T) { + ra1 := makeResAccumulator(t) + err := ra1.MergeVars([]types.Var{ + { + Name: "SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne", + }, + }, + }) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + // Create another accumulator having a resource with different prefix + ra2 := MakeEmptyAccumulator() + + m := resmaptest_test.NewRmBuilderDefault(t). + Add(map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy2", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "command": []interface{}{ + "myserver", + "--somebackendService $(SUB_SERVICE_ONE)", + }, + }, + }, + }, + }, + }}). + // Make it seem like this resource + // went through a prefix transformer. + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Service", + "metadata": map[string]interface{}{ + "name": "sub-backendOne", + "annotations": map[string]interface{}{ + "internal.config.kubernetes.io/previousKinds": "Service", + "internal.config.kubernetes.io/previousNames": "backendOne", + "internal.config.kubernetes.io/previousNamespaces": "default", + "internal.config.kubernetes.io/prefixes": "sub-", + }, + }}).ResMap() + + err = ra2.AppendAll(m) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + err = ra2.MergeVars([]types.Var{ + { + Name: "SUB_SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne", + }, + }, + }) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = ra1.MergeAccumulator(ra2) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + err = ra1.ResolveVars() + if err != nil { + t.Fatalf("unexpected err: %v", err) + } +} + +func find(name string, resMap resmap.ResMap) *resource.Resource { + for _, r := range resMap.Resources() { + if r.GetName() == name { + return r + } + } + return nil +} + +// Assumes arg is a deployment, returns the command of first container. +func getCommand(r *resource.Resource) string { + var m map[string]interface{} + var c []interface{} + resourceMap, _ := r.Map() + m, _ = resourceMap["spec"].(map[string]interface{}) + m, _ = m["template"].(map[string]interface{}) + m, _ = m["spec"].(map[string]interface{}) + c, _ = m["containers"].([]interface{}) + m, _ = c[0].(map[string]interface{}) + + cmd, _ := m["command"].([]interface{}) + n := make([]string, len(cmd)) + for i, v := range cmd { + n[i] = v.(string) + } + return strings.Join(n, " ") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/AnnotationsTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/AnnotationsTransformer.go new file mode 100644 index 000000000..7064fa80c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/AnnotationsTransformer.go @@ -0,0 +1,38 @@ +// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sigs.k8s.io/kustomize/api/filters/annotations" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// Add the given annotations to the given field specifications. +type AnnotationsTransformerPlugin struct { + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func (p *AnnotationsTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Annotations = nil + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error { + if len(p.Annotations) == 0 { + return nil + } + return m.ApplyFilter(annotations.Filter{ + Annotations: p.Annotations, + FsSlice: p.FieldSpecs, + }) +} + +func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin { + return &AnnotationsTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ConfigMapGenerator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ConfigMapGenerator.go new file mode 100644 index 000000000..d853a1cfd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ConfigMapGenerator.go @@ -0,0 +1,39 @@ +// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sigs.k8s.io/kustomize/api/kv" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +type ConfigMapGeneratorPlugin struct { + h *resmap.PluginHelpers + types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + types.ConfigMapArgs +} + +func (p *ConfigMapGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) { + p.ConfigMapArgs = types.ConfigMapArgs{} + err = yaml.Unmarshal(config, p) + if p.ConfigMapArgs.Name == "" { + p.ConfigMapArgs.Name = p.Name + } + if p.ConfigMapArgs.Namespace == "" { + p.ConfigMapArgs.Namespace = p.Namespace + } + p.h = h + return +} + +func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) { + return p.h.ResmapFactory().FromConfigMapArgs( + kv.NewLoader(p.h.Loader(), p.h.Validator()), p.ConfigMapArgs) +} + +func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin { + return &ConfigMapGeneratorPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/HashTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/HashTransformer.go new file mode 100644 index 000000000..54586beeb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/HashTransformer.go @@ -0,0 +1,40 @@ +// Code generated by pluginator on HashTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/resmap" +) + +type HashTransformerPlugin struct { + hasher ifc.KustHasher +} + +func (p *HashTransformerPlugin) Config( + h *resmap.PluginHelpers, _ []byte) (err error) { + p.hasher = h.ResmapFactory().RF().Hasher() + return nil +} + +// Transform appends hash to generated resources. +func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error { + for _, res := range m.Resources() { + if res.NeedHashSuffix() { + h, err := res.Hash(p.hasher) + if err != nil { + return err + } + res.StorePreviousId() + res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) + } + } + return nil +} + +func NewHashTransformerPlugin() resmap.TransformerPlugin { + return &HashTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/HelmChartInflationGenerator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/HelmChartInflationGenerator.go new file mode 100644 index 000000000..2a654ad1f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/HelmChartInflationGenerator.go @@ -0,0 +1,339 @@ +// Code generated by pluginator on HelmChartInflationGenerator; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + + "github.com/imdario/mergo" + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// HelmChartInflationGeneratorPlugin is a plugin to generate resources +// from a remote or local helm chart. +type HelmChartInflationGeneratorPlugin struct { + h *resmap.PluginHelpers + types.HelmGlobals + types.HelmChart + tmpDir string +} + +var KustomizePlugin HelmChartInflationGeneratorPlugin + +const ( + valuesMergeOptionMerge = "merge" + valuesMergeOptionOverride = "override" + valuesMergeOptionReplace = "replace" +) + +var legalMergeOptions = []string{ + valuesMergeOptionMerge, + valuesMergeOptionOverride, + valuesMergeOptionReplace, +} + +// Config uses the input plugin configurations `config` to setup the generator +// options +func (p *HelmChartInflationGeneratorPlugin) Config( + h *resmap.PluginHelpers, config []byte) (err error) { + if h.GeneralConfig() == nil { + return fmt.Errorf("unable to access general config") + } + if !h.GeneralConfig().HelmConfig.Enabled { + return fmt.Errorf("must specify --enable-helm") + } + if h.GeneralConfig().HelmConfig.Command == "" { + return fmt.Errorf("must specify --helm-command") + } + p.h = h + if err = yaml.Unmarshal(config, p); err != nil { + return + } + return p.validateArgs() +} + +// This uses the real file system since tmpDir may be used +// by the helm subprocess. Cannot use a chroot jail or fake +// filesystem since we allow the user to use previously +// downloaded charts. This is safe since this plugin is +// owned by kustomize. +func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) { + if p.tmpDir != "" { + // already done. + return nil + } + p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-") + return err +} + +func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) { + if p.Name == "" { + return fmt.Errorf("chart name cannot be empty") + } + + // ChartHome might be consulted by the plugin (to read + // values files below it), so it must be located under + // the loader root (unless root restrictions are + // disabled, in which case this can be an absolute path). + if p.ChartHome == "" { + p.ChartHome = "charts" + } + + // The ValuesFile may be consulted by the plugin, so it must + // be under the loader root (unless root restrictions are + // disabled). + if p.ValuesFile == "" { + p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml") + } + + if err = p.errIfIllegalValuesMerge(); err != nil { + return err + } + + // ConfigHome is not loaded by the plugin, and can be located anywhere. + if p.ConfigHome == "" { + if err = p.establishTmpDir(); err != nil { + return errors.Wrap( + err, "unable to create tmp dir for HELM_CONFIG_HOME") + } + p.ConfigHome = filepath.Join(p.tmpDir, "helm") + } + return nil +} + +func (p *HelmChartInflationGeneratorPlugin) errIfIllegalValuesMerge() error { + if p.ValuesMerge == "" { + // Use the default. + p.ValuesMerge = valuesMergeOptionOverride + return nil + } + for _, opt := range legalMergeOptions { + if p.ValuesMerge == opt { + return nil + } + } + return fmt.Errorf("valuesMerge must be one of %v", legalMergeOptions) +} + +func (p *HelmChartInflationGeneratorPlugin) absChartHome() string { + if filepath.IsAbs(p.ChartHome) { + return p.ChartHome + } + return filepath.Join(p.h.Loader().Root(), p.ChartHome) +} + +func (p *HelmChartInflationGeneratorPlugin) runHelmCommand( + args []string) ([]byte, error) { + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + cmd := exec.Command(p.h.GeneralConfig().HelmConfig.Command, args...) + cmd.Stdout = stdout + cmd.Stderr = stderr + env := []string{ + fmt.Sprintf("HELM_CONFIG_HOME=%s", p.ConfigHome), + fmt.Sprintf("HELM_CACHE_HOME=%s/.cache", p.ConfigHome), + fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)} + cmd.Env = append(os.Environ(), env...) + err := cmd.Run() + if err != nil { + helm := p.h.GeneralConfig().HelmConfig.Command + err = errors.Wrap( + fmt.Errorf( + "unable to run: '%s %s' with env=%s (is '%s' installed?)", + helm, strings.Join(args, " "), env, helm), + stderr.String(), + ) + } + return stdout.Bytes(), err +} + +// createNewMergedValuesFile replaces/merges original values file with ValuesInline. +func (p *HelmChartInflationGeneratorPlugin) createNewMergedValuesFile() ( + path string, err error) { + if p.ValuesMerge == valuesMergeOptionMerge || + p.ValuesMerge == valuesMergeOptionOverride { + if err = p.replaceValuesInline(); err != nil { + return "", err + } + } + var b []byte + b, err = yaml.Marshal(p.ValuesInline) + if err != nil { + return "", err + } + return p.writeValuesBytes(b) +} + +func (p *HelmChartInflationGeneratorPlugin) replaceValuesInline() error { + pValues, err := p.h.Loader().Load(p.ValuesFile) + if err != nil { + return err + } + chValues := make(map[string]interface{}) + if err = yaml.Unmarshal(pValues, &chValues); err != nil { + return err + } + switch p.ValuesMerge { + case valuesMergeOptionOverride: + err = mergo.Merge( + &chValues, p.ValuesInline, mergo.WithOverride) + case valuesMergeOptionMerge: + err = mergo.Merge(&chValues, p.ValuesInline) + } + p.ValuesInline = chValues + return err +} + +// copyValuesFile to avoid branching. TODO: get rid of this. +func (p *HelmChartInflationGeneratorPlugin) copyValuesFile() (string, error) { + b, err := p.h.Loader().Load(p.ValuesFile) + if err != nil { + return "", err + } + return p.writeValuesBytes(b) +} + +// Write a absolute path file in the tmp file system. +func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes( + b []byte) (string, error) { + if err := p.establishTmpDir(); err != nil { + return "", fmt.Errorf("cannot create tmp dir to write helm values") + } + path := filepath.Join(p.tmpDir, p.Name+"-kustomize-values.yaml") + return path, ioutil.WriteFile(path, b, 0644) +} + +func (p *HelmChartInflationGeneratorPlugin) cleanup() { + if p.tmpDir != "" { + os.RemoveAll(p.tmpDir) + } +} + +// Generate implements generator +func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err error) { + defer p.cleanup() + if err = p.checkHelmVersion(); err != nil { + return nil, err + } + if path, exists := p.chartExistsLocally(); !exists { + if p.Repo == "" { + return nil, fmt.Errorf( + "no repo specified for pull, no chart found at '%s'", path) + } + if _, err := p.runHelmCommand(p.pullCommand()); err != nil { + return nil, err + } + } + if len(p.ValuesInline) > 0 { + p.ValuesFile, err = p.createNewMergedValuesFile() + } else { + p.ValuesFile, err = p.copyValuesFile() + } + if err != nil { + return nil, err + } + var stdout []byte + stdout, err = p.runHelmCommand(p.templateCommand()) + if err != nil { + return nil, err + } + + rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout) + if err == nil { + return rm, nil + } + // try to remove the contents before first "---" because + // helm may produce messages to stdout before it + stdoutStr := string(stdout) + if idx := strings.Index(stdoutStr, "---"); idx != -1 { + return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:])) + } + return nil, err +} + +func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string { + args := []string{"template"} + if p.ReleaseName != "" { + args = append(args, p.ReleaseName) + } + if p.Namespace != "" { + args = append(args, "--namespace", p.Namespace) + } + args = append(args, filepath.Join(p.absChartHome(), p.Name)) + if p.ValuesFile != "" { + args = append(args, "--values", p.ValuesFile) + } + if p.ReleaseName == "" { + // AFAICT, this doesn't work as intended due to a bug in helm. + // See https://github.com/helm/helm/issues/6019 + // I've tried placing the flag before and after the name argument. + args = append(args, "--generate-name") + } + if p.IncludeCRDs { + args = append(args, "--include-crds") + } + return args +} + +func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string { + args := []string{ + "pull", + "--untar", + "--untardir", p.absChartHome(), + "--repo", p.Repo, + p.Name} + if p.Version != "" { + args = append(args, "--version", p.Version) + } + return args +} + +// chartExistsLocally will return true if the chart does exist in +// local chart home. +func (p *HelmChartInflationGeneratorPlugin) chartExistsLocally() (string, bool) { + path := filepath.Join(p.absChartHome(), p.Name) + s, err := os.Stat(path) + if err != nil { + return "", false + } + return path, s.IsDir() +} + +// checkHelmVersion will return an error if the helm version is not V3 +func (p *HelmChartInflationGeneratorPlugin) checkHelmVersion() error { + stdout, err := p.runHelmCommand([]string{"version", "-c", "--short"}) + if err != nil { + return err + } + r, err := regexp.Compile(`v?\d+(\.\d+)+`) + if err != nil { + return err + } + v := r.FindString(string(stdout)) + if v == "" { + return fmt.Errorf("cannot find version string in %s", string(stdout)) + } + if v[0] == 'v' { + v = v[1:] + } + majorVersion := strings.Split(v, ".")[0] + if majorVersion != "3" { + return fmt.Errorf("this plugin requires helm V3 but got v%s", v) + } + return nil +} + +func NewHelmChartInflationGeneratorPlugin() resmap.GeneratorPlugin { + return &HelmChartInflationGeneratorPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/IAMPolicyGenerator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/IAMPolicyGenerator.go new file mode 100644 index 000000000..82a2dd603 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/IAMPolicyGenerator.go @@ -0,0 +1,33 @@ +// Code generated by pluginator on IAMPolicyGenerator; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sigs.k8s.io/kustomize/api/filters/iampolicygenerator" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +type IAMPolicyGeneratorPlugin struct { + types.IAMPolicyGeneratorArgs +} + +func (p *IAMPolicyGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) { + p.IAMPolicyGeneratorArgs = types.IAMPolicyGeneratorArgs{} + err = yaml.Unmarshal(config, p) + return +} + +func (p *IAMPolicyGeneratorPlugin) Generate() (resmap.ResMap, error) { + r := resmap.New() + err := r.ApplyFilter(iampolicygenerator.Filter{ + IAMPolicyGenerator: p.IAMPolicyGeneratorArgs, + }) + return r, err +} + +func NewIAMPolicyGeneratorPlugin() resmap.GeneratorPlugin { + return &IAMPolicyGeneratorPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ImageTagTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ImageTagTransformer.go new file mode 100644 index 000000000..52a44d385 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ImageTagTransformer.go @@ -0,0 +1,41 @@ +// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sigs.k8s.io/kustomize/api/filters/imagetag" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// Find matching image declarations and replace +// the name, tag and/or digest. +type ImageTagTransformerPlugin struct { + ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func (p *ImageTagTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.ImageTag = types.Image{} + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error { + if err := m.ApplyFilter(imagetag.LegacyFilter{ + ImageTag: p.ImageTag, + }); err != nil { + return err + } + return m.ApplyFilter(imagetag.Filter{ + ImageTag: p.ImageTag, + FsSlice: p.FieldSpecs, + }) +} + +func NewImageTagTransformerPlugin() resmap.TransformerPlugin { + return &ImageTagTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/LabelTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/LabelTransformer.go new file mode 100644 index 000000000..af601da5b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/LabelTransformer.go @@ -0,0 +1,38 @@ +// Code generated by pluginator on LabelTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sigs.k8s.io/kustomize/api/filters/labels" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// Add the given labels to the given field specifications. +type LabelTransformerPlugin struct { + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func (p *LabelTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Labels = nil + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error { + if len(p.Labels) == 0 { + return nil + } + return m.ApplyFilter(labels.Filter{ + Labels: p.Labels, + FsSlice: p.FieldSpecs, + }) +} + +func NewLabelTransformerPlugin() resmap.TransformerPlugin { + return &LabelTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/LegacyOrderTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/LegacyOrderTransformer.go new file mode 100644 index 000000000..322cea93a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/LegacyOrderTransformer.go @@ -0,0 +1,46 @@ +// Code generated by pluginator on LegacyOrderTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sort" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" +) + +// Sort the resources using an ordering defined in the Gvk class. +// This puts cluster-wide basic resources with no +// dependencies (like Namespace, StorageClass, etc.) +// first, and resources with a high number of dependencies +// (like ValidatingWebhookConfiguration) last. +type LegacyOrderTransformerPlugin struct{} + +// Nothing needed for configuration. +func (p *LegacyOrderTransformerPlugin) Config( + _ *resmap.PluginHelpers, _ []byte) (err error) { + return nil +} + +func (p *LegacyOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) { + resources := make([]*resource.Resource, m.Size()) + ids := m.AllIds() + sort.Sort(resmap.IdSlice(ids)) + for i, id := range ids { + resources[i], err = m.GetByCurrentId(id) + if err != nil { + return errors.Wrap(err, "expected match for sorting") + } + } + m.Clear() + for _, r := range resources { + m.Append(r) + } + return nil +} + +func NewLegacyOrderTransformerPlugin() resmap.TransformerPlugin { + return &LegacyOrderTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/NamespaceTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/NamespaceTransformer.go new file mode 100644 index 000000000..39a514e8e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/NamespaceTransformer.go @@ -0,0 +1,55 @@ +// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/filters/namespace" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// Change or set the namespace of non-cluster level resources. +type NamespaceTransformerPlugin struct { + types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func (p *NamespaceTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Namespace = "" + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error { + if len(p.Namespace) == 0 { + return nil + } + for _, r := range m.Resources() { + if r.IsNilOrEmpty() { + // Don't mutate empty objects? + continue + } + r.StorePreviousId() + if err := r.ApplyFilter(namespace.Filter{ + Namespace: p.Namespace, + FsSlice: p.FieldSpecs, + }); err != nil { + return err + } + matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals) + if len(matches) != 1 { + return fmt.Errorf( + "namespace transformation produces ID conflict: %+v", matches) + } + } + return nil +} + +func NewNamespaceTransformerPlugin() resmap.TransformerPlugin { + return &NamespaceTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchJson6902Transformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchJson6902Transformer.go new file mode 100644 index 000000000..cb02c4241 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchJson6902Transformer.go @@ -0,0 +1,105 @@ +// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + + jsonpatch "github.com/evanphx/json-patch" + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/filters/patchjson6902" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio/kioutil" + "sigs.k8s.io/yaml" +) + +type PatchJson6902TransformerPlugin struct { + ldr ifc.Loader + decodedPatch jsonpatch.Patch + Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` + JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` +} + +func (p *PatchJson6902TransformerPlugin) Config( + h *resmap.PluginHelpers, c []byte) (err error) { + p.ldr = h.Loader() + err = yaml.Unmarshal(c, p) + if err != nil { + return err + } + if p.Target.Name == "" { + return fmt.Errorf("must specify the target name") + } + if p.Path == "" && p.JsonOp == "" { + return fmt.Errorf("empty file path and empty jsonOp") + } + if p.Path != "" { + if p.JsonOp != "" { + return fmt.Errorf("must specify a file path or jsonOp, not both") + } + rawOp, err := p.ldr.Load(p.Path) + if err != nil { + return err + } + p.JsonOp = string(rawOp) + if p.JsonOp == "" { + return fmt.Errorf("patch file '%s' empty seems to be empty", p.Path) + } + } + if p.JsonOp[0] != '[' { + // if it doesn't seem to be JSON, imagine + // it is YAML, and convert to JSON. + op, err := yaml.YAMLToJSON([]byte(p.JsonOp)) + if err != nil { + return err + } + p.JsonOp = string(op) + } + p.decodedPatch, err = jsonpatch.DecodePatch([]byte(p.JsonOp)) + if err != nil { + return errors.Wrapf(err, "decoding %s", p.JsonOp) + } + if len(p.decodedPatch) == 0 { + return fmt.Errorf( + "patch appears to be empty; file=%s, JsonOp=%s", p.Path, p.JsonOp) + } + return err +} + +func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error { + if p.Target == nil { + return fmt.Errorf("must specify a target for patch %s", p.JsonOp) + } + resources, err := m.Select(*p.Target) + if err != nil { + return err + } + for _, res := range resources { + internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode) + + err = res.ApplyFilter(patchjson6902.Filter{ + Patch: p.JsonOp, + }) + if err != nil { + return err + } + + annotations := res.GetAnnotations() + for key, value := range internalAnnotations { + annotations[key] = value + } + err = res.SetAnnotations(annotations) + if err != nil { + return err + } + } + return nil +} + +func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin { + return &PatchJson6902TransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchStrategicMergeTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchStrategicMergeTransformer.go new file mode 100644 index 000000000..f93d1266d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchStrategicMergeTransformer.go @@ -0,0 +1,89 @@ +// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +type PatchStrategicMergeTransformerPlugin struct { + loadedPatches []*resource.Resource + Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"` + Patches string `json:"patches,omitempty" yaml:"patches,omitempty"` +} + +func (p *PatchStrategicMergeTransformerPlugin) Config( + h *resmap.PluginHelpers, c []byte) (err error) { + err = yaml.Unmarshal(c, p) + if err != nil { + return err + } + if len(p.Paths) == 0 && p.Patches == "" { + return fmt.Errorf("empty file path and empty patch content") + } + if len(p.Paths) != 0 { + patches, err := loadFromPaths(h, p.Paths) + if err != nil { + return err + } + p.loadedPatches = append(p.loadedPatches, patches...) + } + if p.Patches != "" { + patches, err := h.ResmapFactory().RF().SliceFromBytes([]byte(p.Patches)) + if err != nil { + return err + } + p.loadedPatches = append(p.loadedPatches, patches...) + } + if len(p.loadedPatches) == 0 { + return fmt.Errorf( + "patch appears to be empty; files=%v, Patch=%s", p.Paths, p.Patches) + } + return nil +} + +func loadFromPaths( + h *resmap.PluginHelpers, + paths []types.PatchStrategicMerge) ( + result []*resource.Resource, err error) { + var patches []*resource.Resource + for _, path := range paths { + // For legacy reasons, attempt to treat the path string as + // actual patch content. + patches, err = h.ResmapFactory().RF().SliceFromBytes([]byte(path)) + if err != nil { + // Failing that, treat it as a file path. + patches, err = h.ResmapFactory().RF().SliceFromPatches( + h.Loader(), []types.PatchStrategicMerge{path}) + if err != nil { + return + } + } + result = append(result, patches...) + } + return +} + +func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error { + for _, patch := range p.loadedPatches { + target, err := m.GetById(patch.OrgId()) + if err != nil { + return err + } + if err = m.ApplySmPatch( + resource.MakeIdSet([]*resource.Resource{target}), patch); err != nil { + return err + } + } + return nil +} + +func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin { + return &PatchStrategicMergeTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchTransformer.go new file mode 100644 index 000000000..dc51748c8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PatchTransformer.go @@ -0,0 +1,153 @@ +// Code generated by pluginator on PatchTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + "strings" + + jsonpatch "github.com/evanphx/json-patch" + "sigs.k8s.io/kustomize/api/filters/patchjson6902" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio/kioutil" + "sigs.k8s.io/yaml" +) + +type PatchTransformerPlugin struct { + loadedPatch *resource.Resource + decodedPatch jsonpatch.Patch + Path string `json:"path,omitempty" yaml:"path,omitempty"` + Patch string `json:"patch,omitempty" yaml:"patch,omitempty"` + Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` + Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"` +} + +func (p *PatchTransformerPlugin) Config( + h *resmap.PluginHelpers, c []byte) error { + err := yaml.Unmarshal(c, p) + if err != nil { + return err + } + p.Patch = strings.TrimSpace(p.Patch) + if p.Patch == "" && p.Path == "" { + return fmt.Errorf( + "must specify one of patch and path in\n%s", string(c)) + } + if p.Patch != "" && p.Path != "" { + return fmt.Errorf( + "patch and path can't be set at the same time\n%s", string(c)) + } + if p.Path != "" { + loaded, loadErr := h.Loader().Load(p.Path) + if loadErr != nil { + return loadErr + } + p.Patch = string(loaded) + } + + patchSM, errSM := h.ResmapFactory().RF().FromBytes([]byte(p.Patch)) + patchJson, errJson := jsonPatchFromBytes([]byte(p.Patch)) + if (errSM == nil && errJson == nil) || + (patchSM != nil && patchJson != nil) { + return fmt.Errorf( + "illegally qualifies as both an SM and JSON patch: [%v]", + p.Patch) + } + if errSM != nil && errJson != nil { + return fmt.Errorf( + "unable to parse SM or JSON patch from [%v]", p.Patch) + } + if errSM == nil { + p.loadedPatch = patchSM + if p.Options["allowNameChange"] { + p.loadedPatch.AllowNameChange() + } + if p.Options["allowKindChange"] { + p.loadedPatch.AllowKindChange() + } + } else { + p.decodedPatch = patchJson + } + return nil +} + +func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error { + if p.loadedPatch == nil { + return p.transformJson6902(m, p.decodedPatch) + } + // The patch was a strategic merge patch + return p.transformStrategicMerge(m, p.loadedPatch) +} + +// transformStrategicMerge applies the provided strategic merge patch +// to all the resources in the ResMap that match either the Target or +// the identifier of the patch. +func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap, patch *resource.Resource) error { + if p.Target == nil { + target, err := m.GetById(patch.OrgId()) + if err != nil { + return err + } + return target.ApplySmPatch(patch) + } + selected, err := m.Select(*p.Target) + if err != nil { + return err + } + return m.ApplySmPatch(resource.MakeIdSet(selected), patch) +} + +// transformJson6902 applies the provided json6902 patch +// to all the resources in the ResMap that match the Target. +func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error { + if p.Target == nil { + return fmt.Errorf("must specify a target for patch %s", p.Patch) + } + resources, err := m.Select(*p.Target) + if err != nil { + return err + } + for _, res := range resources { + res.StorePreviousId() + internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode) + err = res.ApplyFilter(patchjson6902.Filter{ + Patch: p.Patch, + }) + if err != nil { + return err + } + + annotations := res.GetAnnotations() + for key, value := range internalAnnotations { + annotations[key] = value + } + err = res.SetAnnotations(annotations) + } + return nil +} + +// jsonPatchFromBytes loads a Json 6902 patch from +// a bytes input +func jsonPatchFromBytes( + in []byte) (jsonpatch.Patch, error) { + ops := string(in) + if ops == "" { + return nil, fmt.Errorf("empty json patch operations") + } + + if ops[0] != '[' { + jsonOps, err := yaml.YAMLToJSON(in) + if err != nil { + return nil, err + } + ops = string(jsonOps) + } + return jsonpatch.DecodePatch([]byte(ops)) +} + +func NewPatchTransformerPlugin() resmap.TransformerPlugin { + return &PatchTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PrefixTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PrefixTransformer.go new file mode 100644 index 000000000..03040ce0f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/PrefixTransformer.go @@ -0,0 +1,96 @@ +// Code generated by pluginator on PrefixTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "errors" + + "sigs.k8s.io/kustomize/api/filters/prefix" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Add the given prefix to the field +type PrefixTransformerPlugin struct { + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + FieldSpecs types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +// TODO: Make this gvk skip list part of the config. +var prefixFieldSpecsToSkip = types.FsSlice{ + {Gvk: resid.Gvk{Kind: "CustomResourceDefinition"}}, + {Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"}}, + {Gvk: resid.Gvk{Kind: "Namespace"}}, +} + +func (p *PrefixTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Prefix = "" + p.FieldSpecs = nil + err = yaml.Unmarshal(c, p) + if err != nil { + return + } + if p.FieldSpecs == nil { + return errors.New("fieldSpecs is not expected to be nil") + } + return +} + +func (p *PrefixTransformerPlugin) Transform(m resmap.ResMap) error { + // Even if the Prefix is empty we want to proceed with the + // transformation. This allows to add contextual information + // to the resources (AddNamePrefix). + for _, r := range m.Resources() { + // TODO: move this test into the filter (i.e. make a better filter) + if p.shouldSkip(r.OrgId()) { + continue + } + id := r.OrgId() + // current default configuration contains + // only one entry: "metadata/name" with no GVK + for _, fs := range p.FieldSpecs { + // TODO: this is redundant to filter (but needed for now) + if !id.IsSelected(&fs.Gvk) { + continue + } + // TODO: move this test into the filter. + if fs.Path == "metadata/name" { + // "metadata/name" is the only field. + // this will add a prefix to the resource + // even if it is empty + + r.AddNamePrefix(p.Prefix) + if p.Prefix != "" { + // TODO: There are multiple transformers that can change a resource's name, and each makes a call to + // StorePreviousID(). We should make it so that we only call StorePreviousID once per kustomization layer + // to avoid storing intermediate names between transformations, to prevent intermediate name conflicts. + r.StorePreviousId() + } + } + if err := r.ApplyFilter(prefix.Filter{ + Prefix: p.Prefix, + FieldSpec: fs, + }); err != nil { + return err + } + } + } + return nil +} + +func (p *PrefixTransformerPlugin) shouldSkip(id resid.ResId) bool { + for _, path := range prefixFieldSpecsToSkip { + if id.IsSelected(&path.Gvk) { + return true + } + } + return false +} + +func NewPrefixTransformerPlugin() resmap.TransformerPlugin { + return &PrefixTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ReplacementTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ReplacementTransformer.go new file mode 100644 index 000000000..21e42c81e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ReplacementTransformer.go @@ -0,0 +1,59 @@ +// Code generated by pluginator on ReplacementTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/filters/replacement" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// Replace values in targets with values from a source +type ReplacementTransformerPlugin struct { + ReplacementList []types.ReplacementField `json:"replacements,omitempty" yaml:"replacements,omitempty"` + Replacements []types.Replacement `json:"omitempty" yaml:"omitempty"` +} + +func (p *ReplacementTransformerPlugin) Config( + h *resmap.PluginHelpers, c []byte) (err error) { + p.ReplacementList = []types.ReplacementField{} + if err := yaml.Unmarshal(c, p); err != nil { + return err + } + + for _, r := range p.ReplacementList { + if r.Path != "" && (r.Source != nil || len(r.Targets) != 0) { + return fmt.Errorf("cannot specify both path and inline replacement") + } + if r.Path != "" { + // load the replacement from the path + content, err := h.Loader().Load(r.Path) + if err != nil { + return err + } + repl := types.Replacement{} + if err := yaml.Unmarshal(content, &repl); err != nil { + return err + } + p.Replacements = append(p.Replacements, repl) + } else { + // replacement information is already loaded + p.Replacements = append(p.Replacements, r.Replacement) + } + } + return nil +} + +func (p *ReplacementTransformerPlugin) Transform(m resmap.ResMap) (err error) { + return m.ApplyFilter(replacement.Filter{ + Replacements: p.Replacements, + }) +} + +func NewReplacementTransformerPlugin() resmap.TransformerPlugin { + return &ReplacementTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ReplicaCountTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ReplicaCountTransformer.go new file mode 100644 index 000000000..120fab340 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ReplicaCountTransformer.go @@ -0,0 +1,73 @@ +// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/filters/replicacount" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/yaml" +) + +// Find matching replicas declarations and replace the count. +// Eases the kustomization configuration of replica changes. +type ReplicaCountTransformerPlugin struct { + Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func (p *ReplicaCountTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Replica = types.Replica{} + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error { + found := false + for _, fs := range p.FieldSpecs { + matcher := p.createMatcher(fs) + resList := m.GetMatchingResourcesByAnyId(matcher) + if len(resList) > 0 { + found = true + for _, r := range resList { + // There are redundant checks in the filter + // that we'll live with until resolution of + // https://github.com/kubernetes-sigs/kustomize/issues/2506 + err := r.ApplyFilter(replicacount.Filter{ + Replica: p.Replica, + FieldSpec: fs, + }) + if err != nil { + return err + } + } + } + } + + if !found { + gvks := make([]string, len(p.FieldSpecs)) + for i, replicaSpec := range p.FieldSpecs { + gvks[i] = replicaSpec.Gvk.String() + } + return fmt.Errorf("resource with name %s does not match a config with the following GVK %v", + p.Replica.Name, gvks) + } + + return nil +} + +// Match Replica.Name and FieldSpec +func (p *ReplicaCountTransformerPlugin) createMatcher(fs types.FieldSpec) resmap.IdMatcher { + return func(r resid.ResId) bool { + return r.Name == p.Replica.Name && r.Gvk.IsSelected(&fs.Gvk) + } +} + +func NewReplicaCountTransformerPlugin() resmap.TransformerPlugin { + return &ReplicaCountTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/SecretGenerator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/SecretGenerator.go new file mode 100644 index 000000000..5e8581eb9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/SecretGenerator.go @@ -0,0 +1,39 @@ +// Code generated by pluginator on SecretGenerator; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "sigs.k8s.io/kustomize/api/kv" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +type SecretGeneratorPlugin struct { + h *resmap.PluginHelpers + types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + types.SecretArgs +} + +func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) { + p.SecretArgs = types.SecretArgs{} + err = yaml.Unmarshal(config, p) + if p.SecretArgs.Name == "" { + p.SecretArgs.Name = p.Name + } + if p.SecretArgs.Namespace == "" { + p.SecretArgs.Namespace = p.Namespace + } + p.h = h + return +} + +func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) { + return p.h.ResmapFactory().FromSecretArgs( + kv.NewLoader(p.h.Loader(), p.h.Validator()), p.SecretArgs) +} + +func NewSecretGeneratorPlugin() resmap.GeneratorPlugin { + return &SecretGeneratorPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/SuffixTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/SuffixTransformer.go new file mode 100644 index 000000000..2b389d8e5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/SuffixTransformer.go @@ -0,0 +1,96 @@ +// Code generated by pluginator on SuffixTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "errors" + + "sigs.k8s.io/kustomize/api/filters/suffix" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Add the given suffix to the field +type SuffixTransformerPlugin struct { + Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` + FieldSpecs types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +// TODO: Make this gvk skip list part of the config. +var suffixFieldSpecsToSkip = types.FsSlice{ + {Gvk: resid.Gvk{Kind: "CustomResourceDefinition"}}, + {Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"}}, + {Gvk: resid.Gvk{Kind: "Namespace"}}, +} + +func (p *SuffixTransformerPlugin) Config( + _ *resmap.PluginHelpers, c []byte) (err error) { + p.Suffix = "" + p.FieldSpecs = nil + err = yaml.Unmarshal(c, p) + if err != nil { + return + } + if p.FieldSpecs == nil { + return errors.New("fieldSpecs is not expected to be nil") + } + return +} + +func (p *SuffixTransformerPlugin) Transform(m resmap.ResMap) error { + // Even if the Suffix is empty we want to proceed with the + // transformation. This allows to add contextual information + // to the resources (AddNameSuffix). + for _, r := range m.Resources() { + // TODO: move this test into the filter (i.e. make a better filter) + if p.shouldSkip(r.OrgId()) { + continue + } + id := r.OrgId() + // current default configuration contains + // only one entry: "metadata/name" with no GVK + for _, fs := range p.FieldSpecs { + // TODO: this is redundant to filter (but needed for now) + if !id.IsSelected(&fs.Gvk) { + continue + } + // TODO: move this test into the filter. + if fs.Path == "metadata/name" { + // "metadata/name" is the only field. + // this will add a suffix to the resource + // even if it is empty + + r.AddNameSuffix(p.Suffix) + if p.Suffix != "" { + // TODO: There are multiple transformers that can change a resource's name, and each makes a call to + // StorePreviousID(). We should make it so that we only call StorePreviousID once per kustomization layer + // to avoid storing intermediate names between transformations, to prevent intermediate name conflicts. + r.StorePreviousId() + } + } + if err := r.ApplyFilter(suffix.Filter{ + Suffix: p.Suffix, + FieldSpec: fs, + }); err != nil { + return err + } + } + } + return nil +} + +func (p *SuffixTransformerPlugin) shouldSkip(id resid.ResId) bool { + for _, path := range suffixFieldSpecsToSkip { + if id.IsSelected(&path.Gvk) { + return true + } + } + return false +} + +func NewSuffixTransformerPlugin() resmap.TransformerPlugin { + return &SuffixTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ValueAddTransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ValueAddTransformer.go new file mode 100644 index 000000000..c2c460ab9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/ValueAddTransformer.go @@ -0,0 +1,141 @@ +// Code generated by pluginator on ValueAddTransformer; DO NOT EDIT. +// pluginator {unknown 1970-01-01T00:00:00Z } + +package builtins + +import ( + "fmt" + "path/filepath" + "strings" + + "sigs.k8s.io/kustomize/api/filters/namespace" + "sigs.k8s.io/kustomize/api/filters/valueadd" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +// An 'Add' transformer inspired by the IETF RFC 6902 JSON spec Add operation. +type ValueAddTransformerPlugin struct { + // Value is the value to add. + // Defaults to base name of encompassing kustomization root. + Value string `json:"value,omitempty" yaml:"value,omitempty"` + + // Targets is a slice of targets that should have the value added. + Targets []Target `json:"targets,omitempty" yaml:"targets,omitempty"` + + // TargetFilePath is a file path. If specified, the file will be parsed into + // a slice of Target, and appended to anything that was specified in the + // Targets field. This is just a means to share common target specifications. + TargetFilePath string `json:"targetFilePath,omitempty" yaml:"targetFilePath,omitempty"` +} + +// Target describes where to put the value. +type Target struct { + // Selector selects the resources to modify. + Selector *types.Selector `json:"selector,omitempty" yaml:"selector,omitempty"` + + // NotSelector selects the resources to exclude + // from those included by overly broad selectors. + // TODO: implement this? + // NotSelector *types.Selector `json:"notSelector,omitempty" yaml:"notSelector,omitempty"` + + // FieldPath is a JSON-style path to the field intended to hold the value. + FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"` + + // FilePathPosition is passed to the filter directly. Look there for doc. + FilePathPosition int `json:"filePathPosition,omitempty" yaml:"filePathPosition,omitempty"` +} + +func (p *ValueAddTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error { + err := yaml.Unmarshal(c, p) + if err != nil { + return err + } + p.Value = strings.TrimSpace(p.Value) + if p.Value == "" { + p.Value = filepath.Base(h.Loader().Root()) + } + if p.TargetFilePath != "" { + bytes, err := h.Loader().Load(p.TargetFilePath) + if err != nil { + return err + } + var targets struct { + Targets []Target `json:"targets,omitempty" yaml:"targets,omitempty"` + } + err = yaml.Unmarshal(bytes, &targets) + if err != nil { + return err + } + p.Targets = append(p.Targets, targets.Targets...) + } + if len(p.Targets) == 0 { + return fmt.Errorf("must specify at least one target") + } + for _, target := range p.Targets { + if err = validateSelector(target.Selector); err != nil { + return err + } + // TODO: call validateSelector(target.NotSelector) if field added. + if err = validateJsonFieldPath(target.FieldPath); err != nil { + return err + } + if target.FilePathPosition < 0 { + return fmt.Errorf( + "value of FilePathPosition (%d) cannot be negative", + target.FilePathPosition) + } + } + return nil +} + +// TODO: implement +func validateSelector(_ *types.Selector) error { + return nil +} + +// TODO: Enforce RFC 6902? +func validateJsonFieldPath(p string) error { + if len(p) == 0 { + return fmt.Errorf("fieldPath cannot be empty") + } + return nil +} + +func (p *ValueAddTransformerPlugin) Transform(m resmap.ResMap) (err error) { + for _, t := range p.Targets { + var resources []*resource.Resource + if t.Selector == nil { + resources = m.Resources() + } else { + resources, err = m.Select(*t.Selector) + if err != nil { + return err + } + } + // TODO: consider t.NotSelector if implemented + for _, res := range resources { + if t.FieldPath == types.MetadataNamespacePath { + err = res.ApplyFilter(namespace.Filter{ + Namespace: p.Value, + }) + } else { + err = res.ApplyFilter(valueadd.Filter{ + Value: p.Value, + FieldPath: t.FieldPath, + FilePathPosition: t.FilePathPosition, + }) + } + if err != nil { + return err + } + } + } + return nil +} + +func NewValueAddTransformerPlugin() resmap.TransformerPlugin { + return &ValueAddTransformerPlugin{} +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/doc.go new file mode 100644 index 000000000..37a8dc6e7 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/builtins/doc.go @@ -0,0 +1,8 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package builtins holds code generated from the builtin plugins. +// The "builtin" plugins are written as normal plugins and can +// be used as such, but they are also used to generate the code +// in this package so they can be statically linked to client code. +package builtins diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/configmap.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/configmap.go new file mode 100644 index 000000000..47498aaa5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/configmap.go @@ -0,0 +1,52 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generators + +import ( + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// MakeConfigMap makes a configmap. +// +// ConfigMap: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#configmap-v1-core +// +// ConfigMaps and Secrets are similar. +// +// Both objects have a `data` field, which contains a map from keys to +// values that must be UTF-8 valid strings. Such data might be simple text, +// or whoever made the data may have done so by performing a base64 encoding +// on binary data. Regardless, k8s has no means to know this, so it treats +// the data field as a string. +// +// The ConfigMap has an additional field `binaryData`, also a map, but its +// values are _intended_ to be interpreted as a base64 encoding of []byte, +// by whatever makes use of the ConfigMap. +// +// In a ConfigMap, any key used in `data` cannot also be used in `binaryData` +// and vice-versa. A key must be unique across both maps. +func MakeConfigMap( + ldr ifc.KvLoader, args *types.ConfigMapArgs) (rn *yaml.RNode, err error) { + rn, err = makeBaseNode("ConfigMap", args.Name, args.Namespace) + if err != nil { + return nil, err + } + m, err := makeValidatedDataMap(ldr, args.Name, args.KvPairSources) + if err != nil { + return nil, err + } + if err = rn.LoadMapIntoConfigMapData(m); err != nil { + return nil, err + } + err = copyLabelsAndAnnotations(rn, args.Options) + if err != nil { + return nil, err + } + err = setImmutable(rn, args.Options) + if err != nil { + return nil, err + } + return rn, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/configmap_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/configmap_test.go new file mode 100644 index 000000000..f42ba8f70 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/configmap_test.go @@ -0,0 +1,225 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generators_test + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/internal/generators" + "sigs.k8s.io/kustomize/api/kv" + "sigs.k8s.io/kustomize/api/loader" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +var binaryHello = []byte{ + 0xff, // non-utf8 + 0x68, // h + 0x65, // e + 0x6c, // l + 0x6c, // l + 0x6f, // o +} + +func manyHellos(count int) (result []byte) { + for i := 0; i < count; i++ { + result = append(result, binaryHello...) + } + return +} + +func TestMakeConfigMap(t *testing.T) { + type expected struct { + out string + errMsg string + } + + testCases := map[string]struct { + args types.ConfigMapArgs + exp expected + }{ + "construct config map from env": { + args: types.ConfigMapArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "envConfigMap", + KvPairSources: types.KvPairSources{ + EnvSources: []string{ + filepath.Join("configmap", "app.env"), + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: ConfigMap +metadata: + name: envConfigMap +data: + DB_PASSWORD: qwerty + DB_USERNAME: admin +`, + }, + }, + "construct config map from text file": { + args: types.ConfigMapArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "fileConfigMap1", + KvPairSources: types.KvPairSources{ + FileSources: []string{ + filepath.Join("configmap", "app-init.ini"), + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: ConfigMap +metadata: + name: fileConfigMap1 +data: + app-init.ini: | + FOO=bar + BAR=baz +`, + }, + }, + "construct config map from text and binary file": { + args: types.ConfigMapArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "fileConfigMap2", + KvPairSources: types.KvPairSources{ + FileSources: []string{ + filepath.Join("configmap", "app-init.ini"), + filepath.Join("configmap", "app.bin"), + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: ConfigMap +metadata: + name: fileConfigMap2 +data: + app-init.ini: | + FOO=bar + BAR=baz +binaryData: + app.bin: | + /2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbG + xv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hl + bGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2 + hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv +`, + }, + }, + "construct config map from literal": { + args: types.ConfigMapArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "literalConfigMap1", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"}, + }, + Options: &types.GeneratorOptions{ + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: ConfigMap +metadata: + name: literalConfigMap1 + labels: + foo: 'bar' +data: + a: x + b: y + c: Hello World + d: "true" +`, + }, + }, + "construct config map from literal with GeneratorOptions in ConfigMapArgs": { + args: types.ConfigMapArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "literalConfigMap2", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"}, + }, + Options: &types.GeneratorOptions{ + Labels: map[string]string{ + "veggie": "celery", + "dog": "beagle", + "cat": "annoying", + }, + Annotations: map[string]string{ + "river": "Missouri", + "city": "Iowa City", + }, + Immutable: true, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: ConfigMap +metadata: + name: literalConfigMap2 + labels: + cat: 'annoying' + dog: 'beagle' + veggie: 'celery' + annotations: + city: 'Iowa City' + river: 'Missouri' +data: + a: x + b: y + c: Hello World + d: "true" +immutable: true +`, + }, + }, + } + fSys := filesys.MakeFsInMemory() + fSys.WriteFile( + filesys.RootedPath("configmap", "app.env"), + []byte("DB_USERNAME=admin\nDB_PASSWORD=qwerty\n")) + fSys.WriteFile( + filesys.RootedPath("configmap", "app-init.ini"), + []byte("FOO=bar\nBAR=baz\n")) + fSys.WriteFile( + filesys.RootedPath("configmap", "app.bin"), + manyHellos(30)) + kvLdr := kv.NewLoader( + loader.NewFileLoaderAtRoot(fSys), + valtest_test.MakeFakeValidator()) + + for n := range testCases { + tc := testCases[n] + t.Run(n, func(t *testing.T) { + rn, err := MakeConfigMap(kvLdr, &tc.args) + if err != nil { + if !assert.EqualError(t, err, tc.exp.errMsg) { + t.FailNow() + } + return + } + if tc.exp.errMsg != "" { + t.Fatalf("%s: should return error '%s'", n, tc.exp.errMsg) + } + output := rn.MustString() + if !assert.Equal(t, tc.exp.out, output) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/secret.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/secret.go new file mode 100644 index 000000000..9afaff156 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/secret.go @@ -0,0 +1,59 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generators + +import ( + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// MakeSecret makes a kubernetes Secret. +// +// Secret: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#secret-v1-core +// +// ConfigMaps and Secrets are similar. +// +// Like a ConfigMap, a Secret has a `data` field, but unlike a ConfigMap it has +// no `binaryData` field. +// +// All of a Secret's data is assumed to be opaque in nature, and assumed to be +// base64 encoded from its original representation, regardless of whether the +// original data was UTF-8 text or binary. +// +// This encoding provides no secrecy. It's just a neutral, common means to +// represent opaque text and binary data. Beneath the base64 encoding +// is presumably further encoding under control of the Secret's consumer. +// +// A Secret has string field `type` which holds an identifier, used by the +// client, to choose the algorithm to interpret the `data` field. Kubernetes +// cannot make use of this data; it's up to a controller or some pod's service +// to interpret the value, using `type` as a clue as to how to do this. +func MakeSecret( + ldr ifc.KvLoader, args *types.SecretArgs) (rn *yaml.RNode, err error) { + rn, err = makeBaseNode("Secret", args.Name, args.Namespace) + if err != nil { + return nil, err + } + t := "Opaque" + if args.Type != "" { + t = args.Type + } + if _, err := rn.Pipe( + yaml.FieldSetter{ + Name: "type", + Value: yaml.NewStringRNode(t)}); err != nil { + return nil, err + } + m, err := makeValidatedDataMap(ldr, args.Name, args.KvPairSources) + if err != nil { + return nil, err + } + if err = rn.LoadMapIntoSecretData(m); err != nil { + return nil, err + } + copyLabelsAndAnnotations(rn, args.Options) + setImmutable(rn, args.Options) + return rn, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/secret_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/secret_test.go new file mode 100644 index 000000000..04829531e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/secret_test.go @@ -0,0 +1,233 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generators_test + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/internal/generators" + "sigs.k8s.io/kustomize/api/kv" + "sigs.k8s.io/kustomize/api/loader" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestMakeSecret(t *testing.T) { + type expected struct { + out string + errMsg string + } + + testCases := map[string]struct { + args types.SecretArgs + exp expected + }{ + "construct secret from env": { + args: types.SecretArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "envSecret", + KvPairSources: types.KvPairSources{ + EnvSources: []string{ + filepath.Join("secret", "app.env"), + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: Secret +metadata: + name: envSecret +type: Opaque +data: + DB_PASSWORD: cXdlcnR5 + DB_USERNAME: YWRtaW4= +`, + }, + }, + "construct secret from text file": { + args: types.SecretArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "fileSecret1", + KvPairSources: types.KvPairSources{ + FileSources: []string{ + filepath.Join("secret", "app-init.ini"), + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: Secret +metadata: + name: fileSecret1 +type: Opaque +data: + app-init.ini: Rk9PPWJhcgpCQVI9YmF6Cg== +`, + }, + }, + "construct secret from text and binary file": { + args: types.SecretArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "fileSecret2", + KvPairSources: types.KvPairSources{ + FileSources: []string{ + filepath.Join("secret", "app-init.ini"), + filepath.Join("secret", "app.bin"), + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: Secret +metadata: + name: fileSecret2 +type: Opaque +data: + app-init.ini: Rk9PPWJhcgpCQVI9YmF6Cg== + app.bin: //0= +`, + }, + }, + "construct secret from literal": { + args: types.SecretArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "literalSecret1", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"}, + }, + Options: &types.GeneratorOptions{ + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: Secret +metadata: + name: literalSecret1 + labels: + foo: 'bar' +type: Opaque +data: + a: eA== + b: eQ== + c: SGVsbG8gV29ybGQ= + d: dHJ1ZQ== +`, + }, + }, + "construct secret with type": { + args: types.SecretArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "literalSecret1", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{"a=x"}, + }, + Options: &types.GeneratorOptions{ + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + Type: "foobar", + }, + exp: expected{ + out: `apiVersion: v1 +kind: Secret +metadata: + name: literalSecret1 + labels: + foo: 'bar' +type: foobar +data: + a: eA== +`, + }, + }, + "construct secret from literal with GeneratorOptions in SecretArgs": { + args: types.SecretArgs{ + GeneratorArgs: types.GeneratorArgs{ + Name: "literalSecret2", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"}, + }, + Options: &types.GeneratorOptions{ + Labels: map[string]string{ + "veggie": "celery", + "dog": "beagle", + "cat": "annoying", + }, + Annotations: map[string]string{ + "river": "Missouri", + "city": "Iowa City", + }, + Immutable: true, + }, + }, + }, + exp: expected{ + out: `apiVersion: v1 +kind: Secret +metadata: + name: literalSecret2 + labels: + cat: 'annoying' + dog: 'beagle' + veggie: 'celery' + annotations: + city: 'Iowa City' + river: 'Missouri' +type: Opaque +data: + a: eA== + b: eQ== + c: SGVsbG8gV29ybGQ= + d: dHJ1ZQ== +immutable: true +`, + }, + }, + } + fSys := filesys.MakeFsInMemory() + fSys.WriteFile( + filesys.RootedPath("secret", "app.env"), + []byte("DB_USERNAME=admin\nDB_PASSWORD=qwerty\n")) + fSys.WriteFile( + filesys.RootedPath("secret", "app-init.ini"), + []byte("FOO=bar\nBAR=baz\n")) + fSys.WriteFile( + filesys.RootedPath("secret", "app.bin"), + []byte{0xff, 0xfd}) + kvLdr := kv.NewLoader( + loader.NewFileLoaderAtRoot(fSys), + valtest_test.MakeFakeValidator()) + + for n := range testCases { + tc := testCases[n] + t.Run(n, func(t *testing.T) { + rn, err := MakeSecret(kvLdr, &tc.args) + if err != nil { + if !assert.EqualError(t, err, tc.exp.errMsg) { + t.FailNow() + } + return + } + if tc.exp.errMsg != "" { + t.Fatalf("%s: should return error '%s'", n, tc.exp.errMsg) + } + output := rn.MustString() + if !assert.Equal(t, tc.exp.out, output) { + t.FailNow() + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/utils.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/utils.go new file mode 100644 index 000000000..82040d420 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/generators/utils.go @@ -0,0 +1,91 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generators + +import ( + "fmt" + + "github.com/go-errors/errors" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func makeBaseNode(kind, name, namespace string) (*yaml.RNode, error) { + rn, err := yaml.Parse(fmt.Sprintf(` +apiVersion: v1 +kind: %s +`, kind)) + if err != nil { + return nil, err + } + if name == "" { + return nil, errors.Errorf("a configmap must have a name") + } + if _, err := rn.Pipe(yaml.SetK8sName(name)); err != nil { + return nil, err + } + if namespace != "" { + if _, err := rn.Pipe(yaml.SetK8sNamespace(namespace)); err != nil { + return nil, err + } + } + return rn, nil +} + +func makeValidatedDataMap( + ldr ifc.KvLoader, name string, sources types.KvPairSources) (map[string]string, error) { + pairs, err := ldr.Load(sources) + if err != nil { + return nil, errors.WrapPrefix(err, "loading KV pairs", 0) + } + knownKeys := make(map[string]string) + for _, p := range pairs { + // legal key: alphanumeric characters, '-', '_' or '.' + if err := ldr.Validator().ErrIfInvalidKey(p.Key); err != nil { + return nil, err + } + if _, ok := knownKeys[p.Key]; ok { + return nil, errors.Errorf( + "configmap %s illegally repeats the key `%s`", name, p.Key) + } + knownKeys[p.Key] = p.Value + } + return knownKeys, nil +} + +// copyLabelsAndAnnotations copies labels and annotations from +// GeneratorOptions into the given object. +func copyLabelsAndAnnotations( + rn *yaml.RNode, opts *types.GeneratorOptions) error { + if opts == nil { + return nil + } + for _, k := range yaml.SortedMapKeys(opts.Labels) { + v := opts.Labels[k] + if _, err := rn.Pipe(yaml.SetLabel(k, v)); err != nil { + return err + } + } + for _, k := range yaml.SortedMapKeys(opts.Annotations) { + v := opts.Annotations[k] + if _, err := rn.Pipe(yaml.SetAnnotation(k, v)); err != nil { + return err + } + } + return nil +} + +func setImmutable( + rn *yaml.RNode, opts *types.GeneratorOptions) error { + if opts == nil { + return nil + } + if opts.Immutable { + if _, err := rn.Pipe(yaml.SetField("immutable", yaml.NewScalarRNode("true"))); err != nil { + return err + } + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/cloner.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/cloner.go new file mode 100644 index 000000000..78f4f32cb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/cloner.go @@ -0,0 +1,54 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package git + +import ( + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// Cloner is a function that can clone a git repo. +type Cloner func(repoSpec *RepoSpec) error + +// ClonerUsingGitExec uses a local git install, as opposed +// to say, some remote API, to obtain a local clone of +// a remote repo. +func ClonerUsingGitExec(repoSpec *RepoSpec) error { + r, err := newCmdRunner(repoSpec.Timeout) + if err != nil { + return err + } + repoSpec.Dir = r.dir + if err = r.run("init"); err != nil { + return err + } + if err = r.run( + "remote", "add", "origin", repoSpec.CloneSpec()); err != nil { + return err + } + ref := "HEAD" + if repoSpec.Ref != "" { + ref = repoSpec.Ref + } + if err = r.run("fetch", "--depth=1", "origin", ref); err != nil { + return err + } + if err = r.run("checkout", "FETCH_HEAD"); err != nil { + return err + } + if repoSpec.Submodules { + return r.run("submodule", "update", "--init", "--recursive") + } + return nil +} + +// DoNothingCloner returns a cloner that only sets +// cloneDir field in the repoSpec. It's assumed that +// the cloneDir is associated with some fake filesystem +// used in a test. +func DoNothingCloner(dir filesys.ConfirmedDir) Cloner { + return func(rs *RepoSpec) error { + rs.Dir = dir + return nil + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/gitrunner.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/gitrunner.go new file mode 100644 index 000000000..fcc7130cd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/gitrunner.go @@ -0,0 +1,55 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package git + +import ( + "os/exec" + "time" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// gitRunner runs the external git binary. +type gitRunner struct { + gitProgram string + duration time.Duration + dir filesys.ConfirmedDir +} + +// newCmdRunner returns a gitRunner if it can find the binary. +// It also creats a temp directory for cloning repos. +func newCmdRunner(timeout time.Duration) (*gitRunner, error) { + gitProgram, err := exec.LookPath("git") + if err != nil { + return nil, errors.Wrap(err, "no 'git' program on path") + } + dir, err := filesys.NewTmpConfirmedDir() + if err != nil { + return nil, err + } + return &gitRunner{ + gitProgram: gitProgram, + duration: timeout, + dir: dir, + }, nil +} + +// run a command with a timeout. +func (r gitRunner) run(args ...string) error { + //nolint: gosec + cmd := exec.Command(r.gitProgram, args...) + cmd.Dir = r.dir.String() + return utils.TimedCall( + cmd.String(), + r.duration, + func() error { + _, err := cmd.CombinedOutput() + if err != nil { + return errors.Wrapf(err, "git cmd = '%s'", cmd.String()) + } + return err + }) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/repospec.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/repospec.go new file mode 100644 index 000000000..10ebb980a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/repospec.go @@ -0,0 +1,268 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package git + +import ( + "fmt" + "net/url" + "path/filepath" + "strconv" + "strings" + "time" + + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// Used as a temporary non-empty occupant of the cloneDir +// field, as something distinguishable from the empty string +// in various outputs (especially tests). Not using an +// actual directory name here, as that's a temporary directory +// with a unique name that isn't created until clone time. +const notCloned = filesys.ConfirmedDir("/notCloned") + +// RepoSpec specifies a git repository and a branch and path therein. +type RepoSpec struct { + // Raw, original spec, used to look for cycles. + // TODO(monopole): Drop raw, use processed fields instead. + raw string + + // Host, e.g. github.com + Host string + + // orgRepo name (organization/repoName), + // e.g. kubernetes-sigs/kustomize + OrgRepo string + + // Dir where the orgRepo is cloned to. + Dir filesys.ConfirmedDir + + // Relative path in the repository, and in the cloneDir, + // to a Kustomization. + Path string + + // Branch or tag reference. + Ref string + + // e.g. .git or empty in case of _git is present + GitSuffix string + + // Submodules indicates whether or not to clone git submodules. + Submodules bool + + // Timeout is the maximum duration allowed for execing git commands. + Timeout time.Duration +} + +// CloneSpec returns a string suitable for "git clone {spec}". +func (x *RepoSpec) CloneSpec() string { + if isAzureHost(x.Host) || isAWSHost(x.Host) { + return x.Host + x.OrgRepo + } + return x.Host + x.OrgRepo + x.GitSuffix +} + +func (x *RepoSpec) CloneDir() filesys.ConfirmedDir { + return x.Dir +} + +func (x *RepoSpec) Raw() string { + return x.raw +} + +func (x *RepoSpec) AbsPath() string { + return x.Dir.Join(x.Path) +} + +func (x *RepoSpec) Cleaner(fSys filesys.FileSystem) func() error { + return func() error { return fSys.RemoveAll(x.Dir.String()) } +} + +// NewRepoSpecFromUrl parses git-like urls. +// From strings like git@github.com:someOrg/someRepo.git or +// https://github.com/someOrg/someRepo?ref=someHash, extract +// the parts. +func NewRepoSpecFromUrl(n string) (*RepoSpec, error) { + if filepath.IsAbs(n) { + return nil, fmt.Errorf("uri looks like abs path: %s", n) + } + host, orgRepo, path, gitRef, gitSubmodules, suffix, gitTimeout := parseGitUrl(n) + if orgRepo == "" { + return nil, fmt.Errorf("url lacks orgRepo: %s", n) + } + if host == "" { + return nil, fmt.Errorf("url lacks host: %s", n) + } + return &RepoSpec{ + raw: n, Host: host, OrgRepo: orgRepo, + Dir: notCloned, Path: path, Ref: gitRef, GitSuffix: suffix, + Submodules: gitSubmodules, Timeout: gitTimeout}, nil +} + +const ( + refQuery = "?ref=" + gitSuffix = ".git" + gitDelimiter = "_git/" +) + +// From strings like git@github.com:someOrg/someRepo.git or +// https://github.com/someOrg/someRepo?ref=someHash, extract +// the parts. +func parseGitUrl(n string) ( + host string, orgRepo string, path string, gitRef string, gitSubmodules bool, gitSuff string, gitTimeout time.Duration) { + + if strings.Contains(n, gitDelimiter) { + index := strings.Index(n, gitDelimiter) + // Adding _git/ to host + host = normalizeGitHostSpec(n[:index+len(gitDelimiter)]) + orgRepo = strings.Split(strings.Split(n[index+len(gitDelimiter):], "/")[0], "?")[0] + path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[index+len(gitDelimiter)+len(orgRepo):]) + return + } + host, n = parseHostSpec(n) + gitSuff = gitSuffix + if strings.Contains(n, gitSuffix) { + index := strings.Index(n, gitSuffix) + orgRepo = n[0:index] + n = n[index+len(gitSuffix):] + if len(n) > 0 && n[0] == '/' { + n = n[1:] + } + path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) + return + } + + i := strings.Index(n, "/") + if i < 1 { + path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) + return + } + j := strings.Index(n[i+1:], "/") + if j >= 0 { + j += i + 1 + orgRepo = n[:j] + path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[j+1:]) + return + } + path = "" + orgRepo, gitRef, gitTimeout, gitSubmodules = peelQuery(n) + return host, orgRepo, path, gitRef, gitSubmodules, gitSuff, gitTimeout +} + +// Clone git submodules by default. +const defaultSubmodules = true + +// Arbitrary, but non-infinite, timeout for running commands. +const defaultTimeout = 27 * time.Second + +func peelQuery(arg string) (string, string, time.Duration, bool) { + // Parse the given arg into a URL. In the event of a parse failure, return + // our defaults. + parsed, err := url.Parse(arg) + if err != nil { + return arg, "", defaultTimeout, defaultSubmodules + } + values := parsed.Query() + + // ref is the desired git ref to target. Can be specified by in a git URL + // with ?ref= or ?version=, although ref takes precedence. + ref := values.Get("version") + if queryValue := values.Get("ref"); queryValue != "" { + ref = queryValue + } + + // depth is the desired git exec timeout. Can be specified by in a git URL + // with ?timeout=. + duration := defaultTimeout + if queryValue := values.Get("timeout"); queryValue != "" { + // Attempt to first parse as a number of integer seconds (like "61"), + // and then attempt to parse as a suffixed duration (like "61s"). + if intValue, err := strconv.Atoi(queryValue); err == nil && intValue > 0 { + duration = time.Duration(intValue) * time.Second + } else if durationValue, err := time.ParseDuration(queryValue); err == nil && durationValue > 0 { + duration = durationValue + } + } + + // submodules indicates if git submodule cloning is desired. Can be + // specified by in a git URL with ?submodules=. + submodules := defaultSubmodules + if queryValue := values.Get("submodules"); queryValue != "" { + if boolValue, err := strconv.ParseBool(queryValue); err == nil { + submodules = boolValue + } + } + + return parsed.Path, ref, duration, submodules +} + +func parseHostSpec(n string) (string, string) { + var host string + // Start accumulating the host part. + for _, p := range []string{ + // Order matters here. + "git::", "gh:", "ssh://", "https://", "http://", + "git@", "github.com:", "github.com/"} { + if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p { + n = n[len(p):] + host += p + } + } + if host == "git@" { + i := strings.Index(n, "/") + if i > -1 { + host += n[:i+1] + n = n[i+1:] + } else { + i = strings.Index(n, ":") + if i > -1 { + host += n[:i+1] + n = n[i+1:] + } + } + return host, n + } + + // If host is a http(s) or ssh URL, grab the domain part. + for _, p := range []string{ + "ssh://", "https://", "http://"} { + if strings.HasSuffix(host, p) { + i := strings.Index(n, "/") + if i > -1 { + host = host + n[0:i+1] + n = n[i+1:] + } + break + } + } + + return normalizeGitHostSpec(host), n +} + +func normalizeGitHostSpec(host string) string { + s := strings.ToLower(host) + if strings.Contains(s, "github.com") { + if strings.Contains(s, "git@") || strings.Contains(s, "ssh:") { + host = "git@github.com:" + } else { + host = "https://github.com/" + } + } + if strings.HasPrefix(s, "git::") { + host = strings.TrimPrefix(s, "git::") + } + return host +} + +// The format of Azure repo URL is documented +// https://docs.microsoft.com/en-us/azure/devops/repos/git/clone?view=vsts&tabs=visual-studio#clone_url +func isAzureHost(host string) bool { + return strings.Contains(host, "dev.azure.com") || + strings.Contains(host, "visualstudio.com") +} + +// The format of AWS repo URL is documented +// https://docs.aws.amazon.com/codecommit/latest/userguide/regions.html +func isAWSHost(host string) bool { + return strings.Contains(host, "amazonaws.com") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/repospec_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/repospec_test.go new file mode 100644 index 000000000..2e4404d03 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/git/repospec_test.go @@ -0,0 +1,393 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package git + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var orgRepos = []string{"someOrg/someRepo", "kubernetes/website"} + +var pathNames = []string{"README.md", "foo/krusty.txt", ""} + +var hrefArgs = []string{"someBranch", "master", "v0.1.0", ""} + +var hostNamesRawAndNormalized = [][]string{ + {"gh:", "gh:"}, + {"GH:", "gh:"}, + {"gitHub.com/", "https://github.com/"}, + {"github.com:", "https://github.com/"}, + {"http://github.com/", "https://github.com/"}, + {"https://github.com/", "https://github.com/"}, + {"hTTps://github.com/", "https://github.com/"}, + {"https://git-codecommit.us-east-2.amazonaws.com/", "https://git-codecommit.us-east-2.amazonaws.com/"}, + {"https://fabrikops2.visualstudio.com/", "https://fabrikops2.visualstudio.com/"}, + {"ssh://git.example.com:7999/", "ssh://git.example.com:7999/"}, + {"git::https://gitlab.com/", "https://gitlab.com/"}, + {"git::http://git.example.com/", "http://git.example.com/"}, + {"git::https://git.example.com/", "https://git.example.com/"}, + {"git@github.com:", "git@github.com:"}, + {"git@github.com/", "git@github.com:"}, +} + +func makeUrl(hostFmt, orgRepo, path, href string) string { + if len(path) > 0 { + orgRepo = filepath.Join(orgRepo, path) + } + url := hostFmt + orgRepo + if href != "" { + url += refQuery + href + } + return url +} + +func TestNewRepoSpecFromUrl(t *testing.T) { + var bad [][]string + for _, tuple := range hostNamesRawAndNormalized { + hostRaw := tuple[0] + hostSpec := tuple[1] + for _, orgRepo := range orgRepos { + for _, pathName := range pathNames { + for _, hrefArg := range hrefArgs { + uri := makeUrl(hostRaw, orgRepo, pathName, hrefArg) + rs, err := NewRepoSpecFromUrl(uri) + if err != nil { + t.Errorf("problem %v", err) + } + if rs.Host != hostSpec { + bad = append(bad, []string{"host", uri, rs.Host, hostSpec}) + } + if rs.OrgRepo != orgRepo { + bad = append(bad, []string{"orgRepo", uri, rs.OrgRepo, orgRepo}) + } + if rs.Path != pathName { + bad = append(bad, []string{"path", uri, rs.Path, pathName}) + } + if rs.Ref != hrefArg { + bad = append(bad, []string{"ref", uri, rs.Ref, hrefArg}) + } + } + } + } + } + if len(bad) > 0 { + for _, tuple := range bad { + fmt.Printf("\n"+ + " from uri: %s\n"+ + " actual %4s: %s\n"+ + "expected %4s: %s\n", + tuple[1], tuple[0], tuple[2], tuple[0], tuple[3]) + } + t.Fail() + } +} + +var badData = [][]string{ + {"/tmp", "uri looks like abs path"}, + {"iauhsdiuashduas", "url lacks orgRepo"}, + {"htxxxtp://github.com/", "url lacks host"}, + {"ssh://git.example.com", "url lacks orgRepo"}, + {"git::___", "url lacks orgRepo"}, +} + +func TestNewRepoSpecFromUrlErrors(t *testing.T) { + for _, tuple := range badData { + _, err := NewRepoSpecFromUrl(tuple[0]) + if err == nil { + t.Error("expected error") + } + if !strings.Contains(err.Error(), tuple[1]) { + t.Errorf("unexpected error: %s", err) + } + } +} + +func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) { + testcases := map[string]struct { + input string + cloneSpec string + absPath string + ref string + }{ + "t1": { + input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir", + cloneSpec: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo", + absPath: notCloned.Join("somedir"), + ref: "", + }, + "t2": { + input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir?ref=testbranch", + cloneSpec: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo", + absPath: notCloned.Join("somedir"), + ref: "testbranch", + }, + "t3": { + input: "https://fabrikops2.visualstudio.com/someorg/somerepo?ref=master", + cloneSpec: "https://fabrikops2.visualstudio.com/someorg/somerepo", + absPath: notCloned.String(), + ref: "master", + }, + "t4": { + input: "http://github.com/someorg/somerepo/somedir", + cloneSpec: "https://github.com/someorg/somerepo.git", + absPath: notCloned.Join("somedir"), + ref: "", + }, + "t5": { + input: "git@github.com:someorg/somerepo/somedir", + cloneSpec: "git@github.com:someorg/somerepo.git", + absPath: notCloned.Join("somedir"), + ref: "", + }, + "t6": { + input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?ref=v0.1.0", + cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git", + absPath: notCloned.String(), + ref: "v0.1.0", + }, + "t7": { + input: "git@bitbucket.org:company/project.git//path?ref=branch", + cloneSpec: "git@bitbucket.org:company/project.git", + absPath: notCloned.Join("path"), + ref: "branch", + }, + "t8": { + input: "https://itfs.mycompany.com/collection/project/_git/somerepos", + cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", + absPath: notCloned.String(), + ref: "", + }, + "t9": { + input: "https://itfs.mycompany.com/collection/project/_git/somerepos?version=v1.0.0", + cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", + absPath: notCloned.String(), + ref: "v1.0.0", + }, + "t10": { + input: "https://itfs.mycompany.com/collection/project/_git/somerepos/somedir?version=v1.0.0", + cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", + absPath: notCloned.Join("somedir"), + ref: "v1.0.0", + }, + "t11": { + input: "git::https://itfs.mycompany.com/collection/project/_git/somerepos", + cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos", + absPath: notCloned.String(), + ref: "", + }, + "t12": { + input: "https://bitbucket.example.com/scm/project/repository.git", + cloneSpec: "https://bitbucket.example.com/scm/project/repository.git", + absPath: notCloned.String(), + ref: "", + }, + } + for tn, tc := range testcases { + t.Run(tn, func(t *testing.T) { + rs, err := NewRepoSpecFromUrl(tc.input) + assert.NoError(t, err) + assert.Equal(t, tc.cloneSpec, rs.CloneSpec(), "cloneSpec mismatch") + assert.Equal(t, tc.absPath, rs.AbsPath(), "absPath mismatch") + assert.Equal(t, tc.ref, rs.Ref, "ref mismatch") + }) + } +} + +func TestIsAzureHost(t *testing.T) { + testcases := []struct { + input string + expect bool + }{ + { + input: "https://git-codecommit.us-east-2.amazonaws.com", + expect: false, + }, + { + input: "ssh://git-codecommit.us-east-2.amazonaws.com", + expect: false, + }, + { + input: "https://fabrikops2.visualstudio.com/", + expect: true, + }, + { + input: "https://dev.azure.com/myorg/myproject/", + expect: true, + }, + } + for _, testcase := range testcases { + actual := isAzureHost(testcase.input) + if actual != testcase.expect { + t.Errorf("IsAzureHost: expected %v, but got %v on %s", testcase.expect, actual, testcase.input) + } + } +} + +func TestPeelQuery(t *testing.T) { + testcases := map[string]struct { + input string + path string + ref string + submodules bool + timeout time.Duration + }{ + "t1": { + // All empty. + input: "somerepos", + path: "somerepos", + ref: "", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t2": { + input: "somerepos?ref=v1.0.0", + path: "somerepos", + ref: "v1.0.0", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t3": { + input: "somerepos?version=master", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t4": { + // A ref value takes precedence over a version value. + input: "somerepos?version=master&ref=v1.0.0", + path: "somerepos", + ref: "v1.0.0", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t5": { + // Empty submodules value uses default. + input: "somerepos?version=master&submodules=", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t6": { + // Malformed submodules value uses default. + input: "somerepos?version=master&submodules=maybe", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t7": { + input: "somerepos?version=master&submodules=true", + path: "somerepos", + ref: "master", + submodules: true, + timeout: defaultTimeout, + }, + "t8": { + input: "somerepos?version=master&submodules=false", + path: "somerepos", + ref: "master", + submodules: false, + timeout: defaultTimeout, + }, + "t9": { + // Empty timeout value uses default. + input: "somerepos?version=master&timeout=", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t10": { + // Malformed timeout value uses default. + input: "somerepos?version=master&timeout=jiffy", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t11": { + // Zero timeout value uses default. + input: "somerepos?version=master&timeout=0", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t12": { + input: "somerepos?version=master&timeout=0s", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: defaultTimeout, + }, + "t13": { + input: "somerepos?version=master&timeout=61", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: 61 * time.Second, + }, + "t14": { + input: "somerepos?version=master&timeout=1m1s", + path: "somerepos", + ref: "master", + submodules: defaultSubmodules, + timeout: 61 * time.Second, + }, + "t15": { + input: "somerepos?version=master&submodules=false&timeout=1m1s", + path: "somerepos", + ref: "master", + submodules: false, + timeout: 61 * time.Second, + }, + } + for tn, tc := range testcases { + t.Run(tn, func(t *testing.T) { + path, ref, timeout, submodules := peelQuery(tc.input) + assert.Equal(t, tc.path, path, "path mismatch") + assert.Equal(t, tc.ref, ref, "ref mismatch") + assert.Equal(t, tc.timeout, timeout, "timeout mismatch") + assert.Equal(t, tc.submodules, submodules, "submodules mismatch") + }) + } +} + +func TestIsAWSHost(t *testing.T) { + testcases := []struct { + input string + expect bool + }{ + { + input: "https://git-codecommit.us-east-2.amazonaws.com", + expect: true, + }, + { + input: "ssh://git-codecommit.us-east-2.amazonaws.com", + expect: true, + }, + { + input: "git@github.com:", + expect: false, + }, + { + input: "http://github.com/", + expect: false, + }, + } + for _, testcase := range testcases { + actual := isAWSHost(testcase.input) + if actual != testcase.expect { + t.Errorf("IsAWSHost: expected %v, but got %v on %s", testcase.expect, actual, testcase.input) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/kusterr/yamlformaterror.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/kusterr/yamlformaterror.go new file mode 100644 index 000000000..950ffea8a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/kusterr/yamlformaterror.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package error has contextual error types. +package kusterr + +import ( + "fmt" + "strings" +) + +// YamlFormatError represents error with yaml file name where json/yaml format error happens. +type YamlFormatError struct { + Path string + ErrorMsg string +} + +func (e YamlFormatError) Error() string { + return fmt.Sprintf("YAML file [%s] encounters a format error.\n%s\n", e.Path, e.ErrorMsg) +} + +// Handler handles YamlFormatError +func Handler(e error, path string) error { + if isYAMLSyntaxError(e) { + return YamlFormatError{ + Path: path, + ErrorMsg: e.Error(), + } + } + return e +} + +func isYAMLSyntaxError(e error) bool { + return strings.Contains(e.Error(), "error converting YAML to JSON") || strings.Contains(e.Error(), "error unmarshaling JSON") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/kusterr/yamlformaterror_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/kusterr/yamlformaterror_test.go new file mode 100644 index 000000000..67cd77146 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/kusterr/yamlformaterror_test.go @@ -0,0 +1,41 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kusterr + +import ( + "fmt" + "testing" +) + +const ( + filepath = "/path/to/whatever" + expected = "YAML file [/path/to/whatever] encounters a format error.\n" + + "error converting YAML to JSON: yaml: line 2: found character that cannot start any token\n" +) + +func TestYamlFormatError_Error(t *testing.T) { + testErr := YamlFormatError{ + Path: filepath, + ErrorMsg: "error converting YAML to JSON: yaml: line 2: found character that cannot start any token", + } + if testErr.Error() != expected { + t.Errorf("Expected : %s\n, but found : %s\n", expected, testErr.Error()) + } +} + +func TestErrorHandler(t *testing.T) { + err := fmt.Errorf("error converting YAML to JSON: yaml: line 2: found character that cannot start any token") + testErr := Handler(err, filepath) + expectedErr := fmt.Errorf("format error message") + fmtErr := Handler(expectedErr, filepath) + if fmtErr.Error() != expectedErr.Error() { + t.Errorf("Expected returning fmt.Error, but found %T", fmtErr) + } + if _, ok := testErr.(YamlFormatError); !ok { + t.Errorf("Expected returning YamlFormatError, but found %T", testErr) + } + if testErr == nil || testErr.Error() != expected { + t.Errorf("Expected : %s\n, but found : %s\n", expected, testErr.Error()) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/doc.go new file mode 100644 index 000000000..f41f79b0c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/doc.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package builtinconfig provides legacy methods for +// configuring builtin plugins from a common config file. +// As a user, its best to configure plugins individually +// with plugin config files specified in the `transformers:` +// or `generators:` field, than to use this legacy +// configuration technique. +package builtinconfig diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig.go new file mode 100644 index 000000000..bf5e3f8a3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig.go @@ -0,0 +1,42 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinconfig + +import ( + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/yaml" +) + +// loadDefaultConfig returns a TranformerConfig +// object from a list of files. +func loadDefaultConfig( + ldr ifc.Loader, paths []string) (*TransformerConfig, error) { + result := &TransformerConfig{} + for _, path := range paths { + data, err := ldr.Load(path) + if err != nil { + return nil, err + } + t, err := makeTransformerConfigFromBytes(data) + if err != nil { + return nil, err + } + result, err = result.Merge(t) + if err != nil { + return nil, err + } + } + return result, nil +} + +// makeTransformerConfigFromBytes returns a TransformerConfig object from bytes +func makeTransformerConfigFromBytes(data []byte) (*TransformerConfig, error) { + var t TransformerConfig + err := yaml.Unmarshal(data, &t) + if err != nil { + return nil, err + } + t.sortFields() + return &t, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig_test.go new file mode 100644 index 000000000..7b2172608 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig_test.go @@ -0,0 +1,46 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinconfig + +import ( + "reflect" + "testing" + + "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestLoadDefaultConfigsFromFiles(t *testing.T) { + fSys := filesys.MakeFsInMemory() + err := fSys.WriteFile("config.yaml", []byte(` +namePrefix: +- path: nameprefix/path + kind: SomeKind +`)) + if err != nil { + t.Fatal(err) + } + ldr, err := loader.NewLoader( + loader.RestrictionRootOnly, filesys.Separator, fSys) + if err != nil { + t.Fatal(err) + } + tCfg, err := loadDefaultConfig(ldr, []string{"config.yaml"}) + if err != nil { + t.Fatal(err) + } + expected := &TransformerConfig{ + NamePrefix: []types.FieldSpec{ + { + Gvk: resid.Gvk{Kind: "SomeKind"}, + Path: "nameprefix/path", + }, + }, + } + if !reflect.DeepEqual(tCfg, expected) { + t.Fatalf("expected %v\n but go6t %v\n", expected, tCfg) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/namebackreferences.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/namebackreferences.go new file mode 100644 index 000000000..354f70e51 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/namebackreferences.go @@ -0,0 +1,99 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinconfig + +import ( + "strings" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// NameBackReferences is an association between a gvk.GVK (a ReferralTarget) +// and a list of Referrers that could refer to it. +// +// It is used to handle name changes, and can be thought of as a +// a contact list. If you change your own contact info (name, +// phone number, etc.), you must tell your contacts or they won't +// know about the change. +// +// For example, ConfigMaps can be used by Pods and everything that +// contains a Pod; Deployment, Job, StatefulSet, etc. +// The ConfigMap is the ReferralTarget, the others are Referrers. +// +// If the the name of a ConfigMap instance changed from 'alice' to 'bob', +// one must +// - visit all objects that could refer to the ConfigMap (the Referrers) +// - see if they mention 'alice', +// - if so, change the Referrer's name reference to 'bob'. +// +// The NameBackReferences instance to aid in this could look like +// { +// kind: ConfigMap +// version: v1 +// fieldSpecs: +// - kind: Pod +// version: v1 +// path: spec/volumes/configMap/name +// - kind: Deployment +// path: spec/template/spec/volumes/configMap/name +// - kind: Job +// path: spec/template/spec/volumes/configMap/name +// (etc.) +// } +type NameBackReferences struct { + resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` + // TODO: rename json 'fieldSpecs' to 'referrers' for clarity. + // This will, however, break anyone using a custom config. + Referrers types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func (n NameBackReferences) String() string { + var r []string + for _, f := range n.Referrers { + r = append(r, f.String()) + } + return n.Gvk.String() + ": (\n" + + strings.Join(r, "\n") + "\n)" +} + +type nbrSlice []NameBackReferences + +func (s nbrSlice) Len() int { return len(s) } +func (s nbrSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s nbrSlice) Less(i, j int) bool { + return s[i].Gvk.IsLessThan(s[j].Gvk) +} + +func (s nbrSlice) mergeAll(o nbrSlice) (result nbrSlice, err error) { + result = s + for _, r := range o { + result, err = result.mergeOne(r) + if err != nil { + return nil, err + } + } + return result, nil +} + +func (s nbrSlice) mergeOne(other NameBackReferences) (nbrSlice, error) { + var result nbrSlice + var err error + found := false + for _, c := range s { + if c.Gvk.Equals(other.Gvk) { + c.Referrers, err = c.Referrers.MergeAll(other.Referrers) + if err != nil { + return nil, err + } + found = true + } + result = append(result, c) + } + + if !found { + result = append(result, other) + } + return result, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/namebackreferences_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/namebackreferences_test.go new file mode 100644 index 000000000..b9b60369b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/namebackreferences_test.go @@ -0,0 +1,97 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinconfig + +import ( + "reflect" + "testing" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestMergeAll(t *testing.T) { + fsSlice1 := []types.FieldSpec{ + { + Gvk: resid.Gvk{ + Kind: "Pod", + }, + Path: "path/to/a/name", + CreateIfNotPresent: false, + }, + { + Gvk: resid.Gvk{ + Kind: "Deployment", + }, + Path: "another/path/to/some/name", + CreateIfNotPresent: false, + }, + } + fsSlice2 := []types.FieldSpec{ + { + Gvk: resid.Gvk{ + Kind: "Job", + }, + Path: "morepath/to/name", + CreateIfNotPresent: false, + }, + { + Gvk: resid.Gvk{ + Kind: "StatefulSet", + }, + Path: "yet/another/path/to/a/name", + CreateIfNotPresent: false, + }, + } + + nbrsSlice1 := nbrSlice{ + { + Gvk: resid.Gvk{ + Kind: "ConfigMap", + }, + Referrers: fsSlice1, + }, + { + Gvk: resid.Gvk{ + Kind: "Secret", + }, + Referrers: fsSlice2, + }, + } + nbrsSlice2 := nbrSlice{ + { + Gvk: resid.Gvk{ + Kind: "ConfigMap", + }, + Referrers: fsSlice1, + }, + { + Gvk: resid.Gvk{ + Kind: "Secret", + }, + Referrers: fsSlice2, + }, + } + expected := nbrSlice{ + { + Gvk: resid.Gvk{ + Kind: "ConfigMap", + }, + Referrers: fsSlice1, + }, + { + Gvk: resid.Gvk{ + Kind: "Secret", + }, + Referrers: fsSlice2, + }, + } + actual, err := nbrsSlice1.mergeAll(nbrsSlice2) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected\n %v\n but got\n %v\n", expected, actual) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/transformerconfig.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/transformerconfig.go new file mode 100644 index 000000000..a28627a13 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/transformerconfig.go @@ -0,0 +1,148 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinconfig + +import ( + "log" + "sort" + + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts" + "sigs.k8s.io/kustomize/api/types" +) + +// TransformerConfig holds the data needed to perform transformations. +type TransformerConfig struct { + NamePrefix types.FsSlice `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"` + NameSuffix types.FsSlice `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"` + NameSpace types.FsSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"` + CommonLabels types.FsSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"` + CommonAnnotations types.FsSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"` + NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"` + VarReference types.FsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"` + Images types.FsSlice `json:"images,omitempty" yaml:"images,omitempty"` + Replicas types.FsSlice `json:"replicas,omitempty" yaml:"replicas,omitempty"` +} + +// MakeEmptyConfig returns an empty TransformerConfig object +func MakeEmptyConfig() *TransformerConfig { + return &TransformerConfig{} +} + +// MakeDefaultConfig returns a default TransformerConfig. +func MakeDefaultConfig() *TransformerConfig { + c, err := makeTransformerConfigFromBytes( + builtinpluginconsts.GetDefaultFieldSpecs()) + if err != nil { + log.Fatalf("Unable to make default transformconfig: %v", err) + } + return c +} + +// MakeTransformerConfig returns a merger of custom config, +// if any, with default config. +func MakeTransformerConfig( + ldr ifc.Loader, paths []string) (*TransformerConfig, error) { + t1 := MakeDefaultConfig() + if len(paths) == 0 { + return t1, nil + } + t2, err := loadDefaultConfig(ldr, paths) + if err != nil { + return nil, err + } + return t1.Merge(t2) +} + +// sortFields provides determinism in logging, tests, etc. +func (t *TransformerConfig) sortFields() { + sort.Sort(t.NamePrefix) + sort.Sort(t.NameSpace) + sort.Sort(t.CommonLabels) + sort.Sort(t.CommonAnnotations) + sort.Sort(t.NameReference) + sort.Sort(t.VarReference) + sort.Sort(t.Images) + sort.Sort(t.Replicas) +} + +// AddPrefixFieldSpec adds a FieldSpec to NamePrefix +func (t *TransformerConfig) AddPrefixFieldSpec(fs types.FieldSpec) (err error) { + t.NamePrefix, err = t.NamePrefix.MergeOne(fs) + return err +} + +// AddSuffixFieldSpec adds a FieldSpec to NameSuffix +func (t *TransformerConfig) AddSuffixFieldSpec(fs types.FieldSpec) (err error) { + t.NameSuffix, err = t.NameSuffix.MergeOne(fs) + return err +} + +// AddLabelFieldSpec adds a FieldSpec to CommonLabels +func (t *TransformerConfig) AddLabelFieldSpec(fs types.FieldSpec) (err error) { + t.CommonLabels, err = t.CommonLabels.MergeOne(fs) + return err +} + +// AddAnnotationFieldSpec adds a FieldSpec to CommonAnnotations +func (t *TransformerConfig) AddAnnotationFieldSpec(fs types.FieldSpec) (err error) { + t.CommonAnnotations, err = t.CommonAnnotations.MergeOne(fs) + return err +} + +// AddNamereferenceFieldSpec adds a NameBackReferences to NameReference +func (t *TransformerConfig) AddNamereferenceFieldSpec( + nbrs NameBackReferences) (err error) { + t.NameReference, err = t.NameReference.mergeOne(nbrs) + return err +} + +// Merge merges two TransformerConfigs objects into +// a new TransformerConfig object +func (t *TransformerConfig) Merge(input *TransformerConfig) ( + merged *TransformerConfig, err error) { + if input == nil { + return t, nil + } + merged = &TransformerConfig{} + merged.NamePrefix, err = t.NamePrefix.MergeAll(input.NamePrefix) + if err != nil { + return nil, err + } + merged.NameSuffix, err = t.NameSuffix.MergeAll(input.NameSuffix) + if err != nil { + return nil, err + } + merged.NameSpace, err = t.NameSpace.MergeAll(input.NameSpace) + if err != nil { + return nil, err + } + merged.CommonAnnotations, err = t.CommonAnnotations.MergeAll( + input.CommonAnnotations) + if err != nil { + return nil, err + } + merged.CommonLabels, err = t.CommonLabels.MergeAll(input.CommonLabels) + if err != nil { + return nil, err + } + merged.VarReference, err = t.VarReference.MergeAll(input.VarReference) + if err != nil { + return nil, err + } + merged.NameReference, err = t.NameReference.mergeAll(input.NameReference) + if err != nil { + return nil, err + } + merged.Images, err = t.Images.MergeAll(input.Images) + if err != nil { + return nil, err + } + merged.Replicas, err = t.Replicas.MergeAll(input.Replicas) + if err != nil { + return nil, err + } + merged.sortFields() + return merged, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/transformerconfig_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/transformerconfig_test.go new file mode 100644 index 000000000..112a2d8cd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinconfig/transformerconfig_test.go @@ -0,0 +1,175 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinconfig_test + +import ( + "reflect" + "testing" + + . "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestMakeDefaultConfig(t *testing.T) { + // Confirm default can be made without fatal error inside call. + _ = MakeDefaultConfig() +} + +func TestAddNamereferenceFieldSpec(t *testing.T) { + cfg := &TransformerConfig{} + + nbrs := NameBackReferences{ + Gvk: resid.Gvk{ + Kind: "KindA", + }, + Referrers: []types.FieldSpec{ + { + Gvk: resid.Gvk{ + Kind: "KindB", + }, + Path: "path/to/a/field", + CreateIfNotPresent: false, + }, + }, + } + + err := cfg.AddNamereferenceFieldSpec(nbrs) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if len(cfg.NameReference) != 1 { + t.Fatal("failed to add namereference FieldSpec") + } +} + +func TestAddFieldSpecs(t *testing.T) { + cfg := &TransformerConfig{} + + fieldSpec := types.FieldSpec{ + Gvk: resid.Gvk{Group: "GroupA", Kind: "KindB"}, + Path: "path/to/a/field", + CreateIfNotPresent: true, + } + + err := cfg.AddPrefixFieldSpec(fieldSpec) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if len(cfg.NamePrefix) != 1 { + t.Fatalf("failed to add nameprefix FieldSpec") + } + err = cfg.AddSuffixFieldSpec(fieldSpec) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if len(cfg.NameSuffix) != 1 { + t.Fatalf("failed to add namesuffix FieldSpec") + } + err = cfg.AddLabelFieldSpec(fieldSpec) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if len(cfg.CommonLabels) != 1 { + t.Fatalf("failed to add nameprefix FieldSpec") + } + err = cfg.AddAnnotationFieldSpec(fieldSpec) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if len(cfg.CommonAnnotations) != 1 { + t.Fatalf("failed to add nameprefix FieldSpec") + } +} + +func TestMerge(t *testing.T) { + nameReference := []NameBackReferences{ + { + Gvk: resid.Gvk{ + Kind: "KindA", + }, + Referrers: []types.FieldSpec{ + { + Gvk: resid.Gvk{ + Kind: "KindB", + }, + Path: "path/to/a/field", + CreateIfNotPresent: false, + }, + }, + }, + { + Gvk: resid.Gvk{ + Kind: "KindA", + }, + Referrers: []types.FieldSpec{ + { + Gvk: resid.Gvk{ + Kind: "KindC", + }, + Path: "path/to/a/field", + CreateIfNotPresent: false, + }, + }, + }, + } + fieldSpecs := []types.FieldSpec{ + { + Gvk: resid.Gvk{Group: "GroupA", Kind: "KindB"}, + Path: "path/to/a/field", + CreateIfNotPresent: true, + }, + { + Gvk: resid.Gvk{Group: "GroupA", Kind: "KindC"}, + Path: "path/to/a/field", + CreateIfNotPresent: true, + }, + } + cfga := &TransformerConfig{} + cfga.AddNamereferenceFieldSpec(nameReference[0]) + cfga.AddPrefixFieldSpec(fieldSpecs[0]) + cfga.AddSuffixFieldSpec(fieldSpecs[0]) + + cfgb := &TransformerConfig{} + cfgb.AddNamereferenceFieldSpec(nameReference[1]) + cfgb.AddPrefixFieldSpec(fieldSpecs[1]) + cfga.AddSuffixFieldSpec(fieldSpecs[1]) + + actual, err := cfga.Merge(cfgb) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + + if len(actual.NamePrefix) != 2 { + t.Fatal("merge failed for namePrefix FieldSpec") + } + + if len(actual.NameSuffix) != 2 { + t.Fatal("merge failed for nameSuffix FieldSpec") + } + + if len(actual.NameReference) != 1 { + t.Fatal("merge failed for namereference FieldSpec") + } + + expected := &TransformerConfig{} + expected.AddNamereferenceFieldSpec(nameReference[0]) + expected.AddNamereferenceFieldSpec(nameReference[1]) + expected.AddPrefixFieldSpec(fieldSpecs[0]) + expected.AddPrefixFieldSpec(fieldSpecs[1]) + expected.AddSuffixFieldSpec(fieldSpecs[0]) + expected.AddSuffixFieldSpec(fieldSpecs[1]) + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected: %v\n but got: %v\n", expected, actual) + } + + actual, err = cfga.Merge(nil) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if !reflect.DeepEqual(actual, cfga) { + t.Fatalf("expected: %v\n but got: %v\n", cfga, actual) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinhelpers/builtinplugintype_string.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinhelpers/builtinplugintype_string.go new file mode 100644 index 000000000..1b347cbc8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinhelpers/builtinplugintype_string.go @@ -0,0 +1,42 @@ +// Code generated by "stringer -type=BuiltinPluginType"; DO NOT EDIT. + +package builtinhelpers + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Unknown-0] + _ = x[AnnotationsTransformer-1] + _ = x[ConfigMapGenerator-2] + _ = x[IAMPolicyGenerator-3] + _ = x[HashTransformer-4] + _ = x[ImageTagTransformer-5] + _ = x[LabelTransformer-6] + _ = x[LegacyOrderTransformer-7] + _ = x[NamespaceTransformer-8] + _ = x[PatchJson6902Transformer-9] + _ = x[PatchStrategicMergeTransformer-10] + _ = x[PatchTransformer-11] + _ = x[PrefixSuffixTransformer-12] + _ = x[PrefixTransformer-13] + _ = x[SuffixTransformer-14] + _ = x[ReplicaCountTransformer-15] + _ = x[SecretGenerator-16] + _ = x[ValueAddTransformer-17] + _ = x[HelmChartInflationGenerator-18] + _ = x[ReplacementTransformer-19] +} + +const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorIAMPolicyGeneratorHashTransformerImageTagTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerPrefixTransformerSuffixTransformerReplicaCountTransformerSecretGeneratorValueAddTransformerHelmChartInflationGeneratorReplacementTransformer" + +var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 65, 80, 99, 115, 137, 157, 181, 211, 227, 250, 267, 284, 307, 322, 341, 368, 390} + +func (i BuiltinPluginType) String() string { + if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) { + return "BuiltinPluginType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BuiltinPluginType_name[_BuiltinPluginType_index[i]:_BuiltinPluginType_index[i+1]] +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinhelpers/builtins.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinhelpers/builtins.go new file mode 100644 index 000000000..260ed1bf5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/builtinhelpers/builtins.go @@ -0,0 +1,114 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinhelpers + +import ( + "sigs.k8s.io/kustomize/api/internal/builtins" + "sigs.k8s.io/kustomize/api/resmap" +) + +//go:generate stringer -type=BuiltinPluginType +type BuiltinPluginType int + +const ( + Unknown BuiltinPluginType = iota + AnnotationsTransformer + ConfigMapGenerator + IAMPolicyGenerator + HashTransformer + ImageTagTransformer + LabelTransformer + LegacyOrderTransformer + NamespaceTransformer + PatchJson6902Transformer + PatchStrategicMergeTransformer + PatchTransformer + PrefixSuffixTransformer + PrefixTransformer + SuffixTransformer + ReplicaCountTransformer + SecretGenerator + ValueAddTransformer + HelmChartInflationGenerator + ReplacementTransformer +) + +var stringToBuiltinPluginTypeMap map[string]BuiltinPluginType + +func init() { + stringToBuiltinPluginTypeMap = makeStringToBuiltinPluginTypeMap() +} + +func makeStringToBuiltinPluginTypeMap() (result map[string]BuiltinPluginType) { + result = make(map[string]BuiltinPluginType, 23) + for k := range GeneratorFactories { + result[k.String()] = k + } + for k := range TransformerFactories { + result[k.String()] = k + } + return +} + +func GetBuiltinPluginType(n string) BuiltinPluginType { + result, ok := stringToBuiltinPluginTypeMap[n] + if ok { + return result + } + return Unknown +} + +var GeneratorFactories = map[BuiltinPluginType]func() resmap.GeneratorPlugin{ + ConfigMapGenerator: builtins.NewConfigMapGeneratorPlugin, + IAMPolicyGenerator: builtins.NewIAMPolicyGeneratorPlugin, + SecretGenerator: builtins.NewSecretGeneratorPlugin, + HelmChartInflationGenerator: builtins.NewHelmChartInflationGeneratorPlugin, +} + +type MultiTransformer struct { + transformers []resmap.TransformerPlugin +} + +func (t *MultiTransformer) Transform(m resmap.ResMap) error { + for _, transformer := range t.transformers { + if err := transformer.Transform(m); err != nil { + return err + } + } + return nil +} + +func (t *MultiTransformer) Config(h *resmap.PluginHelpers, b []byte) error { + for _, transformer := range t.transformers { + if err := transformer.Config(h, b); err != nil { + return err + } + } + return nil +} + +func NewMultiTransformer() resmap.TransformerPlugin { + return &MultiTransformer{[]resmap.TransformerPlugin{ + builtins.NewPrefixTransformerPlugin(), + builtins.NewSuffixTransformerPlugin(), + }} +} + +var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin{ + AnnotationsTransformer: builtins.NewAnnotationsTransformerPlugin, + HashTransformer: builtins.NewHashTransformerPlugin, + ImageTagTransformer: builtins.NewImageTagTransformerPlugin, + LabelTransformer: builtins.NewLabelTransformerPlugin, + LegacyOrderTransformer: builtins.NewLegacyOrderTransformerPlugin, + NamespaceTransformer: builtins.NewNamespaceTransformerPlugin, + PatchJson6902Transformer: builtins.NewPatchJson6902TransformerPlugin, + PatchStrategicMergeTransformer: builtins.NewPatchStrategicMergeTransformerPlugin, + PatchTransformer: builtins.NewPatchTransformerPlugin, + PrefixSuffixTransformer: NewMultiTransformer, + PrefixTransformer: builtins.NewPrefixTransformerPlugin, + SuffixTransformer: builtins.NewSuffixTransformerPlugin, + ReplacementTransformer: builtins.NewReplacementTransformerPlugin, + ReplicaCountTransformer: builtins.NewReplicaCountTransformerPlugin, + ValueAddTransformer: builtins.NewValueAddTransformerPlugin, +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/compiler/compiler.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/compiler/compiler.go new file mode 100644 index 000000000..04f827eff --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/compiler/compiler.go @@ -0,0 +1,108 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package compiler + +import ( + "bytes" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" +) + +// Compiler creates Go plugin object files. +type Compiler struct { + // pluginRoot is where the user + // has her ${g}/${v}/$lower(${k})/${k}.go files. + pluginRoot string + // Where compilation happens. + workDir string + // Used as the root file name for src and object. + rawKind string + // Capture compiler output. + stderr bytes.Buffer + // Capture compiler output. + stdout bytes.Buffer +} + +// NewCompiler returns a new compiler instance. +func NewCompiler(root string) *Compiler { + return &Compiler{pluginRoot: root} +} + +// Set GVK converts g,v,k tuples to file path components. +func (b *Compiler) SetGVK(g, v, k string) { + b.rawKind = k + b.workDir = filepath.Join(b.pluginRoot, g, v, strings.ToLower(k)) +} + +func (b *Compiler) srcPath() string { + return filepath.Join(b.workDir, b.rawKind+".go") +} + +func (b *Compiler) objFile() string { + return b.rawKind + ".so" +} + +// Absolute path to the compiler output (the .so file). +func (b *Compiler) ObjPath() string { + return filepath.Join(b.workDir, b.objFile()) +} + +// Compile changes its working directory to +// ${pluginRoot}/${g}/${v}/$lower(${k} and places +// object code next to source code. +func (b *Compiler) Compile() error { + if !utils.FileExists(b.srcPath()) { + return fmt.Errorf("cannot find source at '%s'", b.srcPath()) + } + // If you use an IDE, make sure it's go build and test flags + // match those used below. Same goes for Makefile targets. + commands := []string{ + "build", + // "-trimpath", This flag used to make it better, now it makes it worse, + // see https://github.com/golang/go/issues/31354 + "-buildmode", + "plugin", + "-o", b.objFile(), + } + goBin := utils.GoBin() + if !utils.FileExists(goBin) { + return fmt.Errorf( + "cannot find go compiler %s", goBin) + } + cmd := exec.Command(goBin, commands...) + b.stderr.Reset() + cmd.Stderr = &b.stderr + b.stdout.Reset() + cmd.Stdout = &b.stdout + cmd.Env = os.Environ() + cmd.Dir = b.workDir + if err := cmd.Run(); err != nil { + b.report() + return errors.Wrapf( + err, "cannot compile %s:\nSTDERR\n%s\n", + b.srcPath(), b.stderr.String()) + } + result := filepath.Join(b.workDir, b.objFile()) + if utils.FileExists(result) { + log.Printf("compiler created: %s", result) + return nil + } + return fmt.Errorf("post compile, cannot find '%s'", result) +} + +func (b *Compiler) report() { + log.Println("stdout: -------") + log.Println(b.stdout.String()) + log.Println("----------------") + log.Println("stderr: -------") + log.Println(b.stderr.String()) + log.Println("----------------") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/compiler/compiler_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/compiler/compiler_test.go new file mode 100644 index 000000000..cc3232333 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/compiler/compiler_test.go @@ -0,0 +1,51 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package compiler_test + +import ( + "path/filepath" + "testing" + + . "sigs.k8s.io/kustomize/api/internal/plugins/compiler" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// Regression coverage over compiler behavior. +func TestCompiler(t *testing.T) { + srcRoot, err := utils.DeterminePluginSrcRoot(filesys.MakeFsOnDisk()) + if err != nil { + t.Error(err) + } + c := NewCompiler(srcRoot) + + c.SetGVK("someteam.example.com", "v1", "DatePrefixer") + expectObj := filepath.Join( + srcRoot, "someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so") + if expectObj != c.ObjPath() { + t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath()) + } + err = c.Compile() + if err != nil { + t.Error(err) + } + if !utils.FileExists(expectObj) { + t.Errorf("didn't find expected obj file %s", expectObj) + } + + c.SetGVK("builtin", "", "SecretGenerator") + expectObj = filepath.Join( + srcRoot, + "builtin", "", "secretgenerator", "SecretGenerator.so") + if expectObj != c.ObjPath() { + t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath()) + } + err = c.Compile() + if err != nil { + t.Error(err) + } + if !utils.FileExists(expectObj) { + t.Errorf("didn't find expected obj file %s", expectObj) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/doc.go new file mode 100644 index 000000000..5755f1750 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/doc.go @@ -0,0 +1,115 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +/* + +Read docs/plugins.md first for an overview of kustomize plugins. + + +BUILTIN PLUGIN CONFIGURATION + +There are two kinds of plugins, Go plugins (shared +object library) and exec plugins (independent binary). +For performance and standardized testing reasons, all +builtin plugins are Go plugins (not exec plugins). + +Using "SecretGenerator" as an example in what +follows. + +The plugin config file looks like + + apiVersion: builtin + kind: SecretGenerator + metadata: + name: whatever + otherField1: whatever + otherField2: whatever + ... + +The apiVersion must be 'builtin'. + +The kind is the CamelCase name of the plugin. + +The source for a builtin plugin must be at: + + repo=$GOPATH/src/sigs.k8s.io/kustomize + ${repo}/plugin/builtin/LOWERCASE(${kind})/${kind} + +k8s wants 'kind' values to follow CamelCase, while +Go style doesn't like but does allow such names. + +The lowercased value of kind is used as the name of the +directory holding the plugin, its test, and any +optional associated files (possibly a go.mod file). + + +BUILTIN PLUGIN GENERATION + +The `pluginator` program is a code generator that +converts kustomize generator (G) and/or +transformer (T) Go plugins to statically linkable +code. + +It arises from following requirements: + +* extension + kustomize does two things - generate or + transform k8s resources. Plugins let + users write their own G&T's without + having to fork kustomize and learn its + internals. + +* dogfooding + A G&T extension framework one can trust + should be used by its authors to deliver + builtin G&T's. + +* distribution + kustomize should be distributable via + `go get` and should run where Go + programs are expected to run. + +The extension requirement led to building +a framework that accommodates writing a +G or T as either + +* an 'exec' plugin (any executable file + runnable as a kustomize subprocess), or + +* as a Go plugin - see + https://golang.org/pkg/plugin. + +The dogfooding (and an implicit performance +requirement) requires a 'builtin' G or T to +be written as a Go plugin. + +The distribution ('go get') requirement demands +conversion of Go plugins to statically linked +code, hence this program. + + +TO GENERATE CODE + + repo=$GOPATH/src/sigs.k8s.io/kustomize + cd $repo/plugin/builtin + go generate ./... + +This creates + + $repo/api/plugins/builtins/SecretGenerator.go + +etc. + +Generated plugins are used in kustomize via + + package whatever + import sigs.k8s.io/kustomize/api/plugins/builtins + ... + g := builtin.NewSecretGenerator() + g.Config(h, k) + resources, err := g.Generate() + err = g.Transform(resources) + // Eventually emit resources. + +*/ +package plugins diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/execplugin/execplugin.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/execplugin/execplugin.go new file mode 100644 index 000000000..9380558cb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/execplugin/execplugin.go @@ -0,0 +1,192 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package execplugin + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strings" + + "github.com/google/shlex" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/yaml" +) + +const ( + tmpConfigFilePrefix = "kust-plugin-config-" +) + +// ExecPlugin record the name and args of an executable +// It triggers the executable generator and transformer +type ExecPlugin struct { + // absolute path of the executable + path string + + // Optional command line arguments to the executable + // pulled from specially named fields in cfg. + // This is for executables that don't want to parse YAML. + args []string + + // Plugin configuration data. + cfg []byte + + // PluginHelpers + h *resmap.PluginHelpers +} + +func NewExecPlugin(p string) *ExecPlugin { + return &ExecPlugin{path: p} +} + +func (p *ExecPlugin) ErrIfNotExecutable() error { + f, err := os.Stat(p.path) + if err != nil { + return err + } + // In Windows, it is not possible to determine whether a + // file is executable through file mode. + // TODO: provide for setting the executable FileMode bit on Windows + // The (fs *fileStat) Mode() (m FileMode) {} function in + // https://golang.org/src/os/types_windows.go + // lacks the ability to set the FileMode executable bit in response + // to file data on Windows. + if f.Mode()&0111 == 0000 && runtime.GOOS != "windows" { + return fmt.Errorf("unexecutable plugin at: %s", p.path) + } + return nil +} + +func (p *ExecPlugin) Path() string { + return p.path +} + +func (p *ExecPlugin) Args() []string { + return p.args +} + +func (p *ExecPlugin) Cfg() []byte { + return p.cfg +} + +func (p *ExecPlugin) Config(h *resmap.PluginHelpers, config []byte) error { + p.h = h + p.cfg = config + return p.processOptionalArgsFields() +} + +type argsConfig struct { + ArgsOneLiner string `json:"argsOneLiner,omitempty" yaml:"argsOneLiner,omitempty"` + ArgsFromFile string `json:"argsFromFile,omitempty" yaml:"argsFromFile,omitempty"` +} + +func (p *ExecPlugin) processOptionalArgsFields() error { + var c argsConfig + err := yaml.Unmarshal(p.cfg, &c) + if err != nil { + return err + } + if c.ArgsOneLiner != "" { + p.args, _ = shlex.Split(c.ArgsOneLiner) + } + if c.ArgsFromFile != "" { + content, err := p.h.Loader().Load(c.ArgsFromFile) + if err != nil { + return err + } + for _, x := range strings.Split(string(content), "\n") { + x := strings.TrimLeft(x, " ") + if x != "" { + p.args = append(p.args, x) + } + } + } + return nil +} + +func (p *ExecPlugin) Generate() (resmap.ResMap, error) { + output, err := p.invokePlugin(nil) + if err != nil { + return nil, err + } + rm, err := p.h.ResmapFactory().NewResMapFromBytes(output) + if err != nil { + return nil, err + } + return utils.UpdateResourceOptions(rm) +} + +func (p *ExecPlugin) Transform(rm resmap.ResMap) error { + // add ResIds as annotations to all objects so that we can add them back + inputRM, err := utils.GetResMapWithIDAnnotation(rm) + if err != nil { + return err + } + + // encode the ResMap so it can be fed to the plugin + resources, err := inputRM.AsYaml() + if err != nil { + return err + } + + // invoke the plugin with resources as the input + output, err := p.invokePlugin(resources) + if err != nil { + return fmt.Errorf("%v %s", err, string(output)) + } + + // update the original ResMap based on the output + return utils.UpdateResMapValues(p.path, p.h, output, rm) +} + +// invokePlugin writes plugin config to a temp file, then +// passes the full temp file path as the first arg to a process +// running the plugin binary. Process output is returned. +func (p *ExecPlugin) invokePlugin(input []byte) ([]byte, error) { + f, err := ioutil.TempFile("", tmpConfigFilePrefix) + if err != nil { + return nil, errors.Wrap( + err, "creating tmp plugin config file") + } + _, err = f.Write(p.cfg) + if err != nil { + return nil, errors.Wrap( + err, "writing plugin config to "+f.Name()) + } + err = f.Close() + if err != nil { + return nil, errors.Wrap( + err, "closing plugin config file "+f.Name()) + } + //nolint:gosec + cmd := exec.Command( + p.path, append([]string{f.Name()}, p.args...)...) + cmd.Env = p.getEnv() + cmd.Stdin = bytes.NewReader(input) + cmd.Stderr = os.Stderr + if _, err := os.Stat(p.h.Loader().Root()); err == nil { + cmd.Dir = p.h.Loader().Root() + } + result, err := cmd.Output() + if err != nil { + return nil, errors.Wrapf( + err, "failure in plugin configured via %s; %v", + f.Name(), err.Error()) + } + return result, os.Remove(f.Name()) +} + +func (p *ExecPlugin) getEnv() []string { + env := os.Environ() + env = append(env, + "KUSTOMIZE_PLUGIN_CONFIG_STRING="+string(p.cfg), + "KUSTOMIZE_PLUGIN_CONFIG_ROOT="+p.h.Loader().Root()) + return env +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/execplugin/execplugin_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/execplugin/execplugin_test.go new file mode 100644 index 000000000..5cc637f3b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/execplugin/execplugin_test.go @@ -0,0 +1,128 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package execplugin_test + +import ( + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/require" + . "sigs.k8s.io/kustomize/api/internal/plugins/execplugin" + pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" + fLdr "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestExecPluginConfig(t *testing.T) { + fSys := filesys.MakeFsInMemory() + err := fSys.WriteFile("sed-input.txt", []byte(` +s/$FOO/foo/g +s/$BAR/bar baz/g + \ \ \ +`)) + require.NoError(t, err) + ldr, err := fLdr.NewLoader( + fLdr.RestrictionRootOnly, filesys.Separator, fSys) + if err != nil { + t.Fatal(err) + } + pvd := provider.NewDefaultDepProvider() + rf := resmap.NewFactory(pvd.GetResourceFactory()) + pluginConfig := rf.RF().FromMap( + map[string]interface{}{ + "apiVersion": "someteam.example.com/v1", + "kind": "SedTransformer", + "metadata": map[string]interface{}{ + "name": "some-random-name", + }, + "argsOneLiner": "one two 'foo bar'", + "argsFromFile": "sed-input.txt", + }) + + pluginConfig.RemoveBuildAnnotations() + pc := types.DisabledPluginConfig() + loader := pLdr.NewLoader(pc, rf, fSys) + pluginPath, err := loader.AbsolutePluginPath(pluginConfig.OrgId()) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + p := NewExecPlugin(pluginPath) + // Not checking to see if the plugin is executable, + // because this test does not run it. + // This tests only covers sending configuration + // to the plugin wrapper object and confirming + // that it's properly prepared for execution. + + yaml, err := pluginConfig.AsYAML() + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = p.Config( + resmap.NewPluginHelpers(ldr, pvd.GetFieldValidator(), rf, pc), + yaml) + require.NoError(t, err) + + expected := "someteam.example.com/v1/sedtransformer/SedTransformer" + if !strings.HasSuffix(p.Path(), expected) { + t.Fatalf("expected suffix '%s', got '%s'", expected, p.Path()) + } + + expected = `apiVersion: someteam.example.com/v1 +argsFromFile: sed-input.txt +argsOneLiner: one two 'foo bar' +kind: SedTransformer +metadata: + name: some-random-name +` + if expected != string(p.Cfg()) { + t.Fatalf("expected cfg '%s', got '%s'", expected, string(p.Cfg())) + + } + if len(p.Args()) != 6 { + t.Fatalf("unexpected arg len %d, %#v", len(p.Args()), p.Args()) + } + if p.Args()[0] != "one" || + p.Args()[1] != "two" || + p.Args()[2] != "foo bar" || + p.Args()[3] != "s/$FOO/foo/g" || + p.Args()[4] != "s/$BAR/bar baz/g" || + p.Args()[5] != "\\ \\ \\ " { + t.Fatalf("unexpected arg array: %#v", p.Args()) + } +} + +func TestExecPlugin_ErrIfNotExecutable(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skipf("always returns nil on Windows") + } + + srcRoot, err := utils.DeterminePluginSrcRoot(filesys.MakeFsOnDisk()) + if err != nil { + t.Error(err) + } + + // Test unexecutable plugin + unexecutablePlugin := filepath.Join( + srcRoot, "builtin", "", "secretgenerator", "SecretGenerator.so") + p := NewExecPlugin(unexecutablePlugin) + err = p.ErrIfNotExecutable() + if err == nil { + t.Fatalf("unexpected err: %v", err) + } + + // Test executable plugin + executablePlugin := filepath.Join( + srcRoot, "someteam.example.com", "v1", "bashedconfigmap", "BashedConfigMap") + p = NewExecPlugin(executablePlugin) + err = p.ErrIfNotExecutable() + if err != nil { + t.Fatalf("unexpected err: %v", err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/fnplugin/fnplugin.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/fnplugin/fnplugin.go new file mode 100644 index 000000000..84bc0ac05 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/fnplugin/fnplugin.go @@ -0,0 +1,199 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fnplugin + +import ( + "bytes" + "fmt" + + "github.com/pkg/errors" + + "sigs.k8s.io/kustomize/api/internal/plugins/utils" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" + "sigs.k8s.io/kustomize/kyaml/runfn" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// FnPlugin is the struct to hold function information +type FnPlugin struct { + // Function runner + runFns runfn.RunFns + + // Plugin configuration data. + cfg []byte + + // Plugin name cache for error output + pluginName string + + // PluginHelpers + h *resmap.PluginHelpers +} + +func bytesToRNode(yml []byte) (*yaml.RNode, error) { + rnode, err := yaml.Parse(string(yml)) + if err != nil { + return nil, err + } + return rnode, nil +} + +func resourceToRNode(res *resource.Resource) (*yaml.RNode, error) { + yml, err := res.AsYAML() + if err != nil { + return nil, err + } + + return bytesToRNode(yml) +} + +// GetFunctionSpec return function spec is there is. Otherwise return nil +func GetFunctionSpec(res *resource.Resource) *runtimeutil.FunctionSpec { + rnode, err := resourceToRNode(res) + if err != nil { + return nil + } + + return runtimeutil.GetFunctionSpec(rnode) +} + +func toStorageMounts(mounts []string) []runtimeutil.StorageMount { + var sms []runtimeutil.StorageMount + for _, mount := range mounts { + sms = append(sms, runtimeutil.StringToStorageMount(mount)) + } + return sms +} + +// NewFnPlugin creates a FnPlugin struct +func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin { + return &FnPlugin{ + runFns: runfn.RunFns{ + Functions: []*yaml.RNode{}, + Network: o.Network, + EnableStarlark: o.EnableStar, + EnableExec: o.EnableExec, + StorageMounts: toStorageMounts(o.Mounts), + Env: o.Env, + AsCurrentUser: o.AsCurrentUser, + WorkingDir: o.WorkingDir, + }, + } +} + +// Cfg returns function config +func (p *FnPlugin) Cfg() []byte { + return p.cfg +} + +// Config is called by kustomize to pass-in config information +func (p *FnPlugin) Config(h *resmap.PluginHelpers, config []byte) error { + p.h = h + p.cfg = config + + fn, err := bytesToRNode(p.cfg) + if err != nil { + return err + } + + meta, err := fn.GetMeta() + if err != nil { + return err + } + + p.pluginName = fmt.Sprintf("api: %s, kind: %s, name: %s", + meta.APIVersion, meta.Kind, meta.Name) + + return nil +} + +// Generate is called when run as generator +func (p *FnPlugin) Generate() (resmap.ResMap, error) { + output, err := p.invokePlugin(nil) + if err != nil { + return nil, err + } + rm, err := p.h.ResmapFactory().NewResMapFromBytes(output) + if err != nil { + return nil, err + } + return utils.UpdateResourceOptions(rm) +} + +// Transform is called when run as transformer +func (p *FnPlugin) Transform(rm resmap.ResMap) error { + // add ResIds as annotations to all objects so that we can add them back + inputRM, err := utils.GetResMapWithIDAnnotation(rm) + if err != nil { + return err + } + + // encode the ResMap so it can be fed to the plugin + resources, err := inputRM.AsYaml() + if err != nil { + return err + } + + // invoke the plugin with resources as the input + output, err := p.invokePlugin(resources) + if err != nil { + return fmt.Errorf("%v %s", err, string(output)) + } + + // update the original ResMap based on the output + return utils.UpdateResMapValues(p.pluginName, p.h, output, rm) +} + +func injectAnnotation(input *yaml.RNode, k, v string) error { + err := input.PipeE(yaml.SetAnnotation(k, v)) + if err != nil { + return err + } + return nil +} + +// invokePlugin uses Function runner to run function as plugin +func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) { + // get function config rnode + functionConfig, err := bytesToRNode(p.cfg) + if err != nil { + return nil, err + } + + // This annotation will let kustomize ingnore this item in output + err = injectAnnotation(functionConfig, "config.kubernetes.io/local-config", "true") + if err != nil { + return nil, err + } + // we need to add config as input for generators. Some of them don't work with FunctionConfig + // and in addition kio.Pipeline won't create anything if there are no objects + // see https://github.com/kubernetes-sigs/kustomize/blob/master/kyaml/kio/kio.go#L93 + // Since we added `local-config` annotation so it will be ignored in generator output + // TODO(donnyxia): This is actually not used by generator and only used to bypass a kio limitation. + // Need better solution. + if input == nil { + yml, err := functionConfig.String() + if err != nil { + return nil, err + } + input = []byte(yml) + } + + // Configure and Execute Fn. We don't need to convert resources to ResourceList here + // because function runtime will do that. See kyaml/fn/runtime/runtimeutil/runtimeutil.go + var ouputBuffer bytes.Buffer + p.runFns.Input = bytes.NewReader(input) + p.runFns.Functions = append(p.runFns.Functions, functionConfig) + p.runFns.Output = &ouputBuffer + + err = p.runFns.Execute() + if err != nil { + return nil, errors.Wrap( + err, "couldn't execute function") + } + + return ouputBuffer.Bytes(), nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/loader/loader.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/loader/loader.go new file mode 100644 index 000000000..93c8559d2 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/loader/loader.go @@ -0,0 +1,306 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package loader + +import ( + "fmt" + "log" + "os" + "path/filepath" + "plugin" + "reflect" + "strings" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" + "sigs.k8s.io/kustomize/api/internal/plugins/execplugin" + "sigs.k8s.io/kustomize/api/internal/plugins/fnplugin" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// Loader loads plugins using a file loader (a different loader). +type Loader struct { + pc *types.PluginConfig + rf *resmap.Factory + fs filesys.FileSystem + + // absolutePluginHome caches the location of a valid plugin root directory. + // It should only be set once the directory's existence has been confirmed. + absolutePluginHome string +} + +func NewLoader( + pc *types.PluginConfig, rf *resmap.Factory, fs filesys.FileSystem) *Loader { + return &Loader{pc: pc, rf: rf, fs: fs} +} + +// Config provides the global (not plugin specific) PluginConfig data. +func (l *Loader) Config() *types.PluginConfig { + return l.pc +} + +// SetWorkDir sets the working directory for this loader's plugins +func (l *Loader) SetWorkDir(wd string) { + l.pc.FnpLoadingOptions.WorkingDir = wd +} + +func (l *Loader) LoadGenerators( + ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ( + result []*resmap.GeneratorWithProperties, err error) { + for _, res := range rm.Resources() { + g, err := l.LoadGenerator(ldr, v, res) + if err != nil { + return nil, err + } + generatorOrigin, err := resource.OriginFromCustomPlugin(res) + if err != nil { + return nil, err + } + result = append(result, &resmap.GeneratorWithProperties{Generator: g, Origin: generatorOrigin}) + } + return result, nil +} + +func (l *Loader) LoadGenerator( + ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (resmap.Generator, error) { + c, err := l.loadAndConfigurePlugin(ldr, v, res) + if err != nil { + return nil, err + } + g, ok := c.(resmap.Generator) + if !ok { + return nil, fmt.Errorf("plugin %s not a generator", res.OrgId()) + } + return g, nil +} + +func (l *Loader) LoadTransformers( + ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]*resmap.TransformerWithProperties, error) { + var result []*resmap.TransformerWithProperties + for _, res := range rm.Resources() { + t, err := l.LoadTransformer(ldr, v, res) + if err != nil { + return nil, err + } + transformerOrigin, err := resource.OriginFromCustomPlugin(res) + if err != nil { + return nil, err + } + result = append(result, &resmap.TransformerWithProperties{Transformer: t, Origin: transformerOrigin}) + } + return result, nil +} + +func (l *Loader) LoadTransformer( + ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (*resmap.TransformerWithProperties, error) { + c, err := l.loadAndConfigurePlugin(ldr, v, res) + if err != nil { + return nil, err + } + t, ok := c.(resmap.Transformer) + if !ok { + return nil, fmt.Errorf("plugin %s not a transformer", res.OrgId()) + } + return &resmap.TransformerWithProperties{Transformer: t}, nil +} + +func relativePluginPath(id resid.ResId) string { + return filepath.Join( + id.Group, + id.Version, + strings.ToLower(id.Kind)) +} + +func (l *Loader) AbsolutePluginPath(id resid.ResId) (string, error) { + pluginHome, err := l.absPluginHome() + if err != nil { + return "", err + } + return filepath.Join(pluginHome, relativePluginPath(id), id.Kind), nil +} + +// absPluginHome is the home of kustomize Exec and Go plugins. +// Kustomize plugin configuration files are k8s-style objects +// containing the fields 'apiVersion' and 'kind', e.g. +// apiVersion: apps/v1 +// kind: Deployment +// kustomize reads plugin configuration data from a file path +// specified in the 'generators:' or 'transformers:' field of a +// kustomization file. For Exec and Go plugins, kustomize +// uses this data to both locate the plugin and configure it. +// Each Exec or Go plugin (its code, its tests, its supporting data +// files, etc.) must be housed in its own directory at +// ${absPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind}) +// where +// - ${absPluginHome} is an absolute path, defined below. +// - ${pluginApiVersion} is taken from the plugin config file. +// - ${pluginKind} is taken from the plugin config file. +func (l *Loader) absPluginHome() (string, error) { + // External plugins are disabled--return the dummy plugin root. + if l.pc.PluginRestrictions != types.PluginRestrictionsNone { + return konfig.NoPluginHomeSentinal, nil + } + // We've already determined plugin home--use the cached value. + if l.absolutePluginHome != "" { + return l.absolutePluginHome, nil + } + + // Check default locations for a valid plugin root, and cache it if found. + dir, err := konfig.DefaultAbsPluginHome(l.fs) + if err != nil { + return "", err + } + l.absolutePluginHome = dir + return l.absolutePluginHome, nil +} + +func isBuiltinPlugin(res *resource.Resource) bool { + // TODO: the special string should appear in Group, not Version. + return res.GetGvk().Group == "" && + res.GetGvk().Version == konfig.BuiltinPluginApiVersion +} + +func (l *Loader) loadAndConfigurePlugin( + ldr ifc.Loader, + v ifc.Validator, + res *resource.Resource) (c resmap.Configurable, err error) { + if isBuiltinPlugin(res) { + switch l.pc.BpLoadingOptions { + case types.BploLoadFromFileSys: + c, err = l.loadPlugin(res) + case types.BploUseStaticallyLinked: + // Instead of looking for and loading a .so file, + // instantiate the plugin from a generated factory + // function (see "pluginator"). Being able to do this + // is what makes a plugin "builtin". + c, err = l.makeBuiltinPlugin(res.GetGvk()) + default: + err = fmt.Errorf( + "unknown plugin loader behavior specified: %v", + l.pc.BpLoadingOptions) + } + } else { + switch l.pc.PluginRestrictions { + case types.PluginRestrictionsNone: + c, err = l.loadPlugin(res) + case types.PluginRestrictionsBuiltinsOnly: + err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind) + default: + err = fmt.Errorf( + "unknown plugin restriction specified: %v", + l.pc.PluginRestrictions) + } + } + if err != nil { + return nil, err + } + yaml, err := res.AsYAML() + if err != nil { + return nil, errors.Wrapf(err, "marshalling yaml from res %s", res.OrgId()) + } + err = c.Config(resmap.NewPluginHelpers(ldr, v, l.rf, l.pc), yaml) + if err != nil { + return nil, errors.Wrapf( + err, "plugin %s fails configuration", res.OrgId()) + } + return c, nil +} + +func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) { + bpt := builtinhelpers.GetBuiltinPluginType(r.Kind) + if f, ok := builtinhelpers.GeneratorFactories[bpt]; ok { + return f(), nil + } + if f, ok := builtinhelpers.TransformerFactories[bpt]; ok { + return f(), nil + } + return nil, errors.Errorf("unable to load builtin %s", r) +} + +func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error) { + spec := fnplugin.GetFunctionSpec(res) + if spec != nil { + return fnplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil + } + return l.loadExecOrGoPlugin(res.OrgId()) +} + +func (l *Loader) loadExecOrGoPlugin(resId resid.ResId) (resmap.Configurable, error) { + absPluginPath, err := l.AbsolutePluginPath(resId) + if err != nil { + return nil, err + } + // First try to load the plugin as an executable. + p := execplugin.NewExecPlugin(absPluginPath) + if err = p.ErrIfNotExecutable(); err == nil { + return p, nil + } + if !os.IsNotExist(err) { + // The file exists, but something else is wrong, + // likely it's not executable. + // Assume the user forgot to set the exec bit, + // and return an error, rather than adding ".so" + // to the name and attempting to load it as a Go + // plugin, which will likely fail and result + // in an obscure message. + return nil, err + } + // Failing the above, try loading it as a Go plugin. + c, err := l.loadGoPlugin(resId, absPluginPath+".so") + if err != nil { + return nil, err + } + return c, nil +} + +// registry is a means to avoid trying to load the same .so file +// into memory more than once, which results in an error. +// Each test makes its own loader, and tries to load its own plugins, +// but the loaded .so files are in shared memory, so one will get +// "this plugin already loaded" errors if the registry is maintained +// as a Loader instance variable. So make it a package variable. +var registry = make(map[string]resmap.Configurable) + +func (l *Loader) loadGoPlugin(id resid.ResId, absPath string) (resmap.Configurable, error) { + regId := relativePluginPath(id) + if c, ok := registry[regId]; ok { + return copyPlugin(c), nil + } + if !utils.FileExists(absPath) { + return nil, fmt.Errorf( + "expected file with Go object code at: %s", absPath) + } + log.Printf("Attempting plugin load from '%s'", absPath) + p, err := plugin.Open(absPath) + if err != nil { + return nil, errors.Wrapf(err, "plugin %s fails to load", absPath) + } + symbol, err := p.Lookup(konfig.PluginSymbol) + if err != nil { + return nil, errors.Wrapf( + err, "plugin %s doesn't have symbol %s", + regId, konfig.PluginSymbol) + } + c, ok := symbol.(resmap.Configurable) + if !ok { + return nil, fmt.Errorf("plugin '%s' not configurable", regId) + } + registry[regId] = c + return copyPlugin(c), nil +} + +func copyPlugin(c resmap.Configurable) resmap.Configurable { + indirect := reflect.Indirect(reflect.ValueOf(c)) + newIndirect := reflect.New(indirect.Type()) + newIndirect.Elem().Set(reflect.ValueOf(indirect.Interface())) + newNamed := newIndirect.Interface() + return newNamed.(resmap.Configurable) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/loader/loader_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/loader/loader_test.go new file mode 100644 index 000000000..c5e30b808 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/loader/loader_test.go @@ -0,0 +1,80 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package loader_test + +import ( + "testing" + + . "sigs.k8s.io/kustomize/api/internal/plugins/loader" + "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +const ( + //nolint:gosec + secretGenerator = ` +apiVersion: builtin +kind: SecretGenerator +metadata: + name: secretGenerator +name: mySecret +behavior: merge +envFiles: +- a.env +- b.env +valueFiles: +- longsecret.txt +literals: +- FRUIT=apple +- VEGETABLE=carrot +` + someServiceGenerator = ` +apiVersion: someteam.example.com/v1 +kind: SomeServiceGenerator +metadata: + name: myServiceGenerator +service: my-service +port: "12345" +` +) + +func TestLoader(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + BuildGoPlugin("builtin", "", "SecretGenerator"). + BuildGoPlugin("someteam.example.com", "v1", "SomeServiceGenerator") + defer th.Reset() + p := provider.NewDefaultDepProvider() + rmF := resmap.NewFactory(p.GetResourceFactory()) + fsys := filesys.MakeFsInMemory() + fLdr, err := loader.NewLoader( + loader.RestrictionRootOnly, + filesys.Separator, fsys) + if err != nil { + t.Fatal(err) + } + generatorConfigs, err := rmF.NewResMapFromBytes([]byte( + someServiceGenerator + "---\n" + secretGenerator)) + if err != nil { + t.Fatal(err) + } + for _, behavior := range []types.BuiltinPluginLoadingOptions{ + /* types.BploUseStaticallyLinked, + types.BploLoadFromFileSys */} { + c := types.EnabledPluginConfig(behavior) + pLdr := NewLoader(c, rmF, fsys) + if pLdr == nil { + t.Fatal("expect non-nil loader") + } + _, err = pLdr.LoadGenerators( + fLdr, valtest_test.MakeFakeValidator(), generatorConfigs) + if err != nil { + t.Fatal(err) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/utils/utils.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/utils/utils.go new file mode 100644 index 000000000..8182f203e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/utils/utils.go @@ -0,0 +1,240 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "time" + + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/yaml" +) + +const ( + idAnnotation = "kustomize.config.k8s.io/id" + HashAnnotation = "kustomize.config.k8s.io/needs-hash" + BehaviorAnnotation = "kustomize.config.k8s.io/behavior" +) + +func GoBin() string { + return filepath.Join(runtime.GOROOT(), "bin", "go") +} + +// DeterminePluginSrcRoot guesses where the user +// has her ${g}/${v}/$lower(${k})/${k}.go files. +func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) { + return konfig.FirstDirThatExistsElseError( + "plugin src root", fSys, []konfig.NotedFunc{ + { + Note: "relative to unit test", + F: func() string { + return filepath.Clean( + filepath.Join( + os.Getenv("PWD"), + "..", "..", + konfig.RelPluginHome)) + }, + }, + { + Note: "relative to unit test (internal pkg)", + F: func() string { + return filepath.Clean( + filepath.Join( + os.Getenv("PWD"), + "..", "..", "..", "..", + konfig.RelPluginHome)) + }, + }, + { + Note: "relative to api package", + F: func() string { + return filepath.Clean( + filepath.Join( + os.Getenv("PWD"), + "..", "..", "..", + konfig.RelPluginHome)) + }, + }, + { + Note: "old style $GOPATH", + F: func() string { + return filepath.Join( + os.Getenv("GOPATH"), + "src", konfig.DomainName, + konfig.ProgramName, konfig.RelPluginHome) + }, + }, + { + Note: "HOME with literal 'gopath'", + F: func() string { + return filepath.Join( + konfig.HomeDir(), "gopath", + "src", konfig.DomainName, + konfig.ProgramName, konfig.RelPluginHome) + }, + }, + { + Note: "home directory", + F: func() string { + return filepath.Join( + konfig.HomeDir(), konfig.DomainName, + konfig.ProgramName, konfig.RelPluginHome) + }, + }, + }) +} + +// FileYoungerThan returns true if the file both exists and has an +// age is <= the Duration argument. +func FileYoungerThan(path string, d time.Duration) bool { + fi, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false + } + } + return time.Since(fi.ModTime()) <= d +} + +// FileModifiedAfter returns true if the file both exists and was +// modified after the given time.. +func FileModifiedAfter(path string, t time.Time) bool { + fi, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false + } + } + return fi.ModTime().After(t) +} + +func FileExists(path string) bool { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// GetResMapWithIDAnnotation returns a new copy of the given ResMap with the ResIds annotated in each Resource +func GetResMapWithIDAnnotation(rm resmap.ResMap) (resmap.ResMap, error) { + inputRM := rm.DeepCopy() + for _, r := range inputRM.Resources() { + idString, err := yaml.Marshal(r.CurId()) + if err != nil { + return nil, err + } + annotations := r.GetAnnotations() + annotations[idAnnotation] = string(idString) + if err = r.SetAnnotations(annotations); err != nil { + return nil, err + } + } + return inputRM, nil +} + +// UpdateResMapValues updates the Resource value in the given ResMap +// with the emitted Resource values in output. +func UpdateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byte, rm resmap.ResMap) error { + mapFactory := h.ResmapFactory() + resFactory := mapFactory.RF() + resources, err := resFactory.SliceFromBytes(output) + if err != nil { + return err + } + // Don't use resources here, or error message will be unfriendly to plugin builders + newMap, err := mapFactory.NewResMapFromBytes([]byte{}) + if err != nil { + return err + } + + for _, r := range resources { + // stale--not manipulated by plugin transformers + if err = removeIDAnnotation(r); err != nil { + return err + } + + // Add to the new map, checking for duplicates + if err := newMap.Append(r); err != nil { + prettyID, err := json.Marshal(r.CurId()) + if err != nil { + prettyID = []byte(r.CurId().String()) + } + return fmt.Errorf("plugin %s generated duplicate resource: %s", pluginName, prettyID) + } + + // Add to or update the old map + oldIdx, err := rm.GetIndexOfCurrentId(r.CurId()) + if err != nil { + return err + } + if oldIdx != -1 { + rm.GetByIndex(oldIdx).ResetRNode(r) + } else { + if err := rm.Append(r); err != nil { + return err + } + } + } + + // Remove items the transformer deleted from the old map + for _, id := range rm.AllIds() { + newIdx, _ := newMap.GetIndexOfCurrentId(id) + if newIdx == -1 { + if err = rm.Remove(id); err != nil { + return err + } + } + } + + return nil +} + +func removeIDAnnotation(r *resource.Resource) error { + // remove the annotation set by Kustomize to track the resource + annotations := r.GetAnnotations() + delete(annotations, idAnnotation) + return r.SetAnnotations(annotations) +} + +// UpdateResourceOptions updates the generator options for each resource in the +// given ResMap based on plugin provided annotations. +func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) { + for _, r := range rm.Resources() { + // Disable name hashing by default and require plugin to explicitly + // request it for each resource. + annotations := r.GetAnnotations() + behavior := annotations[BehaviorAnnotation] + var needsHash bool + if val, ok := annotations[HashAnnotation]; ok { + b, err := strconv.ParseBool(val) + if err != nil { + return nil, fmt.Errorf( + "the annotation %q contains an invalid value (%q)", + HashAnnotation, val) + } + needsHash = b + } + delete(annotations, HashAnnotation) + delete(annotations, BehaviorAnnotation) + if err := r.SetAnnotations(annotations); err != nil { + return nil, err + } + if needsHash { + r.EnableHashSuffix() + } + r.SetBehavior(types.NewGenerationBehavior(behavior)) + } + return rm, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/utils/utils_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/utils/utils_test.go new file mode 100644 index 000000000..9aa0184ed --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/plugins/utils/utils_test.go @@ -0,0 +1,116 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestDeterminePluginSrcRoot(t *testing.T) { + actual, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk()) + if err != nil { + t.Error(err) + } + if !filepath.IsAbs(actual) { + t.Errorf("expected absolute path, but got '%s'", actual) + } + if !strings.HasSuffix(actual, konfig.RelPluginHome) { + t.Errorf("expected suffix '%s' in '%s'", konfig.RelPluginHome, actual) + } +} + +func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource { + r := rf.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{"name": name}, + }) + annotations := map[string]string{} + if behavior != "" { + annotations[BehaviorAnnotation] = behavior + } + if hashValue != nil { + annotations[HashAnnotation] = *hashValue + } + if len(annotations) > 0 { + if err := r.SetAnnotations(annotations); err != nil { + panic(err) + } + } + return r +} + +func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource { + return rf.FromMapAndOption(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{"name": name}, + }, &types.GeneratorArgs{ + Behavior: behavior, + Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}}) +} + +func strptr(s string) *string { + return &s +} + +func TestUpdateResourceOptions(t *testing.T) { + rf := provider.NewDefaultDepProvider().GetResourceFactory() + in := resmap.New() + expected := resmap.New() + cases := []struct { + behavior string + needsHash bool + hashValue *string + }{ + {hashValue: strptr("false")}, + {hashValue: strptr("true"), needsHash: true}, + {behavior: "replace"}, + {behavior: "merge"}, + {behavior: "create"}, + {behavior: "nonsense"}, + {behavior: "merge", hashValue: strptr("false")}, + {behavior: "merge", hashValue: strptr("true"), needsHash: true}, + } + for i, c := range cases { + name := fmt.Sprintf("test%d", i) + err := in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue)) + require.NoError(t, err) + err = expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash)) + require.NoError(t, err) + } + actual, err := UpdateResourceOptions(in) + assert.NoError(t, err) + assert.NoError(t, expected.ErrorIfNotEqualLists(actual)) +} + +func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) { + rf := provider.NewDefaultDepProvider().GetResourceFactory() + cases := []string{ + "", + "FaLsE", + "TrUe", + "potato", + } + for i, c := range cases { + name := fmt.Sprintf("test%d", i) + in := resmap.New() + err := in.Append(makeConfigMap(rf, name, "", &c)) + require.NoError(t, err) + _, err = UpdateResourceOptions(in) + require.Error(t, err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/errmissingkustomization.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/errmissingkustomization.go new file mode 100644 index 000000000..45e489593 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/errmissingkustomization.go @@ -0,0 +1,48 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/konfig" +) + +type errMissingKustomization struct { + path string +} + +func (e *errMissingKustomization) Error() string { + return fmt.Sprintf( + "unable to find one of %v in directory '%s'", + commaOr(quoted(konfig.RecognizedKustomizationFileNames())), + e.path) +} + +func IsMissingKustomizationFileError(err error) bool { + _, ok := err.(*errMissingKustomization) + if ok { + return true + } + _, ok = errors.Cause(err).(*errMissingKustomization) + return ok +} + +func NewErrMissingKustomization(p string) *errMissingKustomization { + return &errMissingKustomization{path: p} +} + +func quoted(l []string) []string { + r := make([]string, len(l)) + for i, v := range l { + r[i] = "'" + v + "'" + } + return r +} + +func commaOr(q []string) string { + return strings.Join(q[:len(q)-1], ", ") + " or " + q[len(q)-1] +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget.go new file mode 100644 index 000000000..2fcfd830d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget.go @@ -0,0 +1,555 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + + "github.com/pkg/errors" + + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/accumulator" + "sigs.k8s.io/kustomize/api/internal/builtins" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" + "sigs.k8s.io/kustomize/api/internal/plugins/loader" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/konfig" + load "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/openapi" + "sigs.k8s.io/yaml" +) + +// KustTarget encapsulates the entirety of a kustomization build. +type KustTarget struct { + kustomization *types.Kustomization + kustFileName string + ldr ifc.Loader + validator ifc.Validator + rFactory *resmap.Factory + pLdr *loader.Loader + origin *resource.Origin +} + +// NewKustTarget returns a new instance of KustTarget. +func NewKustTarget( + ldr ifc.Loader, + validator ifc.Validator, + rFactory *resmap.Factory, + pLdr *loader.Loader) *KustTarget { + pLdrCopy := *pLdr + pLdrCopy.SetWorkDir(ldr.Root()) + return &KustTarget{ + ldr: ldr, + validator: validator, + rFactory: rFactory, + pLdr: &pLdrCopy, + } +} + +// Load attempts to load the target's kustomization file. +func (kt *KustTarget) Load() error { + content, kustFileName, err := loadKustFile(kt.ldr) + if err != nil { + return err + } + content, err = types.FixKustomizationPreUnmarshalling(content) + if err != nil { + return err + } + var k types.Kustomization + err = k.Unmarshal(content) + if err != nil { + return err + } + k.FixKustomizationPostUnmarshalling() + errs := k.EnforceFields() + if len(errs) > 0 { + return fmt.Errorf( + "Failed to read kustomization file under %s:\n"+ + strings.Join(errs, "\n"), kt.ldr.Root()) + } + kt.kustomization = &k + kt.kustFileName = kustFileName + return nil +} + +// Kustomization returns a copy of the immutable, internal kustomization object. +func (kt *KustTarget) Kustomization() types.Kustomization { + var result types.Kustomization + b, _ := json.Marshal(*kt.kustomization) + json.Unmarshal(b, &result) + return result +} + +func loadKustFile(ldr ifc.Loader) ([]byte, string, error) { + var content []byte + match := 0 + var kustFileName string + for _, kf := range konfig.RecognizedKustomizationFileNames() { + c, err := ldr.Load(kf) + if err == nil { + match += 1 + content = c + kustFileName = kf + } + } + switch match { + case 0: + return nil, "", NewErrMissingKustomization(ldr.Root()) + case 1: + return content, kustFileName, nil + default: + return nil, "", fmt.Errorf( + "Found multiple kustomization files under: %s\n", ldr.Root()) + } +} + +// MakeCustomizedResMap creates a fully customized ResMap +// per the instructions contained in its kustomization instance. +func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) { + return kt.makeCustomizedResMap() +} + +func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) { + var origin *resource.Origin + if len(kt.kustomization.BuildMetadata) != 0 { + origin = &resource.Origin{} + } + kt.origin = origin + ra, err := kt.AccumulateTarget() + if err != nil { + return nil, err + } + + // The following steps must be done last, not as part of + // the recursion implicit in AccumulateTarget. + + err = kt.addHashesToNames(ra) + if err != nil { + return nil, err + } + + // Given that names have changed (prefixs/suffixes added), + // fix all the back references to those names. + err = ra.FixBackReferences() + if err != nil { + return nil, err + } + + // With all the back references fixed, it's OK to resolve Vars. + err = ra.ResolveVars() + if err != nil { + return nil, err + } + + return ra.ResMap(), nil +} + +func (kt *KustTarget) addHashesToNames( + ra *accumulator.ResAccumulator) error { + p := builtins.NewHashTransformerPlugin() + err := kt.configureBuiltinPlugin(p, nil, builtinhelpers.HashTransformer) + if err != nil { + return err + } + return ra.Transform(p) +} + +// AccumulateTarget returns a new ResAccumulator, +// holding customized resources and the data/rules used +// to do so. The name back references and vars are +// not yet fixed. +// The origin parameter is used through the recursive calls +// to annotate each resource with information about where +// the resource came from, e.g. the file and/or the repository +// it originated from. +// As an entrypoint, one can pass an empty resource.Origin object to +// AccumulateTarget. As AccumulateTarget moves recursively +// through kustomization directories, it updates `origin.path` +// accordingly. When a remote base is found, it updates `origin.repo` +// and `origin.ref` accordingly. +func (kt *KustTarget) AccumulateTarget() ( + ra *accumulator.ResAccumulator, err error) { + return kt.accumulateTarget(accumulator.MakeEmptyAccumulator()) +} + +// ra should be empty when this KustTarget is a Kustomization, or the ra of the parent if this KustTarget is a Component +// (or empty if the Component does not have a parent). +func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) ( + resRa *accumulator.ResAccumulator, err error) { + ra, err = kt.accumulateResources(ra, kt.kustomization.Resources) + if err != nil { + return nil, errors.Wrap(err, "accumulating resources") + } + ra, err = kt.accumulateComponents(ra, kt.kustomization.Components) + if err != nil { + return nil, errors.Wrap(err, "accumulating components") + } + tConfig, err := builtinconfig.MakeTransformerConfig( + kt.ldr, kt.kustomization.Configurations) + if err != nil { + return nil, err + } + err = ra.MergeConfig(tConfig) + if err != nil { + return nil, errors.Wrapf( + err, "merging config %v", tConfig) + } + crdTc, err := accumulator.LoadConfigFromCRDs(kt.ldr, kt.kustomization.Crds) + if err != nil { + return nil, errors.Wrapf( + err, "loading CRDs %v", kt.kustomization.Crds) + } + err = ra.MergeConfig(crdTc) + if err != nil { + return nil, errors.Wrapf( + err, "merging CRDs %v", crdTc) + } + err = kt.runGenerators(ra) + if err != nil { + return nil, err + } + err = kt.runTransformers(ra) + if err != nil { + return nil, err + } + err = kt.runValidators(ra) + if err != nil { + return nil, err + } + err = ra.MergeVars(kt.kustomization.Vars) + if err != nil { + return nil, errors.Wrapf( + err, "merging vars %v", kt.kustomization.Vars) + } + err = kt.IgnoreLocal(ra) + if err != nil { + return nil, err + } + return ra, nil +} + +// IgnoreLocal drops the local resource by checking the annotation "config.kubernetes.io/local-config". +func (kt *KustTarget) IgnoreLocal(ra *accumulator.ResAccumulator) error { + rf := kt.rFactory.RF() + if rf.IncludeLocalConfigs { + return nil + } + remainRes, err := rf.DropLocalNodes(ra.ResMap().ToRNodeSlice()) + if err != nil { + return err + } + return ra.Intersection(kt.rFactory.FromResourceSlice(remainRes)) +} + +func (kt *KustTarget) runGenerators( + ra *accumulator.ResAccumulator) error { + var generators []*resmap.GeneratorWithProperties + gs, err := kt.configureBuiltinGenerators() + if err != nil { + return err + } + generators = append(generators, gs...) + + gs, err = kt.configureExternalGenerators() + if err != nil { + return errors.Wrap(err, "loading generator plugins") + } + generators = append(generators, gs...) + for i, g := range generators { + resMap, err := g.Generate() + if err != nil { + return err + } + if resMap != nil { + err = resMap.AddOriginAnnotation(generators[i].Origin) + if err != nil { + return errors.Wrapf(err, "adding origin annotations for generator %v", g) + } + } + err = ra.AbsorbAll(resMap) + if err != nil { + return errors.Wrapf(err, "merging from generator %v", g) + } + } + return nil +} + +func (kt *KustTarget) configureExternalGenerators() ( + []*resmap.GeneratorWithProperties, error) { + ra := accumulator.MakeEmptyAccumulator() + var generatorPaths []string + for _, p := range kt.kustomization.Generators { + // handle inline generators + rm, err := kt.rFactory.NewResMapFromBytes([]byte(p)) + if err != nil { + // not an inline config + generatorPaths = append(generatorPaths, p) + continue + } + // inline config, track the origin + if kt.origin != nil { + resources := rm.Resources() + for _, r := range resources { + r.SetOrigin(kt.origin.Append(kt.kustFileName)) + rm.Replace(r) + } + } + ra.AppendAll(rm) + } + ra, err := kt.accumulateResources(ra, generatorPaths) + if err != nil { + return nil, err + } + return kt.pLdr.LoadGenerators(kt.ldr, kt.validator, ra.ResMap()) +} + +func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error { + var r []*resmap.TransformerWithProperties + tConfig := ra.GetTransformerConfig() + lts, err := kt.configureBuiltinTransformers(tConfig) + if err != nil { + return err + } + r = append(r, lts...) + lts, err = kt.configureExternalTransformers(kt.kustomization.Transformers) + if err != nil { + return err + } + r = append(r, lts...) + return ra.Transform(newMultiTransformer(r)) +} + +func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]*resmap.TransformerWithProperties, error) { + ra := accumulator.MakeEmptyAccumulator() + var transformerPaths []string + for _, p := range transformers { + // handle inline transformers + rm, err := kt.rFactory.NewResMapFromBytes([]byte(p)) + if err != nil { + // not an inline config + transformerPaths = append(transformerPaths, p) + continue + } + // inline config, track the origin + if kt.origin != nil { + resources := rm.Resources() + for _, r := range resources { + r.SetOrigin(kt.origin.Append(kt.kustFileName)) + rm.Replace(r) + } + } + ra.AppendAll(rm) + } + ra, err := kt.accumulateResources(ra, transformerPaths) + if err != nil { + return nil, err + } + return kt.pLdr.LoadTransformers(kt.ldr, kt.validator, ra.ResMap()) +} + +func (kt *KustTarget) runValidators(ra *accumulator.ResAccumulator) error { + validators, err := kt.configureExternalTransformers(kt.kustomization.Validators) + if err != nil { + return err + } + for _, v := range validators { + // Validators shouldn't modify the resource map + orignal := ra.ResMap().DeepCopy() + err = v.Transform(ra.ResMap()) + if err != nil { + return err + } + newMap := ra.ResMap().DeepCopy() + if err = kt.removeValidatedByLabel(newMap); err != nil { + return err + } + if err = orignal.ErrorIfNotEqualSets(newMap); err != nil { + return fmt.Errorf("validator shouldn't modify the resource map: %v", err) + } + } + return nil +} + +func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) error { + resources := rm.Resources() + for _, r := range resources { + labels := r.GetLabels() + if _, found := labels[konfig.ValidatedByLabelKey]; !found { + continue + } + delete(labels, konfig.ValidatedByLabelKey) + if err := r.SetLabels(labels); err != nil { + return err + } + } + return nil +} + +// accumulateResources fills the given resourceAccumulator +// with resources read from the given list of paths. +func (kt *KustTarget) accumulateResources( + ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) { + for _, path := range paths { + // try loading resource as file then as base (directory or git repository) + if errF := kt.accumulateFile(ra, path); errF != nil { + // not much we can do if the error is an HTTP error so we bail out + if errors.Is(errF, load.ErrorHTTP) { + return nil, errF + } + ldr, err := kt.ldr.New(path) + if err != nil { + return nil, errors.Wrapf( + err, "accumulation err='%s'", errF.Error()) + } + // store the origin, we'll need it later + origin := kt.origin.Copy() + if kt.origin != nil { + kt.origin = kt.origin.Append(path) + ra, err = kt.accumulateDirectory(ra, ldr, false) + // after we are done recursing through the directory, reset the origin + kt.origin = &origin + } else { + ra, err = kt.accumulateDirectory(ra, ldr, false) + } + if err != nil { + return nil, errors.Wrapf( + err, "accumulation err='%s'", errF.Error()) + } + } + } + return ra, nil +} + +// accumulateResources fills the given resourceAccumulator +// with resources read from the given list of paths. +func (kt *KustTarget) accumulateComponents( + ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) { + for _, path := range paths { + // Components always refer to directories + ldr, errL := kt.ldr.New(path) + if errL != nil { + return nil, fmt.Errorf("loader.New %q", errL) + } + var errD error + // store the origin, we'll need it later + origin := kt.origin.Copy() + if kt.origin != nil { + kt.origin = kt.origin.Append(path) + ra, errD = kt.accumulateDirectory(ra, ldr, true) + // after we are done recursing through the directory, reset the origin + kt.origin = &origin + } else { + ra, errD = kt.accumulateDirectory(ra, ldr, true) + } + if errD != nil { + return nil, fmt.Errorf("accumulateDirectory: %q", errD) + } + } + return ra, nil +} + +func (kt *KustTarget) accumulateDirectory( + ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) { + defer ldr.Cleanup() + subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr) + err := subKt.Load() + if err != nil { + return nil, errors.Wrapf( + err, "couldn't make target for path '%s'", ldr.Root()) + } + subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata + subKt.origin = kt.origin + var bytes []byte + path := ldr.Root() + if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists { + bytes, err = ldr.Load(filepath.Join(path, openApiPath)) + if err != nil { + return nil, err + } + } + err = openapi.SetSchema(subKt.Kustomization().OpenAPI, bytes, false) + if err != nil { + return nil, err + } + if isComponent && subKt.kustomization.Kind != types.ComponentKind { + return nil, fmt.Errorf( + "expected kind '%s' for path '%s' but got '%s'", types.ComponentKind, ldr.Root(), subKt.kustomization.Kind) + } else if !isComponent && subKt.kustomization.Kind == types.ComponentKind { + return nil, fmt.Errorf( + "expected kind != '%s' for path '%s'", types.ComponentKind, ldr.Root()) + } + + var subRa *accumulator.ResAccumulator + if isComponent { + // Components don't create a new accumulator: the kustomization directives are added to the current accumulator + subRa, err = subKt.accumulateTarget(ra) + ra = accumulator.MakeEmptyAccumulator() + } else { + // Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later + // be merged into the current accumulator. + subRa, err = subKt.AccumulateTarget() + } + if err != nil { + return nil, errors.Wrapf( + err, "recursed accumulation of path '%s'", ldr.Root()) + } + err = ra.MergeAccumulator(subRa) + if err != nil { + return nil, errors.Wrapf( + err, "recursed merging from path '%s'", ldr.Root()) + } + return ra, nil +} + +func (kt *KustTarget) accumulateFile( + ra *accumulator.ResAccumulator, path string) error { + resources, err := kt.rFactory.FromFile(kt.ldr, path) + if err != nil { + return errors.Wrapf(err, "accumulating resources from '%s'", path) + } + if kt.origin != nil { + originAnno, err := kt.origin.Append(path).String() + if err != nil { + return errors.Wrapf(err, "cannot add path annotation for '%s'", path) + } + err = resources.AnnotateAll(utils.OriginAnnotationKey, originAnno) + if err != nil || originAnno == "" { + return errors.Wrapf(err, "cannot add path annotation for '%s'", path) + } + } + err = ra.AppendAll(resources) + if err != nil { + return errors.Wrapf(err, "merging resources from '%s'", path) + } + return nil +} + +func (kt *KustTarget) configureBuiltinPlugin( + p resmap.Configurable, c interface{}, bpt builtinhelpers.BuiltinPluginType) (err error) { + var y []byte + if c != nil { + y, err = yaml.Marshal(c) + if err != nil { + return errors.Wrapf( + err, "builtin %s marshal", bpt) + } + } + err = p.Config( + resmap.NewPluginHelpers( + kt.ldr, kt.validator, kt.rFactory, kt.pLdr.Config()), + y) + if err != nil { + return errors.Wrapf( + err, "trouble configuring builtin %s with config: `\n%s`", bpt, string(y)) + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget_configplugin.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget_configplugin.go new file mode 100644 index 000000000..5ad0fc490 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget_configplugin.go @@ -0,0 +1,438 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target + +import ( + "fmt" + "path/filepath" + + "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" + "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Functions dedicated to configuring the builtin +// transformer and generator plugins using config data +// read from a kustomization file and from the +// config.TransformerConfig, whose data may be a +// mix of hardcoded values and data read from file. +// +// Non-builtin plugins will get their configuration +// from their own dedicated structs and YAML files. +// +// There are some loops in the functions below because +// the kustomization file would, say, allow someone to +// request multiple secrets be made, or run multiple +// image tag transforms. In these cases, we'll need +// N plugin instances with differing configurations. + +func (kt *KustTarget) configureBuiltinGenerators() ( + result []*resmap.GeneratorWithProperties, err error) { + for _, bpt := range []builtinhelpers.BuiltinPluginType{ + builtinhelpers.ConfigMapGenerator, + builtinhelpers.SecretGenerator, + builtinhelpers.HelmChartInflationGenerator, + } { + r, err := generatorConfigurators[bpt]( + kt, bpt, builtinhelpers.GeneratorFactories[bpt]) + if err != nil { + return nil, err + } + + var generatorOrigin *resource.Origin + if kt.origin != nil { + generatorOrigin = &resource.Origin{ + Repo: kt.origin.Repo, + Ref: kt.origin.Ref, + ConfiguredIn: filepath.Join(kt.origin.Path, kt.kustFileName), + ConfiguredBy: yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "builtin", + Kind: bpt.String(), + }, + }, + } + } + + for i := range r { + result = append(result, &resmap.GeneratorWithProperties{Generator: r[i], Origin: generatorOrigin}) + } + } + return result, nil +} + +func (kt *KustTarget) configureBuiltinTransformers( + tc *builtinconfig.TransformerConfig) ( + result []*resmap.TransformerWithProperties, err error) { + for _, bpt := range []builtinhelpers.BuiltinPluginType{ + builtinhelpers.PatchStrategicMergeTransformer, + builtinhelpers.PatchTransformer, + builtinhelpers.NamespaceTransformer, + builtinhelpers.PrefixTransformer, + builtinhelpers.SuffixTransformer, + builtinhelpers.LabelTransformer, + builtinhelpers.AnnotationsTransformer, + builtinhelpers.PatchJson6902Transformer, + builtinhelpers.ReplicaCountTransformer, + builtinhelpers.ImageTagTransformer, + builtinhelpers.ReplacementTransformer, + } { + r, err := transformerConfigurators[bpt]( + kt, bpt, builtinhelpers.TransformerFactories[bpt], tc) + if err != nil { + return nil, err + } + var transformerOrigin *resource.Origin + if kt.origin != nil { + transformerOrigin = &resource.Origin{ + Repo: kt.origin.Repo, + Ref: kt.origin.Ref, + ConfiguredIn: filepath.Join(kt.origin.Path, kt.kustFileName), + ConfiguredBy: yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "builtin", + Kind: bpt.String(), + }, + }, + } + } + for i := range r { + result = append(result, &resmap.TransformerWithProperties{Transformer: r[i], Origin: transformerOrigin}) + } + } + return result, nil +} + +type gFactory func() resmap.GeneratorPlugin + +var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func( + kt *KustTarget, + bpt builtinhelpers.BuiltinPluginType, + factory gFactory) (result []resmap.Generator, err error){ + builtinhelpers.SecretGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) ( + result []resmap.Generator, err error) { + var c struct { + types.SecretArgs + } + for _, args := range kt.kustomization.SecretGenerator { + c.SecretArgs = args + c.SecretArgs.Options = types.MergeGlobalOptionsIntoLocal( + c.SecretArgs.Options, kt.kustomization.GeneratorOptions) + p := f() + err := kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + return + }, + + builtinhelpers.ConfigMapGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) ( + result []resmap.Generator, err error) { + var c struct { + types.ConfigMapArgs + } + for _, args := range kt.kustomization.ConfigMapGenerator { + c.ConfigMapArgs = args + c.ConfigMapArgs.Options = types.MergeGlobalOptionsIntoLocal( + c.ConfigMapArgs.Options, kt.kustomization.GeneratorOptions) + p := f() + err := kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + return + }, + + builtinhelpers.HelmChartInflationGenerator: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) ( + result []resmap.Generator, err error) { + var c struct { + types.HelmGlobals + types.HelmChart + } + var globals types.HelmGlobals + if kt.kustomization.HelmGlobals != nil { + globals = *kt.kustomization.HelmGlobals + } + for _, chart := range kt.kustomization.HelmCharts { + c.HelmGlobals = globals + c.HelmChart = chart + p := f() + if err = kt.configureBuiltinPlugin(p, c, bpt); err != nil { + return nil, err + } + result = append(result, p) + } + return + }, +} + +type tFactory func() resmap.TransformerPlugin + +var transformerConfigurators = map[builtinhelpers.BuiltinPluginType]func( + kt *KustTarget, + bpt builtinhelpers.BuiltinPluginType, + f tFactory, + tc *builtinconfig.TransformerConfig) (result []resmap.Transformer, err error){ + builtinhelpers.NamespaceTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if kt.kustomization.Namespace == "" { + return + } + var c struct { + types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + FieldSpecs []types.FieldSpec + } + c.Namespace = kt.kustomization.Namespace + c.FieldSpecs = tc.NameSpace + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return + }, + + builtinhelpers.PatchJson6902Transformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + var c struct { + Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` + JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` + } + for _, args := range kt.kustomization.PatchesJson6902 { + c.Target = args.Target + c.Path = args.Path + c.JsonOp = args.Patch + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + return + }, + builtinhelpers.PatchStrategicMergeTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if len(kt.kustomization.PatchesStrategicMerge) == 0 { + return + } + var c struct { + Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"` + } + c.Paths = kt.kustomization.PatchesStrategicMerge + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return + }, + builtinhelpers.PatchTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if len(kt.kustomization.Patches) == 0 { + return + } + var c struct { + Path string `json:"path,omitempty" yaml:"path,omitempty"` + Patch string `json:"patch,omitempty" yaml:"patch,omitempty"` + Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` + Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"` + } + for _, pc := range kt.kustomization.Patches { + c.Target = pc.Target + c.Patch = pc.Patch + c.Path = pc.Path + c.Options = pc.Options + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + return + }, + builtinhelpers.LabelTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if len(kt.kustomization.Labels) == 0 && len(kt.kustomization.CommonLabels) == 0 { + return + } + for _, label := range kt.kustomization.Labels { + var c struct { + Labels map[string]string + FieldSpecs []types.FieldSpec + } + c.Labels = label.Pairs + fss := types.FsSlice(label.FieldSpecs) + // merge the custom fieldSpecs with the default + if label.IncludeSelectors { + fss, err = fss.MergeAll(tc.CommonLabels) + } else { + // only add to metadata by default + fss, err = fss.MergeOne(types.FieldSpec{Path: "metadata/labels", CreateIfNotPresent: true}) + } + if err != nil { + return nil, err + } + c.FieldSpecs = fss + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + var c struct { + Labels map[string]string + FieldSpecs []types.FieldSpec + } + c.Labels = kt.kustomization.CommonLabels + c.FieldSpecs = tc.CommonLabels + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return + }, + builtinhelpers.AnnotationsTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if len(kt.kustomization.CommonAnnotations) == 0 { + return + } + var c struct { + Annotations map[string]string + FieldSpecs []types.FieldSpec + } + c.Annotations = kt.kustomization.CommonAnnotations + c.FieldSpecs = tc.CommonAnnotations + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return + }, + builtinhelpers.PrefixTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if kt.kustomization.NamePrefix == "" { + return + } + var c struct { + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + } + c.Prefix = kt.kustomization.NamePrefix + c.FieldSpecs = tc.NamePrefix + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return + }, + builtinhelpers.SuffixTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if kt.kustomization.NameSuffix == "" { + return + } + var c struct { + Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` + FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + } + c.Suffix = kt.kustomization.NameSuffix + c.FieldSpecs = tc.NameSuffix + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return + }, + builtinhelpers.ImageTagTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + var c struct { + ImageTag types.Image + FieldSpecs []types.FieldSpec + } + for _, args := range kt.kustomization.Images { + c.ImageTag = args + c.FieldSpecs = tc.Images + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + return + }, + builtinhelpers.ReplacementTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + if len(kt.kustomization.Replacements) == 0 { + return + } + var c struct { + Replacements []types.ReplacementField + } + c.Replacements = kt.kustomization.Replacements + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + return result, nil + }, + builtinhelpers.ReplicaCountTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + var c struct { + Replica types.Replica + FieldSpecs []types.FieldSpec + } + for _, args := range kt.kustomization.Replicas { + c.Replica = args + c.FieldSpecs = tc.Replicas + p := f() + err = kt.configureBuiltinPlugin(p, c, bpt) + if err != nil { + return nil, err + } + result = append(result, p) + } + return + }, + // No kustomization file keyword for this yet. + builtinhelpers.ValueAddTransformer: func( + kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) ( + result []resmap.Transformer, err error) { + return nil, fmt.Errorf("valueadd keyword not yet defined") + }, +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget_test.go new file mode 100644 index 000000000..2349106bb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/kusttarget_test.go @@ -0,0 +1,259 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target_test + +import ( + "encoding/base64" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/api/types" +) + +// KustTarget is primarily tested in the krusty package with +// high level tests. + +func TestLoad(t *testing.T) { + th := kusttest_test.MakeHarness(t) + expectedTypeMeta := types.TypeMeta{ + APIVersion: "kustomize.config.k8s.io/v1beta1", + Kind: "Kustomization", + } + + testCases := map[string]struct { + errContains string + content string + k types.Kustomization + }{ + "empty": { + // no content + k: types.Kustomization{ + TypeMeta: expectedTypeMeta, + }, + }, + "nonsenseLatin": { + errContains: "error converting YAML to JSON", + content: ` + Lorem ipsum dolor sit amet, consectetur + adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. + `, + }, + "simple": { + content: ` +commonLabels: + app: nginx +`, + k: types.Kustomization{ + TypeMeta: expectedTypeMeta, + CommonLabels: map[string]string{"app": "nginx"}, + }, + }, + "commented": { + content: ` +# Licensed to the Blah Blah Software Foundation +# ... +# yada yada yada. + +commonLabels: + app: nginx +`, + k: types.Kustomization{ + TypeMeta: expectedTypeMeta, + CommonLabels: map[string]string{"app": "nginx"}, + }, + }, + } + + kt := makeKustTargetWithRf( + t, th.GetFSys(), "/", provider.NewDefaultDepProvider()) + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + th.WriteK("/", tc.content) + err := kt.Load() + if tc.errContains != "" { + require.NotNilf(t, err, "expected error containing: `%s`", tc.errContains) + require.Contains(t, err.Error(), tc.errContains) + } else { + require.Nilf(t, err, "got error: %v", err) + k := kt.Kustomization() + require.Condition(t, func() bool { + return reflect.DeepEqual(tc.k, k) + }, "expected %v, got %v", tc.k, k) + } + }) + } +} + +func TestMakeCustomizedResMap(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/whatever", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namePrefix: foo- +nameSuffix: -bar +namespace: ns1 +commonLabels: + app: nginx +commonAnnotations: + note: This is a test annotation +resources: + - deployment.yaml + - namespace.yaml +generatorOptions: + disableNameSuffixHash: false +configMapGenerator: +- name: literalConfigMap + literals: + - DB_USERNAME=admin + - DB_PASSWORD=somepw +secretGenerator: +- name: secret + literals: + - DB_USERNAME=admin + - DB_PASSWORD=somepw + type: Opaque +patchesJson6902: +- target: + group: apps + version: v1 + kind: Deployment + name: dply1 + path: jsonpatch.json +`) + th.WriteF("/whatever/deployment.yaml", ` +apiVersion: apps/v1 +metadata: + name: dply1 +kind: Deployment +`) + th.WriteF("/whatever/namespace.yaml", ` +apiVersion: v1 +kind: Namespace +metadata: + name: ns1 +`) + th.WriteF("/whatever/jsonpatch.json", `[ + {"op": "add", "path": "/spec/replica", "value": "3"} +]`) + + pvd := provider.NewDefaultDepProvider() + resFactory := pvd.GetResourceFactory() + + resources := []*resource.Resource{ + resFactory.FromMapWithName("dply1", map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "foo-dply1-bar", + "namespace": "ns1", + "labels": map[string]interface{}{ + "app": "nginx", + }, + "annotations": map[string]interface{}{ + "note": "This is a test annotation", + }, + }, + "spec": map[string]interface{}{ + "replica": "3", + "selector": map[string]interface{}{ + "matchLabels": map[string]interface{}{ + "app": "nginx", + }, + }, + "template": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "note": "This is a test annotation", + }, + "labels": map[string]interface{}{ + "app": "nginx", + }, + }, + }, + }, + }), + resFactory.FromMapWithName("ns1", map[string]interface{}{ + "apiVersion": "v1", + "kind": "Namespace", + "metadata": map[string]interface{}{ + "name": "ns1", + "labels": map[string]interface{}{ + "app": "nginx", + }, + "annotations": map[string]interface{}{ + "note": "This is a test annotation", + }, + }, + }), + resFactory.FromMapWithName("literalConfigMap", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "foo-literalConfigMap-bar-g5f6t456f5", + "namespace": "ns1", + "labels": map[string]interface{}{ + "app": "nginx", + }, + "annotations": map[string]interface{}{ + "note": "This is a test annotation", + }, + }, + "data": map[string]interface{}{ + "DB_USERNAME": "admin", + "DB_PASSWORD": "somepw", + }, + }), + resFactory.FromMapWithName("secret", + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "foo-secret-bar-82c2g5f8f6", + "namespace": "ns1", + "labels": map[string]interface{}{ + "app": "nginx", + }, + "annotations": map[string]interface{}{ + "note": "This is a test annotation", + }, + }, + "type": ifc.SecretTypeOpaque, + "data": map[string]interface{}{ + "DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")), + "DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")), + }, + }), + } + + expected := resmap.New() + for _, r := range resources { + if err := expected.Append(r); err != nil { + t.Fatalf("unexpected error %v", err) + } + } + expected.RemoveBuildAnnotations() + expYaml, err := expected.AsYaml() + assert.NoError(t, err) + + kt := makeKustTargetWithRf(t, th.GetFSys(), "/whatever", pvd) + assert.NoError(t, kt.Load()) + actual, err := kt.MakeCustomizedResMap() + assert.NoError(t, err) + actual.RemoveBuildAnnotations() + actYaml, err := actual.AsYaml() + assert.NoError(t, err) + assert.Equal(t, string(expYaml), string(actYaml)) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/maker_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/maker_test.go new file mode 100644 index 000000000..9d4f9b1ef --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/maker_test.go @@ -0,0 +1,46 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target_test + +import ( + "testing" + + pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader" + "sigs.k8s.io/kustomize/api/internal/target" + fLdr "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func makeAndLoadKustTarget( + t *testing.T, + fSys filesys.FileSystem, + root string) *target.KustTarget { + kt := makeKustTargetWithRf(t, fSys, root, provider.NewDefaultDepProvider()) + if err := kt.Load(); err != nil { + t.Fatalf("Unexpected load error %v", err) + } + return kt +} + +func makeKustTargetWithRf( + t *testing.T, + fSys filesys.FileSystem, + root string, + pvd *provider.DepProvider) *target.KustTarget { + ldr, err := fLdr.NewLoader(fLdr.RestrictionRootOnly, root, fSys) + if err != nil { + t.Fatal(err) + } + rf := resmap.NewFactory(pvd.GetResourceFactory()) + pc := types.DisabledPluginConfig() + return target.NewKustTarget( + ldr, + valtest_test.MakeFakeValidator(), + rf, + pLdr.NewLoader(pc, rf, fSys)) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/multitransformer.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/multitransformer.go new file mode 100644 index 000000000..3bc0a8715 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/multitransformer.go @@ -0,0 +1,41 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target + +import ( + "sigs.k8s.io/kustomize/api/resmap" +) + +// multiTransformer contains a list of transformers. +type multiTransformer struct { + transformers []*resmap.TransformerWithProperties +} + +var _ resmap.Transformer = &multiTransformer{} + +// newMultiTransformer constructs a multiTransformer. +func newMultiTransformer(t []*resmap.TransformerWithProperties) resmap.Transformer { + r := &multiTransformer{ + transformers: make([]*resmap.TransformerWithProperties, len(t)), + } + copy(r.transformers, t) + return r +} + +// Transform applies the member transformers in order to the resources, +// optionally detecting and erroring on commutation conflict. +func (o *multiTransformer) Transform(m resmap.ResMap) error { + for _, t := range o.transformers { + if err := t.Transform(m); err != nil { + return err + } + if t.Origin != nil { + if err := m.AddTransformerAnnotation(t.Origin); err != nil { + return err + } + } + m.DropEmpties() + } + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/vars_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/vars_test.go new file mode 100644 index 000000000..901dcde90 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/target/vars_test.go @@ -0,0 +1,188 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package target_test + +import ( + "fmt" + "strings" + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// To simplify tests, these vars specified in alphabetical order. +var someVars = []types.Var{ + { + Name: "AWARD", + ObjRef: types.Target{ + APIVersion: "v7", + Gvk: resid.Gvk{Kind: "Service"}, + Name: "nobelPrize"}, + FieldRef: types.FieldSelector{FieldPath: "some.arbitrary.path"}, + }, + { + Name: "BIRD", + ObjRef: types.Target{ + APIVersion: "v300", + Gvk: resid.Gvk{Kind: "Service"}, + Name: "heron"}, + FieldRef: types.FieldSelector{FieldPath: "metadata.name"}, + }, + { + Name: "FRUIT", + ObjRef: types.Target{ + Gvk: resid.Gvk{Kind: "Service"}, + Name: "apple"}, + FieldRef: types.FieldSelector{FieldPath: "metadata.name"}, + }, + { + Name: "VEGETABLE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Kind: "Leafy"}, + Name: "kale"}, + FieldRef: types.FieldSelector{FieldPath: "metadata.name"}, + }, +} + +func TestGetAllVarsSimple(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/app", ` +vars: + - name: AWARD + objref: + kind: Service + name: nobelPrize + apiVersion: v7 + fieldref: + fieldpath: some.arbitrary.path + - name: BIRD + objref: + kind: Service + name: heron + apiVersion: v300 +`) + ra, err := makeAndLoadKustTarget( + t, th.GetFSys(), "/app").AccumulateTarget() + if err != nil { + t.Fatalf("Err: %v", err) + } + vars := ra.Vars() + if len(vars) != 2 { + t.Fatalf("unexpected size %d", len(vars)) + } + for i := range vars[:2] { + // By using Var.DeepEqual, we are protecting the code + // from a potential invocation of vars[i].ObjRef.GVK() + // during accumulateTarget + if !vars[i].DeepEqual(someVars[i]) { + t.Fatalf("unexpected var[%d]:\n %v\n %v", i, vars[i], someVars[i]) + } + } +} + +func TestGetAllVarsNested(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/app/base", ` +vars: + - name: AWARD + objref: + kind: Service + name: nobelPrize + apiVersion: v7 + fieldref: + fieldpath: some.arbitrary.path + - name: BIRD + objref: + kind: Service + name: heron + apiVersion: v300 +`) + th.WriteK("/app/overlays/o1", ` +vars: + - name: FRUIT + objref: + kind: Service + name: apple +resources: +- ../../base +`) + th.WriteK("/app/overlays/o2", ` +vars: + - name: VEGETABLE + objref: + kind: Leafy + name: kale +resources: +- ../o1 +`) + + ra, err := makeAndLoadKustTarget( + t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget() + if err != nil { + t.Fatalf("Err: %v", err) + } + vars := ra.Vars() + if len(vars) != 4 { + for i, v := range vars { + fmt.Printf("%v: %v\n", i, v) + } + t.Fatalf("expected 4 vars, got %d", len(vars)) + } + for i := range vars { + // By using Var.DeepEqual, we are protecting the code + // from a potential invocation of vars[i].ObjRef.GVK() + // during accumulateTarget + if !vars[i].DeepEqual(someVars[i]) { + t.Fatalf("unexpected var[%d]:\n %v\n %v", i, vars[i], someVars[i]) + } + } +} + +func TestVarCollisionsForbidden(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("/app/base", ` +vars: + - name: AWARD + objref: + kind: Service + name: nobelPrize + apiVersion: v7 + fieldref: + fieldpath: some.arbitrary.path + - name: BIRD + objref: + kind: Service + name: heron + apiVersion: v300 +`) + th.WriteK("/app/overlays/o1", ` +vars: + - name: AWARD + objref: + kind: Service + name: academy +resources: +- ../../base +`) + th.WriteK("/app/overlays/o2", ` +vars: + - name: VEGETABLE + objref: + kind: Leafy + name: kale +resources: +- ../o1 +`) + _, err := makeAndLoadKustTarget( + t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget() + if err == nil { + t.Fatalf("expected var collision") + } + if !strings.Contains(err.Error(), + "var 'AWARD' already encountered") { + t.Fatalf("unexpected error: %v", err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/annotations.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/annotations.go new file mode 100644 index 000000000..34f3553af --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/annotations.go @@ -0,0 +1,26 @@ +package utils + +import "sigs.k8s.io/kustomize/api/konfig" + +const ( + // build annotations + BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds" + BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames" + BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes" + BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes" + BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces" + BuildAnnotationsRefBy = konfig.ConfigAnnoDomain + "/refBy" + BuildAnnotationsGenBehavior = konfig.ConfigAnnoDomain + "/generatorBehavior" + BuildAnnotationsGenAddHashSuffix = konfig.ConfigAnnoDomain + "/needsHashSuffix" + + // the following are only for patches, to specify whether they can change names + // and kinds of their targets + BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange" + BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange" + + // for keeping track of origin and transformer data + OriginAnnotationKey = "config.kubernetes.io/origin" + TransformerAnnotationKey = "alpha.config.kubernetes.io/transformations" + + Enabled = "enabled" +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/errtimeout.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/errtimeout.go new file mode 100644 index 000000000..24b8abe66 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/errtimeout.go @@ -0,0 +1,36 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "fmt" + "time" + + "github.com/pkg/errors" +) + +type errTimeOut struct { + duration time.Duration + cmd string +} + +func NewErrTimeOut(d time.Duration, c string) errTimeOut { + return errTimeOut{duration: d, cmd: c} +} + +func (e errTimeOut) Error() string { + return fmt.Sprintf("hit %s timeout running '%s'", e.duration, e.cmd) +} + +func IsErrTimeout(err error) bool { + if err == nil { + return false + } + _, ok := err.(errTimeOut) + if ok { + return true + } + _, ok = errors.Cause(err).(errTimeOut) + return ok +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/makeResIds.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/makeResIds.go new file mode 100644 index 000000000..17f25168e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/makeResIds.go @@ -0,0 +1,64 @@ +package utils + +import ( + "fmt" + "strings" + + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// MakeResIds returns all of an RNode's current and previous Ids +func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) { + var result []resid.ResId + apiVersion := n.Field(yaml.APIVersionField) + var group, version string + if apiVersion != nil { + group, version = resid.ParseGroupVersion(yaml.GetValue(apiVersion.Value)) + } + result = append(result, resid.NewResIdWithNamespace( + resid.Gvk{Group: group, Version: version, Kind: n.GetKind()}, n.GetName(), n.GetNamespace()), + ) + prevIds, err := PrevIds(n) + if err != nil { + return nil, err + } + result = append(result, prevIds...) + return result, nil +} + +// PrevIds returns all of an RNode's previous Ids +func PrevIds(n *yaml.RNode) ([]resid.ResId, error) { + var ids []resid.ResId + // TODO: merge previous names and namespaces into one list of + // pairs on one annotation so there is no chance of error + annotations := n.GetAnnotations() + if _, ok := annotations[BuildAnnotationPreviousNames]; !ok { + return nil, nil + } + names := strings.Split(annotations[BuildAnnotationPreviousNames], ",") + ns := strings.Split(annotations[BuildAnnotationPreviousNamespaces], ",") + kinds := strings.Split(annotations[BuildAnnotationPreviousKinds], ",") + // This should never happen + if len(names) != len(ns) || len(names) != len(kinds) { + return nil, fmt.Errorf( + "number of previous names, " + + "number of previous namespaces, " + + "number of previous kinds not equal") + } + for i := range names { + meta, err := n.GetMeta() + if err != nil { + return nil, err + } + group, version := resid.ParseGroupVersion(meta.APIVersion) + gvk := resid.Gvk{ + Group: group, + Version: version, + Kind: kinds[i], + } + ids = append(ids, resid.NewResIdWithNamespace( + gvk, names[i], ns[i])) + } + return ids, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/pathsplitter.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/pathsplitter.go new file mode 100644 index 000000000..aa560299f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/pathsplitter.go @@ -0,0 +1,64 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import "strings" + +// TODO: Move these to kyaml + +// PathSplitter splits a delimited string, permitting escaped delimiters. +func PathSplitter(path string, delimiter string) []string { + ps := strings.Split(path, delimiter) + var res []string + res = append(res, ps[0]) + for i := 1; i < len(ps); i++ { + last := len(res) - 1 + if strings.HasSuffix(res[last], `\`) { + res[last] = strings.TrimSuffix(res[last], `\`) + delimiter + ps[i] + } else { + res = append(res, ps[i]) + } + } + return res +} + +// SmarterPathSplitter splits a path, retaining bracketed elements. +// If the element is a list entry identifier (defined by the '='), +// it will retain the brackets. +// E.g. "[name=com.foo.someapp]" survives as one thing after splitting +// "spec.template.spec.containers.[name=com.foo.someapp].image" +// See kyaml/yaml/match.go for use of list entry identifiers. +// If the element is a mapping entry identifier, it will remove the +// brackets. +// E.g. "a.b.c" survives as one thing after splitting +// "metadata.annotations.[a.b.c] +// This function uses `PathSplitter`, so it also respects escaped delimiters. +func SmarterPathSplitter(path string, delimiter string) []string { + var result []string + split := PathSplitter(path, delimiter) + + for i := 0; i < len(split); i++ { + elem := split[i] + if strings.HasPrefix(elem, "[") && !strings.HasSuffix(elem, "]") { + // continue until we find the matching "]" + bracketed := []string{elem} + for i < len(split)-1 { + i++ + bracketed = append(bracketed, split[i]) + if strings.HasSuffix(split[i], "]") { + break + } + } + bracketedStr := strings.Join(bracketed, delimiter) + if strings.Contains(bracketedStr, "=") { + result = append(result, bracketedStr) + } else { + result = append(result, strings.Trim(bracketedStr, "[]")) + } + } else { + result = append(result, elem) + } + } + return result +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/pathsplitter_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/pathsplitter_test.go new file mode 100644 index 000000000..21dcc8391 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/pathsplitter_test.go @@ -0,0 +1,94 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/internal/utils" +) + +func TestPathSplitter(t *testing.T) { + for _, tc := range []struct { + exp []string + path string + }{ + { + path: "", + exp: []string{""}, + }, + { + path: "s", + exp: []string{"s"}, + }, + { + path: "a/b/c", + exp: []string{"a", "b", "c"}, + }, + { + path: `a/b[]/c`, + exp: []string{"a", "b[]", "c"}, + }, + { + path: `a/b\/c/d\/e/f`, + exp: []string{"a", "b/c", "d/e", "f"}, + }, + { + // The actual reason for this. + path: `metadata/annotations/nginx.ingress.kubernetes.io\/auth-secret`, + exp: []string{ + "metadata", + "annotations", + "nginx.ingress.kubernetes.io/auth-secret"}, + }, + } { + assert.Equal(t, tc.exp, PathSplitter(tc.path, "/")) + } +} + +func TestSmarterPathSplitter(t *testing.T) { + testCases := map[string]struct { + input string + expected []string + }{ + "simple": { + input: "spec.replicas", + expected: []string{"spec", "replicas"}, + }, + "sequence": { + input: "spec.data.[name=first].key", + expected: []string{"spec", "data", "[name=first]", "key"}, + }, + "key, value with . prefix": { + input: "spec.data.[.name=.first].key", + expected: []string{"spec", "data", "[.name=.first]", "key"}, + }, + "key, value with . suffix": { + input: "spec.data.[name.=first.].key", + expected: []string{"spec", "data", "[name.=first.]", "key"}, + }, + "multiple '.' in value": { + input: "spec.data.[name=f.i.r.s.t.].key", + expected: []string{"spec", "data", "[name=f.i.r.s.t.]", "key"}, + }, + "with escaped delimiter": { + input: `spec\.replicas`, + expected: []string{`spec.replicas`}, + }, + "unmatched bracket": { + input: "spec.data.[name=f.i.[r.s.t..key", + expected: []string{"spec", "data", "[name=f.i.[r.s.t..key"}, + }, + "mapping value with .": { + input: "metadata.annotations.[a.b.c/d.e.f-g.]", + expected: []string{"metadata", "annotations", "a.b.c/d.e.f-g."}, + }, + } + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + assert.Equal(t, tc.expected, SmarterPathSplitter(tc.input, ".")) + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/stringslice.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/stringslice.go new file mode 100644 index 000000000..3dc422725 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/stringslice.go @@ -0,0 +1,44 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +// StringSliceIndex returns the index of the str, else -1. +func StringSliceIndex(slice []string, str string) int { + for i := range slice { + if slice[i] == str { + return i + } + } + return -1 +} + +// StringSliceContains returns true if the slice has the string. +func StringSliceContains(slice []string, str string) bool { + for _, s := range slice { + if s == str { + return true + } + } + return false +} + +// SameEndingSubSlice returns true if the slices end the same way, e.g. +// {"a", "b", "c"}, {"b", "c"} => true +// {"a", "b", "c"}, {"a", "b"} => false +// If one slice is empty and the other is not, return false. +func SameEndingSubSlice(shortest, longest []string) bool { + if len(shortest) > len(longest) { + longest, shortest = shortest, longest + } + diff := len(longest) - len(shortest) + if len(shortest) == 0 { + return diff == 0 + } + for i := len(shortest) - 1; i >= 0; i-- { + if longest[i+diff] != shortest[i] { + return false + } + } + return true +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/stringslice_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/stringslice_test.go new file mode 100644 index 000000000..d80693490 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/stringslice_test.go @@ -0,0 +1,37 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/internal/utils" +) + +func TestStringSliceIndex(t *testing.T) { + assert.Equal(t, 0, StringSliceIndex([]string{"a", "b"}, "a")) + assert.Equal(t, 1, StringSliceIndex([]string{"a", "b"}, "b")) + assert.Equal(t, -1, StringSliceIndex([]string{"a", "b"}, "c")) + assert.Equal(t, -1, StringSliceIndex([]string{}, "c")) +} + +func TestStringSliceContains(t *testing.T) { + assert.True(t, StringSliceContains([]string{"a", "b"}, "a")) + assert.True(t, StringSliceContains([]string{"a", "b"}, "b")) + assert.False(t, StringSliceContains([]string{"a", "b"}, "c")) + assert.False(t, StringSliceContains([]string{}, "c")) +} + +func TestSameEndingSubarray(t *testing.T) { + assert.True(t, SameEndingSubSlice([]string{"", "a", "b"}, []string{"a", "b"})) + assert.True(t, SameEndingSubSlice([]string{"a", "b", ""}, []string{"b", ""})) + assert.True(t, SameEndingSubSlice([]string{"a", "b"}, []string{"a", "b"})) + assert.True(t, SameEndingSubSlice([]string{"a", "b"}, []string{"b"})) + assert.True(t, SameEndingSubSlice([]string{"b"}, []string{"a", "b"})) + assert.True(t, SameEndingSubSlice([]string{}, []string{})) + assert.False(t, SameEndingSubSlice([]string{"a", "b"}, []string{"b", "a"})) + assert.False(t, SameEndingSubSlice([]string{"a", "b"}, []string{})) + assert.False(t, SameEndingSubSlice([]string{"a", "b"}, []string{""})) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/timedcall.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/timedcall.go new file mode 100644 index 000000000..0afadd0c3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/timedcall.go @@ -0,0 +1,23 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "time" +) + +// TimedCall runs fn, failing if it doesn't complete in the given duration. +// The description is used in the timeout error message. +func TimedCall(description string, d time.Duration, fn func() error) error { + done := make(chan error) + timer := time.NewTimer(d) + defer timer.Stop() + go func() { done <- fn() }() + select { + case err := <-done: + return err + case <-timer.C: + return NewErrTimeOut(d, description) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/timedcall_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/timedcall_test.go new file mode 100644 index 000000000..192edc48c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/utils/timedcall_test.go @@ -0,0 +1,64 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package utils_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/internal/utils" +) + +const ( + timeToWait = 10 * time.Millisecond + tooSlow = 2 * timeToWait +) + +func errMsg(msg string) string { + return fmt.Sprintf("hit %s timeout running '%s'", timeToWait, msg) +} + +func TestTimedCallFastNoError(t *testing.T) { + err := TimedCall( + "fast no error", timeToWait, + func() error { return nil }) + if !assert.NoError(t, err) { + t.Fatal(err) + } +} + +func TestTimedCallFastWithError(t *testing.T) { + err := TimedCall( + "fast with error", timeToWait, + func() error { return assert.AnError }) + if assert.Error(t, err) { + assert.EqualError(t, err, assert.AnError.Error()) + } else { + t.Fail() + } +} + +func TestTimedCallSlowNoError(t *testing.T) { + err := TimedCall( + "slow no error", timeToWait, + func() error { time.Sleep(tooSlow); return nil }) + if assert.Error(t, err) { + assert.EqualError(t, err, errMsg("slow no error")) + } else { + t.Fail() + } +} + +func TestTimedCallSlowWithError(t *testing.T) { + err := TimedCall( + "slow with error", timeToWait, + func() error { time.Sleep(tooSlow); return assert.AnError }) + if assert.Error(t, err) { + assert.EqualError(t, err, errMsg("slow with error")) + } else { + t.Fail() + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/validate/fieldvalidator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/validate/fieldvalidator.go new file mode 100644 index 000000000..5ccfc3ce7 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/validate/fieldvalidator.go @@ -0,0 +1,68 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package validate + +import ( + "sigs.k8s.io/kustomize/api/ifc" +) + +// FieldValidator implements ifc.Validator to check +// the values of various KRM string fields, +// e.g. labels, annotations, names, namespaces. +// +// TODO: Have this use kyaml/yaml/internal/k8sgen/pkg/labels +// which has label and annotation validation code, but is internal +// so this impl would need to move to kyaml (a fine idea). +type FieldValidator struct { +} + +var _ ifc.Validator = (*FieldValidator)(nil) + +func NewFieldValidator() *FieldValidator { + return &FieldValidator{} +} + +// TODO(#FieldValidator): implement MakeAnnotationValidator +func (f FieldValidator) MakeAnnotationValidator() func(map[string]string) error { + return func(x map[string]string) error { + return nil + } +} + +// TODO(#FieldValidator): implement MakeAnnotationNameValidator +func (f FieldValidator) MakeAnnotationNameValidator() func([]string) error { + return func(x []string) error { + return nil + } +} + +// TODO(#FieldValidator): implement MakeLabelValidator +func (f FieldValidator) MakeLabelValidator() func(map[string]string) error { + return func(x map[string]string) error { + return nil + } +} + +// TODO(#FieldValidator): implement MakeLabelNameValidator +func (f FieldValidator) MakeLabelNameValidator() func([]string) error { + return func(x []string) error { + return nil + } +} + +// TODO(#FieldValidator): implement ValidateNamespace +func (f FieldValidator) ValidateNamespace(s string) []string { + var errs []string + return errs +} + +// TODO(#FieldValidator): implement ErrIfInvalidKey +func (f FieldValidator) ErrIfInvalidKey(s string) error { + return nil +} + +// TODO(#FieldValidator): implement IsEnvVarName +func (f FieldValidator) IsEnvVarName(k string) error { + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/validate/fieldvalidator_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/validate/fieldvalidator_test.go new file mode 100644 index 000000000..82f18775b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/internal/validate/fieldvalidator_test.go @@ -0,0 +1,4 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package validate_test diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/commonannotations.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/commonannotations.go new file mode 100644 index 000000000..97c1d6b22 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/commonannotations.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const commonAnnotationFieldSpecs = ` +commonAnnotations: +- path: metadata/annotations + create: true + +- path: spec/template/metadata/annotations + create: true + version: v1 + kind: ReplicationController + +- path: spec/template/metadata/annotations + create: true + kind: Deployment + +- path: spec/template/metadata/annotations + create: true + kind: ReplicaSet + +- path: spec/template/metadata/annotations + create: true + kind: DaemonSet + +- path: spec/template/metadata/annotations + create: true + kind: StatefulSet + +- path: spec/template/metadata/annotations + create: true + group: batch + kind: Job + +- path: spec/jobTemplate/metadata/annotations + create: true + group: batch + kind: CronJob + +- path: spec/jobTemplate/spec/template/metadata/annotations + create: true + group: batch + kind: CronJob + +` diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/commonlabels.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/commonlabels.go new file mode 100644 index 000000000..7775a544f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/commonlabels.go @@ -0,0 +1,159 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const commonLabelFieldSpecs = ` +commonLabels: +- path: metadata/labels + create: true + +- path: spec/selector + create: true + version: v1 + kind: Service + +- path: spec/selector + create: true + version: v1 + kind: ReplicationController + +- path: spec/template/metadata/labels + create: true + version: v1 + kind: ReplicationController + +- path: spec/selector/matchLabels + create: true + kind: Deployment + +- path: spec/template/metadata/labels + create: true + kind: Deployment + +- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels + create: false + group: apps + kind: Deployment + +- path: spec/template/spec/affinity/podAffinity/requiredDuringSchedulingIgnoredDuringExecution/labelSelector/matchLabels + create: false + group: apps + kind: Deployment + +- path: spec/template/spec/affinity/podAntiAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels + create: false + group: apps + kind: Deployment + +- path: spec/template/spec/affinity/podAntiAffinity/requiredDuringSchedulingIgnoredDuringExecution/labelSelector/matchLabels + create: false + group: apps + kind: Deployment + +- path: spec/template/spec/topologySpreadConstraints/labelSelector/matchLabels + create: false + group: apps + kind: Deployment + +- path: spec/selector/matchLabels + create: true + kind: ReplicaSet + +- path: spec/template/metadata/labels + create: true + kind: ReplicaSet + +- path: spec/selector/matchLabels + create: true + kind: DaemonSet + +- path: spec/template/metadata/labels + create: true + kind: DaemonSet + +- path: spec/selector/matchLabels + create: true + group: apps + kind: StatefulSet + +- path: spec/template/metadata/labels + create: true + group: apps + kind: StatefulSet + +- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels + create: false + group: apps + kind: StatefulSet + +- path: spec/template/spec/affinity/podAffinity/requiredDuringSchedulingIgnoredDuringExecution/labelSelector/matchLabels + create: false + group: apps + kind: StatefulSet + +- path: spec/template/spec/affinity/podAntiAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels + create: false + group: apps + kind: StatefulSet + +- path: spec/template/spec/affinity/podAntiAffinity/requiredDuringSchedulingIgnoredDuringExecution/labelSelector/matchLabels + create: false + group: apps + kind: StatefulSet + +- path: spec/template/spec/topologySpreadConstraints/labelSelector/matchLabels + create: false + group: apps + kind: StatefulSet + +- path: spec/volumeClaimTemplates[]/metadata/labels + create: true + group: apps + kind: StatefulSet + +- path: spec/selector/matchLabels + create: false + group: batch + kind: Job + +- path: spec/template/metadata/labels + create: true + group: batch + kind: Job + +- path: spec/jobTemplate/spec/selector/matchLabels + create: false + group: batch + kind: CronJob + +- path: spec/jobTemplate/metadata/labels + create: true + group: batch + kind: CronJob + +- path: spec/jobTemplate/spec/template/metadata/labels + create: true + group: batch + kind: CronJob + +- path: spec/selector/matchLabels + create: false + group: policy + kind: PodDisruptionBudget + +- path: spec/podSelector/matchLabels + create: false + group: networking.k8s.io + kind: NetworkPolicy + +- path: spec/ingress/from/podSelector/matchLabels + create: false + group: networking.k8s.io + kind: NetworkPolicy + +- path: spec/egress/to/podSelector/matchLabels + create: false + group: networking.k8s.io + kind: NetworkPolicy +` diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/defaultconfig.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/defaultconfig.go new file mode 100644 index 000000000..29673d76a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/defaultconfig.go @@ -0,0 +1,40 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +import ( + "bytes" +) + +// GetDefaultFieldSpecs returns default fieldSpecs. +func GetDefaultFieldSpecs() []byte { + configData := [][]byte{ + []byte(namePrefixFieldSpecs), + []byte(nameSuffixFieldSpecs), + []byte(commonLabelFieldSpecs), + []byte(commonAnnotationFieldSpecs), + []byte(namespaceFieldSpecs), + []byte(varReferenceFieldSpecs), + []byte(nameReferenceFieldSpecs), + []byte(imagesFieldSpecs), + []byte(replicasFieldSpecs), + } + return bytes.Join(configData, []byte("\n")) +} + +// GetDefaultFieldSpecsAsMap returns default fieldSpecs +// as a string->string map. +func GetDefaultFieldSpecsAsMap() map[string]string { + result := make(map[string]string) + result["nameprefix"] = namePrefixFieldSpecs + result["namesuffix"] = nameSuffixFieldSpecs + result["commonlabels"] = commonLabelFieldSpecs + result["commonannotations"] = commonAnnotationFieldSpecs + result["namespace"] = namespaceFieldSpecs + result["varreference"] = varReferenceFieldSpecs + result["namereference"] = nameReferenceFieldSpecs + result["images"] = imagesFieldSpecs + result["replicas"] = replicasFieldSpecs + return result +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/doc.go new file mode 100644 index 000000000..4b7b5faac --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/doc.go @@ -0,0 +1,8 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package builtinpluginconsts provides builtin plugin +// configuration data. Builtin plugins can also be +// configured individually with plugin config files, +// in which case the constants in this package are ignored. +package builtinpluginconsts diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/images.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/images.go new file mode 100644 index 000000000..b8d8bf1e3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/images.go @@ -0,0 +1,18 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const ( + imagesFieldSpecs = ` +images: +- path: spec/containers[]/image + create: true +- path: spec/initContainers[]/image + create: true +- path: spec/template/spec/containers[]/image + create: true +- path: spec/template/spec/initContainers[]/image + create: true +` +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/nameprefix.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/nameprefix.go new file mode 100644 index 000000000..59a25a61f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/nameprefix.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const ( + namePrefixFieldSpecs = ` +namePrefix: +- path: metadata/name +` +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namereference.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namereference.go new file mode 100644 index 000000000..724e9a996 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namereference.go @@ -0,0 +1,403 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +// LINT.IfChange +const ( + nameReferenceFieldSpecs = ` +nameReference: +- kind: Deployment + fieldSpecs: + - path: spec/scaleTargetRef/name + kind: HorizontalPodAutoscaler + +- kind: ReplicationController + fieldSpecs: + - path: spec/scaleTargetRef/name + kind: HorizontalPodAutoscaler + +- kind: ReplicaSet + fieldSpecs: + - path: spec/scaleTargetRef/name + kind: HorizontalPodAutoscaler + +- kind: StatefulSet + fieldSpecs: + - path: spec/scaleTargetRef/name + kind: HorizontalPodAutoscaler + +- kind: ConfigMap + version: v1 + fieldSpecs: + - path: spec/volumes/configMap/name + version: v1 + kind: Pod + - path: spec/containers/env/valueFrom/configMapKeyRef/name + version: v1 + kind: Pod + - path: spec/initContainers/env/valueFrom/configMapKeyRef/name + version: v1 + kind: Pod + - path: spec/containers/envFrom/configMapRef/name + version: v1 + kind: Pod + - path: spec/initContainers/envFrom/configMapRef/name + version: v1 + kind: Pod + - path: spec/volumes/projected/sources/configMap/name + version: v1 + kind: Pod + - path: template/spec/volumes/configMap/name + kind: PodTemplate + - path: spec/template/spec/volumes/configMap/name + kind: Deployment + - path: spec/template/spec/containers/env/valueFrom/configMapKeyRef/name + kind: Deployment + - path: spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name + kind: Deployment + - path: spec/template/spec/containers/envFrom/configMapRef/name + kind: Deployment + - path: spec/template/spec/initContainers/envFrom/configMapRef/name + kind: Deployment + - path: spec/template/spec/volumes/projected/sources/configMap/name + kind: Deployment + - path: spec/template/spec/volumes/configMap/name + kind: ReplicaSet + - path: spec/template/spec/containers/env/valueFrom/configMapKeyRef/name + kind: ReplicaSet + - path: spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name + kind: ReplicaSet + - path: spec/template/spec/containers/envFrom/configMapRef/name + kind: ReplicaSet + - path: spec/template/spec/initContainers/envFrom/configMapRef/name + kind: ReplicaSet + - path: spec/template/spec/volumes/projected/sources/configMap/name + kind: ReplicaSet + - path: spec/template/spec/volumes/configMap/name + kind: DaemonSet + - path: spec/template/spec/containers/env/valueFrom/configMapKeyRef/name + kind: DaemonSet + - path: spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name + kind: DaemonSet + - path: spec/template/spec/containers/envFrom/configMapRef/name + kind: DaemonSet + - path: spec/template/spec/initContainers/envFrom/configMapRef/name + kind: DaemonSet + - path: spec/template/spec/volumes/projected/sources/configMap/name + kind: DaemonSet + - path: spec/template/spec/volumes/configMap/name + kind: StatefulSet + - path: spec/template/spec/containers/env/valueFrom/configMapKeyRef/name + kind: StatefulSet + - path: spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name + kind: StatefulSet + - path: spec/template/spec/containers/envFrom/configMapRef/name + kind: StatefulSet + - path: spec/template/spec/initContainers/envFrom/configMapRef/name + kind: StatefulSet + - path: spec/template/spec/volumes/projected/sources/configMap/name + kind: StatefulSet + - path: spec/template/spec/volumes/configMap/name + kind: Job + - path: spec/template/spec/containers/env/valueFrom/configMapKeyRef/name + kind: Job + - path: spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name + kind: Job + - path: spec/template/spec/containers/envFrom/configMapRef/name + kind: Job + - path: spec/template/spec/initContainers/envFrom/configMapRef/name + kind: Job + - path: spec/template/spec/volumes/projected/sources/configMap/name + kind: Job + - path: spec/jobTemplate/spec/template/spec/volumes/configMap/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/volumes/projected/sources/configMap/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/containers/env/valueFrom/configMapKeyRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/containers/envFrom/configMapRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/initContainers/envFrom/configMapRef/name + kind: CronJob + - path: spec/configSource/configMap + kind: Node + - path: rules/resourceNames + kind: Role + - path: rules/resourceNames + kind: ClusterRole + - path: metadata/annotations/nginx.ingress.kubernetes.io\/fastcgi-params-configmap + kind: Ingress + +- kind: Secret + version: v1 + fieldSpecs: + - path: spec/volumes/secret/secretName + version: v1 + kind: Pod + - path: spec/containers/env/valueFrom/secretKeyRef/name + version: v1 + kind: Pod + - path: spec/initContainers/env/valueFrom/secretKeyRef/name + version: v1 + kind: Pod + - path: spec/containers/envFrom/secretRef/name + version: v1 + kind: Pod + - path: spec/initContainers/envFrom/secretRef/name + version: v1 + kind: Pod + - path: spec/imagePullSecrets/name + version: v1 + kind: Pod + - path: spec/volumes/projected/sources/secret/name + version: v1 + kind: Pod + - path: spec/template/spec/volumes/secret/secretName + kind: Deployment + - path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: Deployment + - path: spec/template/spec/initContainers/env/valueFrom/secretKeyRef/name + kind: Deployment + - path: spec/template/spec/containers/envFrom/secretRef/name + kind: Deployment + - path: spec/template/spec/initContainers/envFrom/secretRef/name + kind: Deployment + - path: spec/template/spec/imagePullSecrets/name + kind: Deployment + - path: spec/template/spec/volumes/projected/sources/secret/name + kind: Deployment + - path: spec/template/spec/volumes/secret/secretName + kind: ReplicaSet + - path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: ReplicaSet + - path: spec/template/spec/initContainers/env/valueFrom/secretKeyRef/name + kind: ReplicaSet + - path: spec/template/spec/containers/envFrom/secretRef/name + kind: ReplicaSet + - path: spec/template/spec/initContainers/envFrom/secretRef/name + kind: ReplicaSet + - path: spec/template/spec/imagePullSecrets/name + kind: ReplicaSet + - path: spec/template/spec/volumes/projected/sources/secret/name + kind: ReplicaSet + - path: spec/template/spec/volumes/secret/secretName + kind: DaemonSet + - path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: DaemonSet + - path: spec/template/spec/initContainers/env/valueFrom/secretKeyRef/name + kind: DaemonSet + - path: spec/template/spec/containers/envFrom/secretRef/name + kind: DaemonSet + - path: spec/template/spec/initContainers/envFrom/secretRef/name + kind: DaemonSet + - path: spec/template/spec/imagePullSecrets/name + kind: DaemonSet + - path: spec/template/spec/volumes/projected/sources/secret/name + kind: DaemonSet + - path: spec/template/spec/volumes/secret/secretName + kind: StatefulSet + - path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: StatefulSet + - path: spec/template/spec/initContainers/env/valueFrom/secretKeyRef/name + kind: StatefulSet + - path: spec/template/spec/containers/envFrom/secretRef/name + kind: StatefulSet + - path: spec/template/spec/initContainers/envFrom/secretRef/name + kind: StatefulSet + - path: spec/template/spec/imagePullSecrets/name + kind: StatefulSet + - path: spec/template/spec/volumes/projected/sources/secret/name + kind: StatefulSet + - path: spec/template/spec/volumes/secret/secretName + kind: Job + - path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: Job + - path: spec/template/spec/initContainers/env/valueFrom/secretKeyRef/name + kind: Job + - path: spec/template/spec/containers/envFrom/secretRef/name + kind: Job + - path: spec/template/spec/initContainers/envFrom/secretRef/name + kind: Job + - path: spec/template/spec/imagePullSecrets/name + kind: Job + - path: spec/template/spec/volumes/projected/sources/secret/name + kind: Job + - path: spec/jobTemplate/spec/template/spec/volumes/secret/secretName + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/volumes/projected/sources/secret/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/initContainers/env/valueFrom/secretKeyRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/containers/envFrom/secretRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/initContainers/envFrom/secretRef/name + kind: CronJob + - path: spec/jobTemplate/spec/template/spec/imagePullSecrets/name + kind: CronJob + - path: spec/tls/secretName + kind: Ingress + - path: metadata/annotations/ingress.kubernetes.io\/auth-secret + kind: Ingress + - path: metadata/annotations/nginx.ingress.kubernetes.io\/auth-secret + kind: Ingress + - path: metadata/annotations/nginx.ingress.kubernetes.io\/auth-tls-secret + kind: Ingress + - path: spec/tls/secretName + kind: Ingress + - path: imagePullSecrets/name + kind: ServiceAccount + - path: parameters/secretName + kind: StorageClass + - path: parameters/adminSecretName + kind: StorageClass + - path: parameters/userSecretName + kind: StorageClass + - path: parameters/secretRef + kind: StorageClass + - path: rules/resourceNames + kind: Role + - path: rules/resourceNames + kind: ClusterRole + - path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name + kind: Service + group: serving.knative.dev + version: v1 + - path: spec/azureFile/secretName + kind: PersistentVolume + +- kind: Service + version: v1 + fieldSpecs: + - path: spec/serviceName + kind: StatefulSet + group: apps + - path: spec/rules/http/paths/backend/serviceName + kind: Ingress + - path: spec/backend/serviceName + kind: Ingress + - path: spec/rules/http/paths/backend/service/name + kind: Ingress + - path: spec/defaultBackend/service/name + kind: Ingress + - path: spec/service/name + kind: APIService + group: apiregistration.k8s.io + - path: webhooks/clientConfig/service + kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + - path: webhooks/clientConfig/service + kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + +- kind: Role + group: rbac.authorization.k8s.io + fieldSpecs: + - path: roleRef/name + kind: RoleBinding + group: rbac.authorization.k8s.io + +- kind: ClusterRole + group: rbac.authorization.k8s.io + fieldSpecs: + - path: roleRef/name + kind: RoleBinding + group: rbac.authorization.k8s.io + - path: roleRef/name + kind: ClusterRoleBinding + group: rbac.authorization.k8s.io + +- kind: ServiceAccount + version: v1 + fieldSpecs: + - path: subjects + kind: RoleBinding + group: rbac.authorization.k8s.io + - path: subjects + kind: ClusterRoleBinding + group: rbac.authorization.k8s.io + - path: spec/serviceAccountName + kind: Pod + - path: spec/template/spec/serviceAccountName + kind: StatefulSet + - path: spec/template/spec/serviceAccountName + kind: Deployment + - path: spec/template/spec/serviceAccountName + kind: ReplicationController + - path: spec/jobTemplate/spec/template/spec/serviceAccountName + kind: CronJob + - path: spec/template/spec/serviceAccountName + kind: Job + - path: spec/template/spec/serviceAccountName + kind: DaemonSet + +- kind: PersistentVolumeClaim + version: v1 + fieldSpecs: + - path: spec/volumes/persistentVolumeClaim/claimName + kind: Pod + - path: spec/template/spec/volumes/persistentVolumeClaim/claimName + kind: StatefulSet + - path: spec/template/spec/volumes/persistentVolumeClaim/claimName + kind: Deployment + - path: spec/template/spec/volumes/persistentVolumeClaim/claimName + kind: ReplicationController + - path: spec/jobTemplate/spec/template/spec/volumes/persistentVolumeClaim/claimName + kind: CronJob + - path: spec/template/spec/volumes/persistentVolumeClaim/claimName + kind: Job + - path: spec/template/spec/volumes/persistentVolumeClaim/claimName + kind: DaemonSet + +- kind: PersistentVolume + version: v1 + fieldSpecs: + - path: spec/volumeName + kind: PersistentVolumeClaim + - path: rules/resourceNames + kind: ClusterRole + +- kind: StorageClass + version: v1 + group: storage.k8s.io + fieldSpecs: + - path: spec/storageClassName + kind: PersistentVolume + - path: spec/storageClassName + kind: PersistentVolumeClaim + - path: spec/volumeClaimTemplates/spec/storageClassName + kind: StatefulSet + +- kind: PriorityClass + version: v1 + group: scheduling.k8s.io + fieldSpecs: + - path: spec/priorityClassName + kind: Pod + - path: spec/template/spec/priorityClassName + kind: StatefulSet + - path: spec/template/spec/priorityClassName + kind: Deployment + - path: spec/template/spec/priorityClassName + kind: ReplicationController + - path: spec/jobTemplate/spec/template/spec/priorityClassName + kind: CronJob + - path: spec/template/spec/priorityClassName + kind: Job + - path: spec/template/spec/priorityClassName + kind: DaemonSet + +- kind: IngressClass + version: v1 + group: networking.k8s.io/v1 + fieldSpecs: + - path: spec/ingressClassName + kind: Ingress +` +) + +// LINT.ThenChange(/examples/transformerconfigs/README.md) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namespace.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namespace.go new file mode 100644 index 000000000..a35ef9c6f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namespace.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const ( + namespaceFieldSpecs = ` +namespace: +- path: metadata/namespace + create: true +- path: metadata/name + kind: Namespace + create: true +- path: subjects + kind: RoleBinding +- path: subjects + kind: ClusterRoleBinding +- path: spec/service/namespace + group: apiregistration.k8s.io + kind: APIService + create: true +- path: spec/conversion/webhook/clientConfig/service/namespace + group: apiextensions.k8s.io + kind: CustomResourceDefinition +` +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namesuffix.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namesuffix.go new file mode 100644 index 000000000..11592bd2b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/namesuffix.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const ( + nameSuffixFieldSpecs = ` +nameSuffix: +- path: metadata/name +` +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/replicas.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/replicas.go new file mode 100644 index 000000000..76549c21f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/replicas.go @@ -0,0 +1,23 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const replicasFieldSpecs = ` +replicas: +- path: spec/replicas + create: true + kind: Deployment + +- path: spec/replicas + create: true + kind: ReplicationController + +- path: spec/replicas + create: true + kind: ReplicaSet + +- path: spec/replicas + create: true + kind: StatefulSet +` diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/varreference.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/varreference.go new file mode 100644 index 000000000..f4011d825 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/builtinpluginconsts/varreference.go @@ -0,0 +1,223 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package builtinpluginconsts + +const ( + varReferenceFieldSpecs = ` +varReference: +- path: spec/jobTemplate/spec/template/spec/containers/args + kind: CronJob + +- path: spec/jobTemplate/spec/template/spec/containers/command + kind: CronJob + +- path: spec/jobTemplate/spec/template/spec/containers/env/value + kind: CronJob + +- path: spec/jobTemplate/spec/template/spec/containers/volumeMounts/mountPath + kind: CronJob + +- path: spec/jobTemplate/spec/template/spec/initContainers/args + kind: CronJob + +- path: spec/jobTemplate/spec/template/spec/initContainers/command + kind: CronJob + +- path: spec/jobTemplate/spec/template/spec/initContainers/env/value + kind: CronJob + +- 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 + +- path: spec/template/spec/containers/command + kind: DaemonSet + +- path: spec/template/spec/containers/env/value + kind: DaemonSet + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: DaemonSet + +- path: spec/template/spec/initContainers/args + kind: DaemonSet + +- path: spec/template/spec/initContainers/command + kind: DaemonSet + +- path: spec/template/spec/initContainers/env/value + kind: DaemonSet + +- 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 + +- path: spec/template/spec/containers/command + kind: Deployment + +- path: spec/template/spec/containers/env/value + kind: Deployment + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: Deployment + +- path: spec/template/spec/initContainers/args + kind: Deployment + +- path: spec/template/spec/initContainers/command + kind: Deployment + +- path: spec/template/spec/initContainers/env/value + kind: Deployment + +- path: spec/template/spec/initContainers/volumeMounts/mountPath + kind: Deployment + +- path: spec/template/spec/volumes/nfs/server + kind: Deployment + +- path: spec/template/metadata/annotations + kind: Deployment + +- path: spec/rules/host + kind: Ingress + +- path: spec/tls/hosts + kind: Ingress + +- path: spec/tls/secretName + kind: Ingress + +- path: spec/template/spec/containers/args + kind: Job + +- path: spec/template/spec/containers/command + kind: Job + +- path: spec/template/spec/containers/env/value + kind: Job + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: Job + +- path: spec/template/spec/initContainers/args + kind: Job + +- path: spec/template/spec/initContainers/command + kind: Job + +- path: spec/template/spec/initContainers/env/value + kind: Job + +- path: spec/template/spec/initContainers/volumeMounts/mountPath + kind: Job + +- path: spec/template/spec/volumes/nfs/server + kind: Job + +- path: spec/containers/args + kind: Pod + +- path: spec/containers/command + kind: Pod + +- path: spec/containers/env/value + kind: Pod + +- path: spec/containers/volumeMounts/mountPath + kind: Pod + +- path: spec/initContainers/args + kind: Pod + +- path: spec/initContainers/command + kind: Pod + +- path: spec/initContainers/env/value + kind: Pod + +- path: spec/initContainers/volumeMounts/mountPath + kind: Pod + +- path: spec/volumes/nfs/server + kind: Pod + +- path: spec/template/spec/containers/args + kind: ReplicaSet + +- path: spec/template/spec/containers/command + kind: ReplicaSet + +- path: spec/template/spec/containers/env/value + kind: ReplicaSet + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: ReplicaSet + +- path: spec/template/spec/initContainers/args + kind: ReplicaSet + +- path: spec/template/spec/initContainers/command + kind: ReplicaSet + +- path: spec/template/spec/initContainers/env/value + kind: ReplicaSet + +- path: spec/template/spec/initContainers/volumeMounts/mountPath + kind: ReplicaSet + +- path: spec/template/spec/volumes/nfs/server + kind: ReplicaSet + +- path: spec/ports/port + kind: Service + +- path: spec/ports/targetPort + kind: Service + +- path: spec/template/spec/containers/args + kind: StatefulSet + +- path: spec/template/spec/containers/command + kind: StatefulSet + +- path: spec/template/spec/containers/env/value + kind: StatefulSet + +- path: spec/template/spec/containers/volumeMounts/mountPath + kind: StatefulSet + +- path: spec/template/spec/initContainers/args + kind: StatefulSet + +- path: spec/template/spec/initContainers/command + kind: StatefulSet + +- path: spec/template/spec/initContainers/env/value + kind: StatefulSet + +- 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 +` +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/doc.go new file mode 100644 index 000000000..8c5f8f2cd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/doc.go @@ -0,0 +1,7 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package konfig provides configuration methods and constants +// for the kustomize API, e.g. the set of file names to look for +// to identify a kustomization root. +package konfig diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/general.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/general.go new file mode 100644 index 000000000..712bfe789 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/general.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package konfig + +// RecognizedKustomizationFileNames is a list of file names +// that kustomize recognizes. +// To avoid ambiguity, a kustomization directory may not +// contain more than one match to this list. +func RecognizedKustomizationFileNames() []string { + return []string{ + "kustomization.yaml", + "kustomization.yml", + "Kustomization", + } +} + +func DefaultKustomizationFileName() string { + return RecognizedKustomizationFileNames()[0] +} + +const ( + // An environment variable to consult for kustomization + // configuration data. See: + // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + XdgConfigHomeEnv = "XDG_CONFIG_HOME" + + // Use this when XdgConfigHomeEnv not defined. + XdgConfigHomeEnvDefault = ".config" + + // A program name, for use in help, finding the XDG_CONFIG_DIR, etc. + ProgramName = "kustomize" + + // ConfigAnnoDomain is internal configuration-related annotation namespace. + // See https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md. + ConfigAnnoDomain = "internal.config.kubernetes.io" + + // If a resource has this annotation, kustomize will drop it. + IgnoredByKustomizeAnnotation = "config.kubernetes.io/local-config" + + // Label key that indicates the resources are built from Kustomize + ManagedbyLabelKey = "app.kubernetes.io/managed-by" + + // An environment variable to turn on/off adding the ManagedByLabelKey + EnableManagedbyLabelEnv = "KUSTOMIZE_ENABLE_MANAGEDBY_LABEL" + + // Label key that indicates the resources are validated by a validator + ValidatedByLabelKey = "validated-by" +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/plugins.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/plugins.go new file mode 100644 index 000000000..30bd3b6e3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/plugins.go @@ -0,0 +1,138 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package konfig + +import ( + "os" + "path/filepath" + "runtime" + + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +const ( + // Symbol that must be used inside Go plugins. + PluginSymbol = "KustomizePlugin" + + // Name of environment variable used to set AbsPluginHome. + // See that variable for an explanation. + KustomizePluginHomeEnv = "KUSTOMIZE_PLUGIN_HOME" + + // Relative path below XDG_CONFIG_HOME/kustomize to find plugins. + // e.g. AbsPluginHome = XDG_CONFIG_HOME/kustomize/plugin + RelPluginHome = "plugin" + + // Location of builtin plugins below AbsPluginHome. + BuiltinPluginPackage = "builtin" + + // The value of kubernetes ApiVersion to use in configuration + // files for builtin plugins. + // The value for non-builtins can be anything. + BuiltinPluginApiVersion = BuiltinPluginPackage + + // Domain from which kustomize code is imported, for locating + // plugin source code under $GOPATH when GOPATH is defined. + DomainName = "sigs.k8s.io" + + // Injected into plugin paths when plugins are disabled. + // Provides a clue in flows that shouldn't happen. + NoPluginHomeSentinal = "/No/non-builtin/plugins!" +) + +type NotedFunc struct { + Note string + F func() string +} + +// DefaultAbsPluginHome returns the absolute path in the given file +// system to first directory that looks like a good candidate for +// the home of kustomize plugins. +func DefaultAbsPluginHome(fSys filesys.FileSystem) (string, error) { + return FirstDirThatExistsElseError( + "plugin root", fSys, []NotedFunc{ + { + Note: "homed in $" + KustomizePluginHomeEnv, + F: func() string { + return os.Getenv(KustomizePluginHomeEnv) + }, + }, + { + Note: "homed in $" + XdgConfigHomeEnv, + F: func() string { + if root := os.Getenv(XdgConfigHomeEnv); root != "" { + return filepath.Join(root, ProgramName, RelPluginHome) + } + // do not look in "kustomize/plugin" if XdgConfigHomeEnv is unset + return "" + }, + }, + { + Note: "homed in default value of $" + XdgConfigHomeEnv, + F: func() string { + return filepath.Join( + HomeDir(), XdgConfigHomeEnvDefault, + ProgramName, RelPluginHome) + }, + }, + { + Note: "homed in home directory", + F: func() string { + return filepath.Join( + HomeDir(), ProgramName, RelPluginHome) + }, + }, + }) +} + +// FirstDirThatExistsElseError tests different path functions for +// existence, returning the first that works, else error if all fail. +func FirstDirThatExistsElseError( + what string, + fSys filesys.FileSystem, + pathFuncs []NotedFunc) (string, error) { + var nope []types.Pair + for _, dt := range pathFuncs { + if dir := dt.F(); dir != "" { + if fSys.Exists(dir) { + return dir, nil + } + nope = append(nope, types.Pair{Key: dt.Note, Value: dir}) + } else { + nope = append(nope, types.Pair{Key: dt.Note, Value: ""}) + } + } + return "", types.NewErrUnableToFind(what, nope) +} + +func HomeDir() string { + home := os.Getenv(homeEnv()) + if len(home) > 0 { + return home + } + return "~" +} + +func homeEnv() string { + if runtime.GOOS == "windows" { + return "USERPROFILE" + } + return "HOME" +} + +func CurrentWorkingDir() string { + // Try for full path first to be explicit. + pwd := os.Getenv(pwdEnv()) + if len(pwd) > 0 { + return pwd + } + return filesys.SelfDir +} + +func pwdEnv() string { + if runtime.GOOS == "windows" { + return "CD" + } + return "PWD" +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/plugins_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/plugins_test.go new file mode 100644 index 000000000..f3ed8e828 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/konfig/plugins_test.go @@ -0,0 +1,187 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package konfig + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestDefaultAbsPluginHome_NoKustomizePluginHomeEnv(t *testing.T) { + fSys := filesys.MakeFsInMemory() + keep, isSet := os.LookupEnv(KustomizePluginHomeEnv) + if isSet { + unsetenv(t, KustomizePluginHomeEnv) + } + _, err := DefaultAbsPluginHome(fSys) + if isSet { + setenv(t, KustomizePluginHomeEnv, keep) + } + if err == nil { + t.Fatalf("expected err") + } + if !types.IsErrUnableToFind(err) { + t.Fatalf("unexpected err: %v", err) + } + for _, expectedMsg := range []string{ + "unable to find plugin root - tried:", + "(''; homed in $KUSTOMIZE_PLUGIN_HOME)", + "; homed in $XDG_CONFIG_HOME)", + "/.config/kustomize/plugin'; homed in default value of $XDG_CONFIG_HOME)", + "/kustomize/plugin'; homed in home directory)", + } { + assert.Contains(t, err.Error(), expectedMsg) + } +} + +func TestDefaultAbsPluginHome_EmptyKustomizePluginHomeEnv(t *testing.T) { + keep, isSet := os.LookupEnv(KustomizePluginHomeEnv) + setenv(t, KustomizePluginHomeEnv, "") + + _, err := DefaultAbsPluginHome(filesys.MakeFsInMemory()) + if !isSet { + unsetenv(t, KustomizePluginHomeEnv) + } else { + setenv(t, KustomizePluginHomeEnv, keep) + } + if err == nil { + t.Fatalf("expected err") + } + if !types.IsErrUnableToFind(err) { + t.Fatalf("unexpected err: %v", err) + } + assert.Contains(t, err.Error(), "(''; homed in $KUSTOMIZE_PLUGIN_HOME)") +} + +func TestDefaultAbsPluginHome_WithKustomizePluginHomeEnv(t *testing.T) { + fSys := filesys.MakeFsInMemory() + keep, isSet := os.LookupEnv(KustomizePluginHomeEnv) + if !isSet { + keep = "whatever" + setenv(t, KustomizePluginHomeEnv, keep) + } + err := fSys.Mkdir(keep) + require.NoError(t, err) + h, err := DefaultAbsPluginHome(fSys) + if !isSet { + unsetenv(t, KustomizePluginHomeEnv) + } + require.NoError(t, err) + if h != keep { + t.Fatalf("unexpected config dir: %s", h) + } +} + +func TestDefaultAbsPluginHomeWithXdg(t *testing.T) { + fSys := filesys.MakeFsInMemory() + keep, isSet := os.LookupEnv(XdgConfigHomeEnv) + if !isSet { + keep = "whatever" + setenv(t, XdgConfigHomeEnv, keep) + } + configDir := filepath.Join(keep, ProgramName, RelPluginHome) + err := fSys.Mkdir(configDir) + require.NoError(t, err) + h, err := DefaultAbsPluginHome(fSys) + if !isSet { + unsetenv(t, XdgConfigHomeEnv) + } + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if h != configDir { + t.Fatalf("unexpected config dir: %s", h) + } +} + +func TestDefaultAbsPluginHomeNoConfig(t *testing.T) { + fSys := filesys.MakeFsInMemory() + keep, isSet := os.LookupEnv(XdgConfigHomeEnv) + if isSet { + unsetenv(t, XdgConfigHomeEnv) + } + _, err := DefaultAbsPluginHome(fSys) + if isSet { + setenv(t, XdgConfigHomeEnv, keep) + } + if err == nil { + t.Fatalf("expected err") + } + if !types.IsErrUnableToFind(err) { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestDefaultAbsPluginHomeEmptyXdgConfig(t *testing.T) { + keep, isSet := os.LookupEnv(XdgConfigHomeEnv) + setenv(t, XdgConfigHomeEnv, "") + if isSet { + unsetenv(t, XdgConfigHomeEnv) + } + _, err := DefaultAbsPluginHome(filesys.MakeFsInMemory()) + if isSet { + setenv(t, XdgConfigHomeEnv, keep) + } + if err == nil { + t.Fatalf("expected err") + } + if !types.IsErrUnableToFind(err) { + t.Fatalf("unexpected err: %v", err) + } + assert.Contains(t, err.Error(), "(''; homed in $XDG_CONFIG_HOME)") +} + +func TestDefaultAbsPluginHomeNoXdgWithDotConfig(t *testing.T) { + fSys := filesys.MakeFsInMemory() + configDir := filepath.Join( + HomeDir(), XdgConfigHomeEnvDefault, ProgramName, RelPluginHome) + err := fSys.Mkdir(configDir) + require.NoError(t, err) + keep, isSet := os.LookupEnv(XdgConfigHomeEnv) + if isSet { + unsetenv(t, XdgConfigHomeEnv) + } + s, err := DefaultAbsPluginHome(fSys) + require.NoError(t, err) + if isSet { + setenv(t, XdgConfigHomeEnv, keep) + } + if s != configDir { + t.Fatalf("unexpected config dir: %s", s) + } +} + +func TestDefaultAbsPluginHomeNoXdgJustHomeDir(t *testing.T) { + fSys := filesys.MakeFsInMemory() + configDir := filepath.Join( + HomeDir(), ProgramName, RelPluginHome) + err := fSys.Mkdir(configDir) + require.NoError(t, err) + keep, isSet := os.LookupEnv(XdgConfigHomeEnv) + if isSet { + unsetenv(t, XdgConfigHomeEnv) + } + s, err := DefaultAbsPluginHome(fSys) + require.NoError(t, err) + if isSet { + setenv(t, XdgConfigHomeEnv, keep) + } + if s != configDir { + t.Fatalf("unexpected config dir: %s", s) + } +} + +func setenv(t *testing.T, key, value string) { + require.NoError(t, os.Setenv(key, value)) +} + +func unsetenv(t *testing.T, key string) { + require.NoError(t, os.Unsetenv(key)) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/accumulation_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/accumulation_test.go new file mode 100644 index 000000000..c9c9703ab --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/accumulation_test.go @@ -0,0 +1,162 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "path/filepath" + "strings" + "testing" + + . "sigs.k8s.io/kustomize/api/internal/target" + "sigs.k8s.io/kustomize/api/konfig" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestTargetMustHaveKustomizationFile(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: aService +`) + th.WriteF("deeper/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: anotherService +`) + err := th.RunWithErr(".", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected an error") + } + if !IsMissingKustomizationFileError(err) { + t.Fatalf("unexpected error: %q", err) + } +} + +func TestTargetMustHaveOnlyOneKustomizationFile(t *testing.T) { + th := kusttest_test.MakeHarness(t) + for _, n := range konfig.RecognizedKustomizationFileNames() { + th.WriteF(filepath.Join(".", n), ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`) + } + err := th.RunWithErr(".", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected an error") + } + if !strings.Contains(err.Error(), "Found multiple kustomization files") { + t.Fatalf("unexpected error: %q", err) + } +} + +func TestBaseMustHaveKustomizationFile(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- base +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +spec: + selector: + backend: bungie + ports: + - port: 7002 +`) + err := th.RunWithErr(".", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected an error") + } + if !strings.Contains(err.Error(), "accumulating resources") { + t.Fatalf("unexpected error: %q", err) + } +} + +func TestResourceNotFound(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- deployment.yaml +`) + err := th.RunWithErr(".", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected an error") + } + if !strings.Contains(err.Error(), "accumulating resources") { + t.Fatalf("unexpected error: %q", err) + } +} + +func TestResourceHasAnchor(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- ingress.yaml +`) + th.WriteF("ingress.yaml", ` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: blog +spec: + tls: + - hosts: + - xyz.me + - www.xyz.me + secretName: cert-tls + rules: + - host: xyz.me + http: &xxx_rules + paths: + - path: / + pathType: Prefix + backend: + service: + name: service + port: + number: 80 + - host: www.xyz.me + http: *xxx_rules +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: blog +spec: + rules: + - host: xyz.me + http: + paths: + - backend: + service: + name: service + port: + number: 80 + path: / + pathType: Prefix + - host: www.xyz.me + http: + paths: + - backend: + service: + name: service + port: + number: 80 + path: / + pathType: Prefix + tls: + - hosts: + - xyz.me + - www.xyz.me + secretName: cert-tls +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/baseandoverlaymedium_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/baseandoverlaymedium_test.go new file mode 100644 index 000000000..cfa1afa48 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/baseandoverlaymedium_test.go @@ -0,0 +1,307 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func writeMediumBase(th kusttest_test.Harness) { + th.WriteK("base", ` +namePrefix: baseprefix- +commonLabels: + foo: bar +commonAnnotations: + baseAnno: This is a base annotation +resources: +- deployment/deployment.yaml +- service/service.yaml +`) + th.WriteF("base/service/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: mungebot-service + labels: + app: mungebot +spec: + ports: + - port: 7002 + selector: + app: mungebot +`) + th.WriteF("base/deployment/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mungebot + labels: + app: mungebot +spec: + replicas: 1 + template: + metadata: + labels: + app: mungebot + spec: + containers: + - name: nginx + image: nginx + env: + - name: foo + value: bar + ports: + - containerPort: 80 +`) +} + +func TestMediumBase(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeMediumBase(th) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: baseprefix-mungebot +spec: + replicas: 1 + selector: + matchLabels: + foo: bar + template: + metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + spec: + containers: + - env: + - name: foo + value: bar + image: nginx + name: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: baseprefix-mungebot-service +spec: + ports: + - port: 7002 + selector: + app: mungebot + foo: bar +`) +} + +func TestMediumOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeMediumBase(th) + th.WriteK("overlay", ` +namePrefix: test-infra- +commonLabels: + app: mungebot + org: kubernetes + repo: test-infra +commonAnnotations: + note: This is a test annotation +resources: +- ../base +patchesStrategicMerge: +- deployment/deployment.yaml +configMapGenerator: +- name: app-env + envs: + - configmap/db.env + - configmap/units.ini + - configmap/food.ini +- name: app-config + files: + - nonsense=configmap/dummy.txt +images: +- name: nginx + newTag: 1.8.0`) + + th.WriteF("overlay/configmap/db.env", ` +DB_USERNAME=admin +DB_PASSWORD=somepw +`) + th.WriteF("overlay/configmap/units.ini", ` +LENGTH=kilometer +ENERGY=electronvolt +`) + th.WriteF("overlay/configmap/food.ini", ` +FRUIT=banana +LEGUME=chickpea +`) + th.WriteF("overlay/configmap/dummy.txt", + `Lorem ipsum dolor sit amet, consectetur +adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. +`) + th.WriteF("overlay/deployment/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mungebot +spec: + replicas: 2 + template: + spec: + containers: + - name: nginx + image: nginx:1.7.9 + env: + - name: FOO + valueFrom: + configMapKeyRef: + name: app-env + key: somekey + - name: busybox + image: busybox + envFrom: + - configMapRef: + name: someConfigMap + - configMapRef: + name: app-env + volumeMounts: + - mountPath: /tmp/env + name: app-env + volumes: + - configMap: + name: app-env + name: app-env +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + baseAnno: This is a base annotation + note: This is a test annotation + labels: + app: mungebot + foo: bar + org: kubernetes + repo: test-infra + name: test-infra-baseprefix-mungebot +spec: + replicas: 2 + selector: + matchLabels: + app: mungebot + foo: bar + org: kubernetes + repo: test-infra + template: + metadata: + annotations: + baseAnno: This is a base annotation + note: This is a test annotation + labels: + app: mungebot + foo: bar + org: kubernetes + repo: test-infra + spec: + containers: + - env: + - name: FOO + valueFrom: + configMapKeyRef: + key: somekey + name: test-infra-app-env-8h5mh7f7ch + - name: foo + value: bar + image: nginx:1.8.0 + name: nginx + ports: + - containerPort: 80 + - envFrom: + - configMapRef: + name: someConfigMap + - configMapRef: + name: test-infra-app-env-8h5mh7f7ch + image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/env + name: app-env + volumes: + - configMap: + name: test-infra-app-env-8h5mh7f7ch + name: app-env +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + baseAnno: This is a base annotation + note: This is a test annotation + labels: + app: mungebot + foo: bar + org: kubernetes + repo: test-infra + name: test-infra-baseprefix-mungebot-service +spec: + ports: + - port: 7002 + selector: + app: mungebot + foo: bar + org: kubernetes + repo: test-infra +--- +apiVersion: v1 +data: + DB_PASSWORD: somepw + DB_USERNAME: admin + ENERGY: electronvolt + FRUIT: banana + LEGUME: chickpea + LENGTH: kilometer +kind: ConfigMap +metadata: + annotations: + note: This is a test annotation + labels: + app: mungebot + org: kubernetes + repo: test-infra + name: test-infra-app-env-8h5mh7f7ch +--- +apiVersion: v1 +data: + nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod + tempor\nincididunt ut labore et dolore magna aliqua. \n" +kind: ConfigMap +metadata: + annotations: + note: This is a test annotation + labels: + app: mungebot + org: kubernetes + repo: test-infra + name: test-infra-app-config-49d6f5h7b5 +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/baseandoverlaysmall_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/baseandoverlaysmall_test.go new file mode 100644 index 000000000..52f217287 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/baseandoverlaysmall_test.go @@ -0,0 +1,498 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "strings" + "testing" + + . "sigs.k8s.io/kustomize/api/krusty" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/api/types" +) + +func TestOrderPreserved(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +namePrefix: b- +resources: +- namespace.yaml +- role.yaml +- service.yaml +- deployment.yaml +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +`) + th.WriteF("base/namespace.yaml", ` +apiVersion: v1 +kind: Namespace +metadata: + name: myNs +`) + th.WriteF("base/role.yaml", ` +apiVersion: v1 +kind: Role +metadata: + name: myRole +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: myDep +`) + th.WriteK("prod", ` +namePrefix: p- +resources: +- ../base +- service.yaml +- namespace.yaml +`) + th.WriteF("prod/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService2 +`) + th.WriteF("prod/namespace.yaml", ` +apiVersion: v1 +kind: Namespace +metadata: + name: myNs2 +`) + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Namespace +metadata: + name: myNs +--- +apiVersion: v1 +kind: Role +metadata: + name: p-b-myRole +--- +apiVersion: v1 +kind: Service +metadata: + name: p-b-myService +--- +apiVersion: v1 +kind: Deployment +metadata: + name: p-b-myDep +--- +apiVersion: v1 +kind: Service +metadata: + name: p-myService2 +--- +apiVersion: v1 +kind: Namespace +metadata: + name: myNs2 +`) +} + +func TestBaseInResourceList(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("prod", ` +namePrefix: b- +resources: +- ../base +`) + th.WriteK("base", ` +namePrefix: a- +resources: +- service.yaml +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +spec: + selector: + backend: bungie +`) + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + name: b-a-myService +spec: + selector: + backend: bungie +`) +} + +func TestTinyOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +namePrefix: a- +resources: +- deployment.yaml +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + spec: + containers: + - image: whatever +`) + th.WriteK("overlay", ` +namePrefix: b- +resources: +- ../base +patchesStrategicMerge: +- depPatch.yaml +`) + th.WriteF("overlay/depPatch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 999 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-a-myDeployment +spec: + replicas: 999 + template: + spec: + containers: + - image: whatever +`) +} + +func writeSmallBase(th kusttest_test.Harness) { + th.WriteK("base", ` +namePrefix: a- +commonLabels: + app: myApp +resources: +- deployment.yaml +- service.yaml +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +spec: + selector: + backend: bungie + ports: + - port: 7002 +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + metadata: + labels: + backend: awesome + spec: + containers: + - name: whatever + image: whatever +`) +} + +func TestSmallBase(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeSmallBase(th) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: myApp + name: a-myDeployment +spec: + selector: + matchLabels: + app: myApp + template: + metadata: + labels: + app: myApp + backend: awesome + spec: + containers: + - image: whatever + name: whatever +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myApp + name: a-myService +spec: + ports: + - port: 7002 + selector: + app: myApp + backend: bungie +`) +} + +func TestSmallOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeSmallBase(th) + th.WriteK("overlay", ` +namePrefix: b- +commonLabels: + env: prod + quotedFruit: "peach" + quotedBoolean: "true" +resources: +- ../base +patchesStrategicMerge: +- deployment/deployment.yaml +images: +- name: whatever + newTag: 1.8.0 +`) + + th.WriteF("overlay/configmap/app.env", ` +DB_USERNAME=admin +DB_PASSWORD=somepw +`) + th.WriteF("overlay/configmap/app-init.ini", ` +FOO=bar +BAR=baz +`) + th.WriteF("overlay/deployment/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 1000 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: myApp + env: prod + quotedBoolean: "true" + quotedFruit: peach + name: b-a-myDeployment +spec: + replicas: 1000 + selector: + matchLabels: + app: myApp + env: prod + quotedBoolean: "true" + quotedFruit: peach + template: + metadata: + labels: + app: myApp + backend: awesome + env: prod + quotedBoolean: "true" + quotedFruit: peach + spec: + containers: + - image: whatever:1.8.0 + name: whatever +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myApp + env: prod + quotedBoolean: "true" + quotedFruit: peach + name: b-a-myService +spec: + ports: + - port: 7002 + selector: + app: myApp + backend: bungie + env: prod + quotedBoolean: "true" + quotedFruit: peach +`) +} + +func TestSharedPatchDisAllowed(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeSmallBase(th) + th.WriteK("overlay", ` +commonLabels: + env: prod +resources: +- ../base +patchesStrategicMerge: +- ../shared/deployment-patch.yaml +`) + th.WriteF("shared/deployment-patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 1000 +`) + err := th.RunWithErr("overlay", func() Options { + o := th.MakeDefaultOptions() + o.LoadRestrictions = types.LoadRestrictionsRootOnly + return o + }()) + if !strings.Contains( + err.Error(), + "security; file '/shared/deployment-patch.yaml' is not in or below '/overlay'") { + t.Fatalf("unexpected error: %s", err) + } +} + +func TestSharedPatchAllowed(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeSmallBase(th) + th.WriteK("overlay", ` +commonLabels: + env: prod +resources: +- ../base +patchesStrategicMerge: +- ../shared/deployment-patch.yaml +`) + th.WriteF("shared/deployment-patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + replicas: 1000 +`) + m := th.Run("overlay", func() Options { + o := th.MakeDefaultOptions() + o.LoadRestrictions = types.LoadRestrictionsNone + return o + }()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: myApp + env: prod + name: a-myDeployment +spec: + replicas: 1000 + selector: + matchLabels: + app: myApp + env: prod + template: + metadata: + labels: + app: myApp + backend: awesome + env: prod + spec: + containers: + - image: whatever + name: whatever +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myApp + env: prod + name: a-myService +spec: + ports: + - port: 7002 + selector: + app: myApp + backend: bungie + env: prod +`) +} + +func TestSmallOverlayJSONPatch(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeSmallBase(th) + th.WriteK("overlay", ` +resources: +- ../base +patchesJson6902: +- target: + version: v1 + kind: Service + name: a-myService + path: service-patch.yaml +`) + + th.WriteF("overlay/service-patch.yaml", ` +- op: add + path: /spec/selector/backend + value: beagle +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: myApp + name: a-myDeployment +spec: + selector: + matchLabels: + app: myApp + template: + metadata: + labels: + app: myApp + backend: awesome + spec: + containers: + - image: whatever + name: whatever +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: myApp + name: a-myService +spec: + ports: + - port: 7002 + selector: + app: myApp + backend: beagle +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/basereusenameprefix_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/basereusenameprefix_test.go new file mode 100644 index 000000000..fb7a8e8e0 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/basereusenameprefix_test.go @@ -0,0 +1,196 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// Here is a structure of a kustomization of two components, component1 +// and component2, that both use a shared postgres definition, which +// they would individually adjust. This test case checks that the name +// prefix does not cause a name reference conflict. +// +// root +// / \ +// component1/overlay component2/overlay +// | | +// component1/base component2/base +// \ / +// base +// +// This is the directory layout: +// +// ├── component1 +// │ ├── base +// │ │ └── kustomization.yaml +// │ └── overlay +// │ └── kustomization.yaml +// ├── component2 +// │ ├── base +// │ │ └── kustomization.yaml +// │ └── overlay +// │ └── kustomization.yaml +// ├── shared +// │ ├── kustomization.yaml +// │ └── resources.yaml +// ├── kustomization.yaml + +func TestBaseReuseNameConflict(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("component1/base", ` +resources: + - ../../shared + +namePrefix: component1- +`) + th.WriteK("component1/overlay", ` +resources: + - ../base + +namePrefix: overlay- +`) + + th.WriteK("component2/base", ` +resources: + - ../../shared + +namePrefix: component2- +`) + th.WriteK("component2/overlay", ` +resources: + - ../base + +namePrefix: overlay- +`) + + th.WriteK("shared", ` +resources: + - resources.yaml +`) + th.WriteF("shared/resources.yaml", ` +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: postgres +spec: + resources: + requests: + storage: 1Gi + accessModes: + - ReadWriteOnce +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + selector: + matchLabels: {} + strategy: + type: Recreate + template: + spec: + containers: + - name: postgres + image: postgres + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /var/lib/postgresql + name: data + ports: + - name: postgres + containerPort: 5432 + volumes: + - name: data + persistentVolumeClaim: + claimName: postgres +`) + + th.WriteK(".", ` +resources: + - component1/overlay + - component2/overlay +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: overlay-component1-postgres +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: overlay-component1-postgres +spec: + selector: + matchLabels: {} + strategy: + type: Recreate + template: + spec: + containers: + - image: postgres + imagePullPolicy: IfNotPresent + name: postgres + ports: + - containerPort: 5432 + name: postgres + volumeMounts: + - mountPath: /var/lib/postgresql + name: data + volumes: + - name: data + persistentVolumeClaim: + claimName: overlay-component1-postgres +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: overlay-component2-postgres +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: overlay-component2-postgres +spec: + selector: + matchLabels: {} + strategy: + type: Recreate + template: + spec: + containers: + - image: postgres + imagePullPolicy: IfNotPresent + name: postgres + ports: + - containerPort: 5432 + name: postgres + volumeMounts: + - mountPath: /var/lib/postgresql + name: data + volumes: + - name: data + persistentVolumeClaim: + claimName: overlay-component2-postgres +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/basic_io_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/basic_io_test.go new file mode 100644 index 000000000..6a2b9c302 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/basic_io_test.go @@ -0,0 +1,82 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestBasicIO_1(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- service.yaml +`) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + annotations: + port: 8080 + happy: true + color: green + name: demo +spec: + clusterIP: None +`) + m := th.Run(".", th.MakeDefaultOptions()) + // The annotations are sorted by key, hence the order change. + // Quotes are added intentionally. + th.AssertActualEqualsExpected( + m, ` +apiVersion: v1 +kind: Service +metadata: + annotations: + color: green + happy: "true" + port: "8080" + name: demo +spec: + clusterIP: None +`) +} + +func TestBasicIO_2(t *testing.T) { + th := kusttest_test.MakeHarness(t) + opts := th.MakeDefaultOptions() + th.WriteK(".", ` +resources: +- service.yaml +`) + // All the annotation values are quoted in the input. + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + annotations: + port: "8080" + happy: "true" + color: green + name: demo +spec: + clusterIP: None +`) + m := th.Run(".", opts) + // The annotations are sorted by key, hence the order change. + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + annotations: + color: green + happy: "true" + port: "8080" + name: demo +spec: + clusterIP: None +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/blankvalues_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/blankvalues_test.go new file mode 100644 index 000000000..dfba4f365 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/blankvalues_test.go @@ -0,0 +1,49 @@ +package krusty_test + +import ( + "strings" + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// test for https://github.com/kubernetes-sigs/kustomize/issues/4240 +func TestBlankNamespace4240(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- resource.yaml +`) + + th.WriteF("resource.yaml", ` +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: test +rules: [] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: test +subjects: +- kind: ServiceAccount + name: test + namespace: +roleRef: + kind: Role + name: test + apiGroup: rbac.authorization.k8s.io +`) + + err := th.RunWithErr(".", th.MakeDefaultOptions()) + if !strings.Contains(err.Error(), "Invalid Input: namespace is blank for resource") { + t.Fatalf("unexpected error: %q", err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/chartinflatorplugin_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/chartinflatorplugin_test.go new file mode 100644 index 000000000..606c494f4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/chartinflatorplugin_test.go @@ -0,0 +1,105 @@ +// +build notravis + +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Disabled on travis, because don't want to install helm on travis. + +package krusty_test + +import ( + "regexp" + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// This is an example of using a helm chart as a base, +// inflating it and then customizing it with a nameprefix +// applied to all its resources. +// +// The helm chart used is downloaded from +// https://github.com/helm/charts/tree/master/stable/minecraft +// with each test run, so it's a bit brittle as that +// chart could change obviously and break the test. +// +// This test requires having the helm binary on the PATH. +// +// TODO: Download and inflate the chart, and check that +// in for the test. +func TestChartInflatorPlugin(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + PrepExecPlugin("someteam.example.com", "v1", "ChartInflator") + defer th.Reset() + + th.WriteK(".", ` +generators: +- chartInflator.yaml +namePrefix: LOOOOOOOONG- +`) + + th.WriteF("./chartInflator.yaml", ` +apiVersion: someteam.example.com/v1 +kind: ChartInflator +metadata: + name: notImportantHere +chartName: minecraft +`) + + m := th.Run(".", th.MakeOptionsPluginsEnabled()) + chartName := regexp.MustCompile("chart: minecraft-[0-9.]+") + th.AssertActualEqualsExpectedWithTweak(m, + func(x []byte) []byte { + return chartName.ReplaceAll(x, []byte("chart: minecraft-SOMEVERSION")) + }, ` +apiVersion: v1 +data: + rcon-password: Q0hBTkdFTUUh +kind: Secret +metadata: + labels: + app: release-name-minecraft + chart: minecraft-SOMEVERSION + heritage: Tiller + release: release-name + name: LOOOOOOOONG-release-name-minecraft +type: Opaque +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + volume.alpha.kubernetes.io/storage-class: default + labels: + app: release-name-minecraft + chart: minecraft-SOMEVERSION + heritage: Tiller + release: release-name + name: LOOOOOOOONG-release-name-minecraft-datadir +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: release-name-minecraft + chart: minecraft-SOMEVERSION + heritage: Tiller + release: release-name + name: LOOOOOOOONG-release-name-minecraft +spec: + ports: + - name: minecraft + port: 25565 + protocol: TCP + targetPort: minecraft + selector: + app: release-name-minecraft + type: LoadBalancer +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/complexcomposition_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/complexcomposition_test.go new file mode 100644 index 000000000..7c3dfc39f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/complexcomposition_test.go @@ -0,0 +1,556 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "strings" + "testing" + + . "sigs.k8s.io/kustomize/api/krusty" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/api/types" +) + +const httpsService = ` +apiVersion: v1 +kind: Service +metadata: + name: my-https-svc +spec: + ports: + - port: 443 + protocol: TCP + name: https + selector: + app: my-app +` + +func writeStatefulSetBase(th kusttest_test.Harness) { + th.WriteK("base", ` +resources: +- statefulset.yaml +`) + th.WriteF("base/statefulset.yaml", ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: my-sts +spec: + serviceName: my-svc + selector: + matchLabels: + app: my-app + template: + metadata: + labels: + app: my-app + spec: + containers: + - name: app + image: my-image + volumeClaimTemplates: + - spec: + storageClassName: default +`) +} + +func writeHTTPSOverlay(th kusttest_test.Harness) { + th.WriteK("https", ` +resources: +- ../base +- https-svc.yaml +patchesStrategicMerge: +- sts-patch.yaml +`) + th.WriteF("https/https-svc.yaml", httpsService) + th.WriteF("https/sts-patch.yaml", ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: my-sts +spec: + serviceName: my-https-svc +`) +} + +func writeHTTPSTransformerRaw(th kusttest_test.Harness) { + th.WriteF("https/service/https-svc.yaml", httpsService) + th.WriteF("https/transformer/transformer.yaml", ` +apiVersion: builtin +kind: PatchTransformer +metadata: + name: svcNameTran +target: + group: apps + version: v1 + kind: StatefulSet + name: my-sts +patch: |- + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: my-sts + spec: + serviceName: my-https-svc +`) +} + +func writeHTTPSTransformerBase(th kusttest_test.Harness) { + th.WriteK("https/service", ` +resources: +- https-svc.yaml +`) + th.WriteK("https/transformer", ` +resources: +- transformer.yaml +`) + writeHTTPSTransformerRaw(th) +} + +func writeConfigFromEnvOverlay(th kusttest_test.Harness) { + th.WriteK("config", ` +resources: +- ../base +configMapGenerator: +- name: my-config + literals: + - MY_ENV=foo +generatorOptions: + disableNameSuffixHash: true +patchesStrategicMerge: +- sts-patch.yaml +`) + th.WriteF("config/sts-patch.yaml", ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: my-sts +spec: + template: + spec: + containers: + - name: app + envFrom: + - configMapRef: + name: my-config +`) +} + +func writeConfigFromEnvTransformerRaw(th kusttest_test.Harness) { + th.WriteF("config/map/generator.yaml", ` +apiVersion: builtin +kind: ConfigMapGenerator +metadata: + name: my-config +options: + disableNameSuffixHash: true +literals: +- MY_ENV=foo +`) + th.WriteF("config/transformer/transformer.yaml", ` +apiVersion: builtin +kind: PatchTransformer +metadata: + name: envFromConfigTrans +target: + group: apps + version: v1 + kind: StatefulSet + name: my-sts +patch: |- + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: my-sts + spec: + template: + spec: + containers: + - name: app + envFrom: + - configMapRef: + name: my-config +`) +} +func writeConfigFromEnvTransformerBase(th kusttest_test.Harness) { + th.WriteK("config/map", ` +resources: +- generator.yaml +`) + th.WriteK("config/transformer", ` +resources: +- transformer.yaml +`) + writeConfigFromEnvTransformerRaw(th) +} + +func writeTolerationsOverlay(th kusttest_test.Harness) { + th.WriteK("tolerations", ` +resources: +- ../base +patchesStrategicMerge: +- sts-patch.yaml +`) + th.WriteF("tolerations/sts-patch.yaml", ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: my-sts +spec: + template: + spec: + tolerations: + - effect: NoExecute + key: node.kubernetes.io/not-ready + tolerationSeconds: 30 +`) +} + +func writeTolerationsTransformerRaw(th kusttest_test.Harness) { + th.WriteF("tolerations/transformer.yaml", ` +apiVersion: builtin +kind: PatchTransformer +metadata: + name: tolTrans +target: + group: apps + version: v1 + kind: StatefulSet + name: my-sts +patch: |- + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: my-sts + spec: + template: + spec: + tolerations: + - effect: NoExecute + key: node.kubernetes.io/not-ready + tolerationSeconds: 30 +`) +} + +func writeTolerationsTransformerBase(th kusttest_test.Harness) { + th.WriteK("tolerations", ` +resources: +- transformer.yaml +`) + writeTolerationsTransformerRaw(th) +} + +func writeStorageOverlay(th kusttest_test.Harness) { + th.WriteK("storage", ` +resources: +- ../base +patchesJson6902: +- target: + group: apps + version: v1 + kind: StatefulSet + name: my-sts + path: sts-patch.json +`) + th.WriteF("storage/sts-patch.json", ` +[{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}] +`) +} + +func writeStorageTransformerRaw(th kusttest_test.Harness) { + th.WriteF("storage/transformer.yaml", ` +apiVersion: builtin +kind: PatchTransformer +metadata: + name: storageTrans +target: + group: apps + version: v1 + kind: StatefulSet + name: my-sts +patch: |- + [{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}] +`) +} + +func writeStorageTransformerBase(th kusttest_test.Harness) { + th.WriteK("storage", ` +resources: +- transformer.yaml +`) + writeStorageTransformerRaw(th) +} + +func writePatchingOverlays(th kusttest_test.Harness) { + writeStorageOverlay(th) + writeConfigFromEnvOverlay(th) + writeTolerationsOverlay(th) + writeHTTPSOverlay(th) +} + +func writePatchingTransformersRaw(th kusttest_test.Harness) { + writeStorageTransformerRaw(th) + writeConfigFromEnvTransformerRaw(th) + writeTolerationsTransformerRaw(th) + writeHTTPSTransformerRaw(th) +} + +// Similar to writePatchingTransformersRaw, except here the +// transformers and related artifacts are addressable as _bases_. +// They are listed in a kustomization file, and consumers of +// the plugin refer to the kustomization instead of to the local +// file in the "transformers:" field. +// +// Using bases makes the set of files relocatable with +// respect to the overlays, and avoids the need to relax load +// restrictions on file paths reaching outside the `dev` and +// `prod` kustomization roots. I.e. with bases tests can use +// NewKustTestHarness instead of NewKustTestHarnessNoLoadRestrictor. +// +// Using transformer plugins from _bases_ means the plugin config +// must be self-contained, i.e. the config may not have fields that +// refer to local files, since those files won't be present when +// the plugin is instantiated and used. +func writePatchingTransformerBases(th kusttest_test.Harness) { + writeStorageTransformerBase(th) + writeConfigFromEnvTransformerBase(th) + writeTolerationsTransformerBase(th) + writeHTTPSTransformerBase(th) +} + +// Here's a complex kustomization scenario that combines multiple overlays +// on a common base: +// +// dev prod +// | | +// | | +// + ------- + + ------------ + ------------- + +// | | | | | +// | | | | | +// v | v v v +// storage + -----> config tolerations https +// | | | | +// | | | | +// | + --- + + --- + | +// | | | | +// | v v | +// + -----------------------> base <------------------ + +// +// The base resource is a statefulset. Each intermediate overlay manages or +// generates new resources and patches different aspects of the same base +// resource, without using any of the `namePrefix`, `nameSuffix` or `namespace` +// kustomization keywords. +// +// Intermediate overlays: +// - storage: Changes the storage class of the stateful set with a JSON patch. +// - config: Generates a config map and adds a field as an environment +// variable. +// - tolerations: Adds a new tolerations field in the spec. +// - https: Adds a new service resource and changes the service name in the +// stateful set. +// +// Top overlays: +// - dev: Combines the storage and config intermediate overlays. +// - prod: Combines the config, tolerations and https intermediate overlays. + +func TestComplexComposition_Dev_Failure(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeStatefulSetBase(th) + writePatchingOverlays(th) + th.WriteK("dev", ` +resources: +- ../storage +- ../config +`) + err := th.RunWithErr("dev", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("Expected resource accumulation error") + } + if !strings.Contains( + err.Error(), "already registered id: StatefulSet.v1.apps/my-sts.[noNs]") { + t.Fatalf("Unexpected err: %v", err) + } +} + +const devDesiredResult = ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: my-sts +spec: + selector: + matchLabels: + app: my-app + serviceName: my-svc + template: + metadata: + labels: + app: my-app + spec: + containers: + - envFrom: + - configMapRef: + name: my-config + image: my-image + name: app + volumeClaimTemplates: + - spec: + storageClassName: my-sc +--- +apiVersion: v1 +data: + MY_ENV: foo +kind: ConfigMap +metadata: + name: my-config +` + +func TestComplexComposition_Dev_SuccessWithRawTransformers(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeStatefulSetBase(th) + writePatchingTransformersRaw(th) + th.WriteK("dev", ` +resources: +- ../base +generators: +- ../config/map/generator.yaml +transformers: +- ../config/transformer/transformer.yaml +- ../storage/transformer.yaml +`) + m := th.Run("dev", func() Options { + o := th.MakeDefaultOptions() + o.LoadRestrictions = types.LoadRestrictionsNone + return o + }()) + th.AssertActualEqualsExpected(m, devDesiredResult) +} + +func TestComplexComposition_Dev_SuccessWithBaseTransformers(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeStatefulSetBase(th) + writePatchingTransformerBases(th) + th.WriteK("dev", ` +resources: +- ../base +generators: +- ../config/map +transformers: +- ../config/transformer +- ../storage +`) + m := th.Run("dev", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, devDesiredResult) +} + +func TestComplexComposition_Prod_Failure(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeStatefulSetBase(th) + writePatchingOverlays(th) + th.WriteK("prod", ` +resources: +- ../config +- ../tolerations +- ../https +`) + err := th.RunWithErr("prod", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("Expected resource accumulation error") + } + if !strings.Contains( + err.Error(), "already registered id: StatefulSet.v1.apps/my-sts.[noNs]") { + t.Fatalf("Unexpected err: %v", err) + } +} + +const prodDesiredResult = ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: my-sts +spec: + selector: + matchLabels: + app: my-app + serviceName: my-https-svc + template: + metadata: + labels: + app: my-app + spec: + containers: + - envFrom: + - configMapRef: + name: my-config + image: my-image + name: app + tolerations: + - effect: NoExecute + key: node.kubernetes.io/not-ready + tolerationSeconds: 30 + volumeClaimTemplates: + - spec: + storageClassName: default +--- +apiVersion: v1 +kind: Service +metadata: + name: my-https-svc +spec: + ports: + - name: https + port: 443 + protocol: TCP + selector: + app: my-app +--- +apiVersion: v1 +data: + MY_ENV: foo +kind: ConfigMap +metadata: + name: my-config +` + +func TestComplexComposition_Prod_SuccessWithRawTransformers(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeStatefulSetBase(th) + writePatchingTransformersRaw(th) + th.WriteK("prod", ` +resources: +- ../base +- ../https/service/https-svc.yaml +generators: +- ../config/map/generator.yaml +transformers: +- ../config/transformer/transformer.yaml +- ../https/transformer/transformer.yaml +- ../tolerations/transformer.yaml +`) + m := th.Run("prod", func() Options { + o := th.MakeDefaultOptions() + o.LoadRestrictions = types.LoadRestrictionsNone + return o + }()) + th.AssertActualEqualsExpected(m, prodDesiredResult) +} + +func TestComplexComposition_Prod_SuccessWithBaseTransformers(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeStatefulSetBase(th) + writePatchingTransformerBases(th) + th.WriteK("prod", ` +resources: +- ../base +- ../https/service +generators: +- ../config/map +transformers: +- ../config/transformer +- ../https/transformer +- ../tolerations +`) + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, prodDesiredResult) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/component_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/component_test.go new file mode 100644 index 000000000..a76a80284 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/component_test.go @@ -0,0 +1,658 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "fmt" + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/konfig" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +type FileGen func(kusttest_test.Harness) + +func writeC(path string, content string) FileGen { + return func(th kusttest_test.Harness) { + th.WriteC(path, content) + } +} + +func writeF(path string, content string) FileGen { + return func(th kusttest_test.Harness) { + th.WriteF(path, content) + } +} + +func writeK(path string, content string) FileGen { + return func(th kusttest_test.Harness) { + th.WriteK(path, content) + } +} + +func writeTestBase(th kusttest_test.Harness) { + th.WriteK("base", ` +resources: +- deploy.yaml +configMapGenerator: +- name: my-configmap + literals: + - testValue=purple + - otherValue=green +`) + th.WriteF("base/deploy.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + replicas: 1 +`) +} + +func writeTestComponent(th kusttest_test.Harness) { + th.WriteC("comp", ` +namePrefix: comp- +replicas: +- name: storefront + count: 3 +resources: +- stub.yaml +configMapGenerator: +- name: my-configmap + behavior: merge + literals: + - testValue=blue + - compValue=red +`) + th.WriteF("comp/stub.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: stub +spec: + replicas: 1 +`) +} + +func writeOverlayProd(th kusttest_test.Harness) { + th.WriteK("prod", ` +resources: +- ../base +- db + +components: +- ../comp +`) + writeDB(th) +} + +func writeDB(th kusttest_test.Harness) { + deployment("db", "prod/db")(th) +} + +func deployment(name string, path string) FileGen { + return writeF(path, fmt.Sprintf(` +apiVersion: v1 +kind: Deployment +metadata: + name: %s +spec: + type: Logical +`, name)) +} + +func TestComponent(t *testing.T) { + testCases := map[string]struct { + input []FileGen + runPath string + expectedOutput string + }{ + // Components are inserted into the resource hierarchy as the parent of those + // resources that come before it in the resources list of the parent Kustomization. + "basic-component": { + input: []FileGen{writeTestBase, writeTestComponent, writeOverlayProd}, + runPath: "prod", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: comp-storefront +spec: + replicas: 3 +--- +apiVersion: v1 +data: + compValue: red + otherValue: green + testValue: blue +kind: ConfigMap +metadata: + name: comp-my-configmap-97647ckcmg +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-db +spec: + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-stub +spec: + replicas: 1 +`, + }, + "multiple-components": { + input: []FileGen{writeTestBase, writeTestComponent, writeDB, + writeC("additionalcomp", ` +configMapGenerator: +- name: my-configmap + behavior: merge + literals: + - otherValue=orange +`), + writeK("prod", ` +resources: +- ../base +- db + +components: +- ../comp +- ../additionalcomp +`), + }, + runPath: "prod", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: comp-storefront +spec: + replicas: 3 +--- +apiVersion: v1 +data: + compValue: red + otherValue: orange + testValue: blue +kind: ConfigMap +metadata: + name: comp-my-configmap-g486mb229k +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-db +spec: + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-stub +spec: + replicas: 1 +`, + }, + "nested-components": { + input: []FileGen{writeTestBase, writeTestComponent, writeDB, + writeC("additionalcomp", ` +components: +- ../comp +configMapGenerator: +- name: my-configmap + behavior: merge + literals: + - otherValue=orange +`), + writeK("prod", ` +resources: +- ../base +- db + +components: +- ../additionalcomp +`), + }, + runPath: "prod", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: comp-storefront +spec: + replicas: 3 +--- +apiVersion: v1 +data: + compValue: red + otherValue: orange + testValue: blue +kind: ConfigMap +metadata: + name: comp-my-configmap-g486mb229k +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-db +spec: + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-stub +spec: + replicas: 1 +`, + }, + // If a component sets a name prefix on a base, then that base can also be separately included + // without being affected by the component in another branch of the resource tree + "basic-component-with-repeated-base": { + input: []FileGen{writeTestBase, writeTestComponent, writeOverlayProd, + writeK("repeated", ` +resources: +- ../base +- ../prod +`), + }, + runPath: "repeated", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + replicas: 1 +--- +apiVersion: v1 +data: + otherValue: green + testValue: purple +kind: ConfigMap +metadata: + name: my-configmap-9cd648hm8f +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-storefront +spec: + replicas: 3 +--- +apiVersion: v1 +data: + compValue: red + otherValue: green + testValue: blue +kind: ConfigMap +metadata: + name: comp-my-configmap-97647ckcmg +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-db +spec: + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: comp-stub +spec: + replicas: 1 +`, + }, + "applying-component-directly-should-be-same-as-kustomization": { + input: []FileGen{writeTestBase, writeTestComponent, + writeC("direct-component", ` +resources: +- ../base +configMapGenerator: +- name: my-configmap + behavior: merge + literals: + - compValue=red + - testValue=blue +`), + }, + runPath: "direct-component", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + replicas: 1 +--- +apiVersion: v1 +data: + compValue: red + otherValue: green + testValue: blue +kind: ConfigMap +metadata: + name: my-configmap-97647ckcmg +`, + }, + "missing-optional-component-api-version": { + input: []FileGen{writeTestBase, writeOverlayProd, + writeF("comp/"+konfig.DefaultKustomizationFileName(), ` +kind: Component +configMapGenerator: +- name: my-configmap + behavior: merge + literals: + - otherValue=orange +`), + }, + runPath: "prod", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + replicas: 1 +--- +apiVersion: v1 +data: + otherValue: orange + testValue: purple +kind: ConfigMap +metadata: + name: my-configmap-6hhdg8gkdg +--- +apiVersion: v1 +kind: Deployment +metadata: + name: db +spec: + type: Logical +`, + }, + // See how nameSuffix "-b" is also added to the resources included by "comp-a" because they are in the + // accumulator when "comp-b" is accumulated. In practice we could use simple Kustomizations for this example. + "components-can-add-the-same-base-if-the-first-renames-resources": { + input: []FileGen{writeTestBase, + deployment("proxy", "comp-a/proxy.yaml"), + writeC("comp-a", ` +resources: +- ../base + +nameSuffix: "-a" +`), + writeC("comp-b", ` +resources: +- ../base + +nameSuffix: "-b" +`), + writeK("prod", ` +components: +- ../comp-a +- ../comp-b`), + }, + runPath: "prod", + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront-a-b +spec: + replicas: 1 +--- +apiVersion: v1 +data: + otherValue: green + testValue: purple +kind: ConfigMap +metadata: + name: my-configmap-a-b-9cd648hm8f +--- +apiVersion: v1 +kind: Deployment +metadata: + name: storefront-b +spec: + replicas: 1 +--- +apiVersion: v1 +data: + otherValue: green + testValue: purple +kind: ConfigMap +metadata: + name: my-configmap-b-9cd648hm8f +`, + }, + + "multiple-bases-can-add-the-same-component-if-it-doesn-not-define-named-entities": { + input: []FileGen{ + writeC("comp", ` +namespace: prod +`), + writeK("base-a", ` +resources: +- proxy.yaml + +components: +- ../comp +`), + deployment("proxy-a", "base-a/proxy.yaml"), + writeK("base-b", ` +resources: +- proxy.yaml + +components: +- ../comp +`), + deployment("proxy-b", "base-b/proxy.yaml"), + writeK("prod", ` +resources: +- proxy.yaml +- ../base-a +- ../base-b +`), + deployment("proxy-prod", "prod/proxy.yaml"), + }, + runPath: "prod", + // Note that the namepsace has not been applied to proxy-prod because it was not in scope when the + // component was applied + expectedOutput: ` +apiVersion: v1 +kind: Deployment +metadata: + name: proxy-prod +spec: + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: proxy-a + namespace: prod +spec: + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: proxy-b + namespace: prod +spec: + type: Logical +`, + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + th := kusttest_test.MakeHarness(t) + for _, f := range tc.input { + f(th) + } + m := th.Run(tc.runPath, th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, tc.expectedOutput) + }) + } +} + +func TestComponentErrors(t *testing.T) { + testCases := map[string]struct { + input []FileGen + runPath string + expectedError string + }{ + "components-cannot-be-added-to-resources": { + input: []FileGen{writeTestBase, writeTestComponent, + writeK("compinres", ` +resources: +- ../base +- ../comp +`), + }, + runPath: "compinres", + expectedError: "expected kind != 'Component' for path '/comp'", + }, + "kustomizations-cannot-be-added-to-components": { + input: []FileGen{writeTestBase, writeTestComponent, + writeK("kustincomponents", ` +components: +- ../base +- ../comp +`), + }, + runPath: "kustincomponents", + expectedError: "accumulating components: accumulateDirectory: \"expected kind 'Component' for path " + + "'/base' but got 'Kustomization'", + }, + "files-cannot-be-added-to-components-list": { + input: []FileGen{writeTestBase, + writeF("filesincomponents/stub.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: stub +spec: + replicas: 1 +`), + writeK("filesincomponents", ` +components: +- stub.yaml +- ../comp +`), + }, + runPath: "filesincomponents", + expectedError: "'/filesincomponents/stub.yaml' must be a directory to be a root", + }, + "invalid-component-api-version": { + input: []FileGen{writeTestBase, writeOverlayProd, + writeF("comp/"+konfig.DefaultKustomizationFileName(), ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Component +configMapGenerator: +- name: my-configmap + behavior: merge + literals: + - otherValue=orange +`), + }, + runPath: "prod", + expectedError: "apiVersion for Component should be kustomize.config.k8s.io/v1alpha1", + }, + "components-cannot-add-the-same-resource": { + input: []FileGen{writeTestBase, + writeC("comp-a", ` +resources: +- proxy.yaml +`), + deployment("proxy", "comp-a/proxy.yaml"), + writeC("comp-b", ` +resources: +- proxy.yaml +`), + deployment("proxy", "comp-b/proxy.yaml"), + writeK("prod", ` +resources: +- ../base + +components: +- ../comp-a +- ../comp-b`), + }, + runPath: "prod", + expectedError: "may not add resource with an already registered id: Deployment.v1.[noGrp]/proxy.[noNs]", + }, + "components-cannot-add-the-same-base": { + input: []FileGen{writeTestBase, + deployment("proxy", "comp-a/proxy.yaml"), + writeC("comp-a", ` +resources: +- ../base +`), + writeC("comp-b", ` +resources: +- ../base +`), + writeK("prod", ` +components: +- ../comp-a +- ../comp-b`), + }, + runPath: "prod", + expectedError: "may not add resource with an already registered id: Deployment.v1.[noGrp]/storefront.[noNs]", + }, + "components-cannot-add-bases-containing-the-same-resource": { + input: []FileGen{writeTestBase, + writeC("comp-a", ` +resources: +- ../base-a +`), + writeK("base-a", ` +resources: +- proxy.yaml +`), + deployment("proxy", "base-a/proxy.yaml"), + writeC("comp-b", ` +resources: +- ../base-b +`), + writeK("base-b", ` +resources: +- proxy.yaml +`), + deployment("proxy", "base-b/proxy.yaml"), + writeK("prod", ` +resources: +- ../base + +components: +- ../comp-a +- ../comp-b`), + }, + runPath: "prod", + expectedError: "may not add resource with an already registered id: Deployment.v1.[noGrp]/proxy.[noNs]", + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + th := kusttest_test.MakeHarness(t) + for _, f := range tc.input { + f(th) + } + err := th.RunWithErr(tc.runPath, th.MakeDefaultOptions()) + if err == nil || !strings.Contains(err.Error(), tc.expectedError) { + t.Fatalf("unexpected error: %s", err) + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/configmaps_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/configmaps_test.go new file mode 100644 index 000000000..721979983 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/configmaps_test.go @@ -0,0 +1,574 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// Numbers and booleans are quoted +func TestGeneratorIntVsStringNoMerge(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- service.yaml +configMapGenerator: +- name: bob + literals: + - fruit=Indian Gooseberry + - year=2020 + - crisis=true +`) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: demo +spec: + clusterIP: None +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected( + m, ` +apiVersion: v1 +kind: Service +metadata: + name: demo +spec: + clusterIP: None +--- +apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + year: "2020" +kind: ConfigMap +metadata: + name: bob-79t79mt227 +`) +} + +func TestGeneratorIntVsStringWithMerge(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +configMapGenerator: +- name: bob + literals: + - fruit=Indian Gooseberry + - year=2020 + - crisis=true +`) + th.WriteK("overlay", ` +resources: +- ../base +configMapGenerator: +- name: bob + behavior: merge + literals: + - month=12 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, `apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + month: "12" + year: "2020" +kind: ConfigMap +metadata: + name: bob-bk46gm59c6 +`) +} + +func TestGeneratorFromProperties(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +configMapGenerator: + - name: test-configmap + behavior: create + envs: + - properties +`) + th.WriteF("base/properties", ` +VAR1=100 +`) + th.WriteK("overlay", ` +resources: +- ../base +configMapGenerator: +- name: test-configmap + behavior: "merge" + envs: + - properties +`) + th.WriteF("overlay/properties", ` +VAR2=200 +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, `apiVersion: v1 +data: + VAR1: "100" + VAR2: "200" +kind: ConfigMap +metadata: + name: test-configmap-hdghb5ddkg +`) +} + +// Generate a Secret and a ConfigMap from the same data +// to compare the result. +func TestGeneratorBasics(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +namePrefix: blah- +configMapGenerator: +- name: bob + literals: + - fruit=apple + - vegetable=broccoli + envs: + - foo.env + env: bar.env + files: + - passphrase=phrase.dat + - forces.txt +- name: json + literals: + - 'v2=[{"path": "var/druid/segment-cache"}]' + - >- + druid_segmentCache_locations=[{"path": + "var/druid/segment-cache", + "maxSize": 32000000000, + "freeSpacePercent": 1.0}] +secretGenerator: +- name: bob + literals: + - fruit=apple + - vegetable=broccoli + envs: + - foo.env + files: + - passphrase=phrase.dat + - forces.txt + env: bar.env +`) + th.WriteF("foo.env", ` +MOUNTAIN=everest +OCEAN=pacific +`) + th.WriteF("bar.env", ` +BIRD=falcon +`) + th.WriteF("phrase.dat", ` +Life is short. +But the years are long. +Not while the evil days come not. +`) + th.WriteF("forces.txt", ` +gravitational +electromagnetic +strong nuclear +weak nuclear +`) + opts := th.MakeDefaultOptions() + m := th.Run(".", opts) + th.AssertActualEqualsExpected( + m, ` +apiVersion: v1 +data: + BIRD: falcon + MOUNTAIN: everest + OCEAN: pacific + forces.txt: |2 + + gravitational + electromagnetic + strong nuclear + weak nuclear + fruit: apple + passphrase: |2 + + Life is short. + But the years are long. + Not while the evil days come not. + vegetable: broccoli +kind: ConfigMap +metadata: + name: blah-bob-g9df72cd5b +--- +apiVersion: v1 +data: + druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize": + 32000000000, "freeSpacePercent": 1.0}]' + v2: '[{"path": "var/druid/segment-cache"}]' +kind: ConfigMap +metadata: + name: blah-json-5298bc8g99 +--- +apiVersion: v1 +data: + BIRD: ZmFsY29u + MOUNTAIN: ZXZlcmVzdA== + OCEAN: cGFjaWZpYw== + forces.txt: | + CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn + VjbGVhcgo= + fruit: YXBwbGU= + passphrase: | + CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG + UgZXZpbCBkYXlzIGNvbWUgbm90Lgo= + vegetable: YnJvY2NvbGk= +kind: Secret +metadata: + name: blah-bob-58g62h555c +type: Opaque +`) +} + +// TODO: These should be errors instead. +func TestGeneratorRepeatsInKustomization(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +namePrefix: blah- +configMapGenerator: +- name: bob + behavior: create + literals: + - bean=pinto + - star=wolf-rayet + literals: + - fruit=apple + - vegetable=broccoli + files: + - forces.txt + files: + - nobles=nobility.txt +`) + th.WriteF("forces.txt", ` +gravitational +electromagnetic +strong nuclear +weak nuclear +`) + th.WriteF("nobility.txt", ` +helium +neon +argon +krypton +xenon +radon +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + fruit: apple + nobles: |2 + + helium + neon + argon + krypton + xenon + radon + vegetable: broccoli +kind: ConfigMap +metadata: + name: blah-bob-db529cg5bk +`) +} + +func TestIssue3393(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- cm.yaml +configMapGenerator: + - name: project + behavior: merge + literals: + - ANOTHER_ENV_VARIABLE="bar" +`) + th.WriteF("cm.yaml", ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: project +data: + A_FIRST_ENV_VARIABLE: "foo" +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + A_FIRST_ENV_VARIABLE: foo + ANOTHER_ENV_VARIABLE: bar +kind: ConfigMap +metadata: + name: project +`) +} + +func TestGeneratorSimpleOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +namePrefix: p- +configMapGenerator: +- name: cm + behavior: create + literals: + - fruit=apple +`) + th.WriteK("overlay", ` +resources: +- ../base +configMapGenerator: +- name: cm + behavior: merge + literals: + - veggie=broccoli +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + fruit: apple + veggie: broccoli +kind: ConfigMap +metadata: + name: p-cm-877mt5hc89 +`) +} + +var binaryHello = []byte{ + 0xff, // non-utf8 + 0x68, // h + 0x65, // e + 0x6c, // l + 0x6c, // l + 0x6f, // o +} + +func manyHellos(count int) (result []byte) { + for i := 0; i < count; i++ { + result = append(result, binaryHello...) + } + return +} + +func TestGeneratorOverlaysBinaryData(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("base/data.bin", string(manyHellos(30))) + th.WriteK("base", ` +namePrefix: p1- +configMapGenerator: +- name: com1 + behavior: create + files: + - data.bin +`) + th.WriteK("overlay", ` +resources: +- ../base +configMapGenerator: +- name: com1 + behavior: merge +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +binaryData: + data.bin: | + /2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbG + xv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hl + bGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2 + hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv +kind: ConfigMap +metadata: + name: p1-com1-96gmmt6gt5 +`) +} + +func TestGeneratorOverlays(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base1", ` +namePrefix: p1- +configMapGenerator: +- name: com1 + behavior: create + literals: + - from=base +`) + th.WriteK("base2", ` +namePrefix: p2- +configMapGenerator: +- name: com2 + behavior: create + literals: + - from=base +`) + th.WriteK("overlay/o1", ` +resources: +- ../../base1 +configMapGenerator: +- name: com1 + behavior: merge + literals: + - from=overlay +`) + th.WriteK("overlay/o2", ` +resources: +- ../../base2 +configMapGenerator: +- name: com2 + behavior: merge + literals: + - from=overlay +`) + th.WriteK("overlay", ` +resources: +- o1 +- o2 +configMapGenerator: +- name: com1 + behavior: merge + literals: + - foo=bar + - baz=qux +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + baz: qux + foo: bar + from: overlay +kind: ConfigMap +metadata: + name: p1-com1-8tc62428t2 +--- +apiVersion: v1 +data: + from: overlay +kind: ConfigMap +metadata: + name: p2-com2-87mcggf7d7 +`) +} + +func TestConfigMapGeneratorMergeNamePrefix(t *testing.T) { + + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +configMapGenerator: +- name: cm + behavior: create + literals: + - foo=bar +`) + th.WriteK("o1", ` +resources: +- ../base +namePrefix: o1- +`) + th.WriteK("o2", ` +resources: +- ../base +nameSuffix: -o2 +`) + th.WriteK(".", ` +resources: +- o1 +- o2 +configMapGenerator: +- name: o1-cm + behavior: merge + literals: + - big=bang +- name: cm-o2 + behavior: merge + literals: + - big=crunch +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + big: bang + foo: bar +kind: ConfigMap +metadata: + name: o1-cm-ft9mmdc8c6 +--- +apiVersion: v1 +data: + big: crunch + foo: bar +kind: ConfigMap +metadata: + name: cm-o2-5k95kd76ft +`) +} + +func TestConfigMapGeneratorLiteralNewline(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +generators: +- configmaps.yaml +`) + th.WriteF("configmaps.yaml", ` +apiVersion: builtin +kind: ConfigMapGenerator +metadata: + name: testing +literals: + - | + initial.txt=greetings + everyone + - | + final.txt=different + behavior +--- +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected( + m, ` +apiVersion: v1 +data: + final.txt: | + different + behavior + initial.txt: | + greetings + everyone +kind: ConfigMap +metadata: + name: testing-tt4769fb52 +`) +} + +// regression test for https://github.com/kubernetes-sigs/kustomize/issues/4233 +func TestDataEndsWithQuotes(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +configMapGenerator: + - name: test + literals: + - TEST=this is a 'test' +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected( + m, `apiVersion: v1 +data: + TEST: this is a 'test' +kind: ConfigMap +metadata: + name: test-k9cc55dfm5 +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/crd_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/crd_test.go new file mode 100644 index 000000000..a448b2ce8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/crd_test.go @@ -0,0 +1,362 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func writeBaseWithCrd(th kusttest_test.Harness) { + th.WriteK("base", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +crds: +- mycrd.json + +resources: +- secret.yaml +- mykind.yaml +- bee.yaml + +namePrefix: x- +`) + th.WriteF("base/bee.yaml", ` +apiVersion: v1beta1 +kind: Bee +metadata: + name: bee +spec: + action: fly +`) + th.WriteF("base/mykind.yaml", ` +apiVersion: jingfang.example.com/v1 +kind: MyKind +metadata: + name: mykind +spec: + secretRef: + name: crdsecret + beeRef: + name: bee +`) + th.WriteF("base/secret.yaml", ` +apiVersion: v1 +kind: Secret +metadata: + name: crdsecret +data: + PATH: yellowBrickRoad +`) + th.WriteF("base/mycrd.json", ` +{ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee": { + "Schema": { + "description": "Bee", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents.", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeList": { + "Schema": { + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object.", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents.", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.Bee", + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference": { + "Schema": { + "properties": { + "name": { + "type": "string" + } + } + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": { + "Schema": { + "description": "BeeSpec defines the desired state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": { + "Schema": { + "description": "BeeStatus defines the observed state of Bee" + }, + "Dependencies": [] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKind": { + "Schema": { + "description": "MyKind", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents.", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + }, + "spec": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec" + }, + "status": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec", + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus", + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindList": { + "Schema": { + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object.", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKind" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents.", + "type": "string" + }, + "metadata": { + "$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.MyKind", + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": { + "Schema": { + "description": "MyKindSpec defines the desired state of MyKind", + "properties": { + "beeRef": { + "x-kubernetes-object-ref-api-version": "v1beta1", + "x-kubernetes-object-ref-kind": "Bee", + "$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference" + }, + "secretRef": { + "description": "If defined, use this secret for configuring the MYSQL_ROOT_PASSWORD", + "x-kubernetes-object-ref-api-version": "v1", + "x-kubernetes-object-ref-kind": "Secret", + "$ref": "k8s.io/api/core/v1.LocalObjectReference" + } + } + }, + "Dependencies": [ + "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference", + "k8s.io/api/core/v1.LocalObjectReference" + ] + }, + "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": { + "Schema": { + "description": "MyKindStatus defines the observed state of MyKind" + }, + "Dependencies": [] + } +} +`) +} + +func TestCrdBase(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBaseWithCrd(th) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + PATH: yellowBrickRoad +kind: Secret +metadata: + name: x-crdsecret +--- +apiVersion: jingfang.example.com/v1 +kind: MyKind +metadata: + name: x-mykind +spec: + beeRef: + name: x-bee + secretRef: + name: x-crdsecret +--- +apiVersion: v1beta1 +kind: Bee +metadata: + name: x-bee +spec: + action: fly +`) +} + +func TestCrdWithOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBaseWithCrd(th) + th.WriteK("overlay", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namePrefix: prod- +resources: +- ../base +patchesStrategicMerge: +- bee.yaml +`) + th.WriteF("overlay/bee.yaml", ` +apiVersion: v1beta1 +kind: Bee +metadata: + name: bee +spec: + action: makehoney +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +data: + PATH: yellowBrickRoad +kind: Secret +metadata: + name: prod-x-crdsecret +--- +apiVersion: jingfang.example.com/v1 +kind: MyKind +metadata: + name: prod-x-mykind +spec: + beeRef: + name: prod-x-bee + secretRef: + name: prod-x-crdsecret +--- +apiVersion: v1beta1 +kind: Bee +metadata: + name: prod-x-bee +spec: + action: makehoney +`) +} + +func TestCrdWithContainers(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("crd/containers", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - crd.yaml +images: + - name: test/test + newName: registry.gitlab.com/test + newTag: latest +`) + th.WriteF("crd/containers/crd.yaml", ` +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: crontabs.stable.example.com +spec: + group: stable.example.com + scope: Namespaced + names: + plural: crontabs + singular: crontab + kind: CronTab + shortNames: + - ct + validation: + openAPIV3Schema: + properties: + spec: + containers: + description: Containers allows injecting additional containers + `) + m := th.Run("crd/containers", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: crontabs.stable.example.com +spec: + group: stable.example.com + names: + kind: CronTab + plural: crontabs + shortNames: + - ct + singular: crontab + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + containers: + description: Containers allows injecting additional containers +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfig_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfig_test.go new file mode 100644 index 000000000..291de8dd2 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfig_test.go @@ -0,0 +1,316 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func makeBaseReferencingCustomConfig(th kusttest_test.Harness) { + th.WriteK("base", ` +namePrefix: x- +commonLabels: + app: myApp +vars: +- name: APRIL_DIET + objref: + kind: Giraffe + name: april + fieldref: + fieldpath: spec.diet +- name: KOKO_DIET + objref: + kind: Gorilla + name: koko + fieldref: + fieldpath: spec.diet +resources: +- animalPark.yaml +- giraffes.yaml +- gorilla.yaml +configurations: +- config/defaults.yaml +- config/custom.yaml +`) + th.WriteF("base/giraffes.yaml", ` +kind: Giraffe +metadata: + name: april +spec: + diet: mimosa + location: NE +--- +kind: Giraffe +metadata: + name: may +spec: + diet: acacia + location: SE +`) + th.WriteF("base/gorilla.yaml", ` +kind: Gorilla +metadata: + name: koko +spec: + diet: bambooshoots + location: SW +`) + th.WriteF("base/animalPark.yaml", ` +apiVersion: foo +kind: AnimalPark +metadata: + name: sandiego +spec: + gorillaRef: + name: koko + giraffeRef: + name: april + food: + - "$(APRIL_DIET)" + - "$(KOKO_DIET)" +`) +} + +func TestCustomConfig(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeBaseReferencingCustomConfig(th) + th.WriteLegacyConfigs("base/config/defaults.yaml") + th.WriteF("base/config/custom.yaml", ` +nameReference: +- kind: Gorilla + fieldSpecs: + - kind: AnimalPark + path: spec/gorillaRef/name +- kind: Giraffe + fieldSpecs: + - kind: AnimalPark + path: spec/giraffeRef/name +varReference: +- path: spec/food + kind: AnimalPark +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: foo +kind: AnimalPark +metadata: + labels: + app: myApp + name: x-sandiego +spec: + food: + - mimosa + - bambooshoots + giraffeRef: + name: x-april + gorillaRef: + name: x-koko +--- +kind: Giraffe +metadata: + labels: + app: myApp + name: x-april +spec: + diet: mimosa + location: NE +--- +kind: Giraffe +metadata: + labels: + app: myApp + name: x-may +spec: + diet: acacia + location: SE +--- +kind: Gorilla +metadata: + labels: + app: myApp + name: x-koko +spec: + diet: bambooshoots + location: SW +`) +} + +func TestCustomConfigWithDefaultOverspecification(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeBaseReferencingCustomConfig(th) + th.WriteLegacyConfigs("base/config/defaults.yaml") + // Specifying namePrefix here conflicts with (is the same as) + // the defaults written above. This is intentional in the + // test to assure duplicate config doesn't cause problems. + th.WriteF("base/config/custom.yaml", ` +namePrefix: +- path: metadata/name +nameReference: +- kind: Gorilla + fieldSpecs: + - kind: AnimalPark + path: spec/gorillaRef/name +- kind: Giraffe + fieldSpecs: + - kind: AnimalPark + path: spec/giraffeRef/name +varReference: +- path: spec/food + kind: AnimalPark +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: foo +kind: AnimalPark +metadata: + labels: + app: myApp + name: x-sandiego +spec: + food: + - mimosa + - bambooshoots + giraffeRef: + name: x-april + gorillaRef: + name: x-koko +--- +kind: Giraffe +metadata: + labels: + app: myApp + name: x-april +spec: + diet: mimosa + location: NE +--- +kind: Giraffe +metadata: + labels: + app: myApp + name: x-may +spec: + diet: acacia + location: SE +--- +kind: Gorilla +metadata: + labels: + app: myApp + name: x-koko +spec: + diet: bambooshoots + location: SW +`) +} + +func TestFixedBug605_BaseCustomizationAvailableInOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeBaseReferencingCustomConfig(th) + th.WriteLegacyConfigs("base/config/defaults.yaml") + th.WriteF("base/config/custom.yaml", ` +nameReference: +- kind: Gorilla + fieldSpecs: + - apiVersion: foo + kind: AnimalPark + path: spec/gorillaRef/name +- kind: Giraffe + fieldSpecs: + - apiVersion: foo + kind: AnimalPark + path: spec/giraffeRef/name +varReference: +- path: spec/food + apiVersion: foo + kind: AnimalPark +`) + th.WriteK("overlay", ` +namePrefix: o- +commonLabels: + movie: planetOfTheApes +patchesStrategicMerge: +- animalPark.yaml +resources: +- ../base +- ursus.yaml +`) + th.WriteF("overlay/ursus.yaml", ` +kind: Gorilla +metadata: + name: ursus +spec: + diet: heston + location: Arizona +`) + // The following replaces the gorillaRef in the AnimalPark. + th.WriteF("overlay/animalPark.yaml", ` +apiVersion: foo +kind: AnimalPark +metadata: + name: sandiego +spec: + gorillaRef: + name: ursus +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: foo +kind: AnimalPark +metadata: + labels: + app: myApp + movie: planetOfTheApes + name: o-x-sandiego +spec: + food: + - mimosa + - bambooshoots + giraffeRef: + name: o-x-april + gorillaRef: + name: o-ursus +--- +kind: Giraffe +metadata: + labels: + app: myApp + movie: planetOfTheApes + name: o-x-april +spec: + diet: mimosa + location: NE +--- +kind: Giraffe +metadata: + labels: + app: myApp + movie: planetOfTheApes + name: o-x-may +spec: + diet: acacia + location: SE +--- +kind: Gorilla +metadata: + labels: + app: myApp + movie: planetOfTheApes + name: o-x-koko +spec: + diet: bambooshoots + location: SW +--- +kind: Gorilla +metadata: + labels: + movie: planetOfTheApes + name: o-ursus +spec: + diet: heston + location: Arizona +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfigofbuiltinplugin_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfigofbuiltinplugin_test.go new file mode 100644 index 000000000..7b33e4038 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfigofbuiltinplugin_test.go @@ -0,0 +1,96 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + "sigs.k8s.io/kustomize/api/types" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// Demo custom configuration of a builtin transformation. +// This is a NamePrefixer that touches Deployments +// and Services exclusively. +func TestCustomNamePrefixer(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + th.GetPluginConfig().BpLoadingOptions = types.BploUseStaticallyLinked + defer th.Reset() + + th.WriteK(".", ` +resources: +- deployment.yaml +- role.yaml +- service.yaml +transformers: +- prefixer.yaml +`) + th.WriteF("prefixer.yaml", ` +apiVersion: builtin +kind: PrefixSuffixTransformer +metadata: + name: customPrefixer +prefix: zzz- +fieldSpecs: +- kind: Deployment + path: metadata/name +- kind: Service + path: metadata/name +`) + th.WriteF("deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + metadata: + labels: + backend: awesome + spec: + containers: + - name: whatever + image: whatever +`) + th.WriteF("role.yaml", ` +apiVersion: v1 +kind: Role +metadata: + name: myRole +`) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zzz-myDeployment +spec: + template: + metadata: + labels: + backend: awesome + spec: + containers: + - image: whatever + name: whatever +--- +apiVersion: v1 +kind: Role +metadata: + name: myRole +--- +apiVersion: v1 +kind: Service +metadata: + name: zzz-myService +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfigreusable_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfigreusable_test.go new file mode 100644 index 000000000..04e56512c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/customconfigreusable_test.go @@ -0,0 +1,178 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/api/types" +) + +// Demo custom configuration as a base. +// This test shows usage of three custom configurations sitting +// in a base, allowing them to be reused in any number of +// kustomizations. +func TestReusableCustomTransformers(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + PrepBuiltin("AnnotationsTransformer"). + PrepBuiltin("LabelTransformer") + th.GetPluginConfig().BpLoadingOptions = types.BploUseStaticallyLinked + defer th.Reset() + + // First write three custom configurations for builtin plugins. + + // A custom name prefixer that only touches Deployments and Services. + th.WriteF("mytransformers/deploymentServicePrefixer.yaml", ` +apiVersion: builtin +kind: PrefixSuffixTransformer +metadata: + name: myPrefixer +prefix: bob- +fieldSpecs: +- kind: Deployment + path: metadata/name +- kind: Service + path: metadata/name +`) + + // A custom annotator exclusively annotating Roles. + th.WriteF("mytransformers/roleAnnotator.yaml", ` +apiVersion: builtin +kind: AnnotationsTransformer +metadata: + name: myAnnotator +annotations: + # Imagine that SRE has not approved this role yet. + status: probationary +fieldSpecs: +- path: metadata/annotations + create: true + kind: Role +`) + + // A custom labeller that only labels Deployments, + // and only labels them at their top metadata level + // exclusively. It does not modify selectors or + // add labels to pods in the template. + th.WriteF("mytransformers/deploymentLabeller.yaml", ` +apiVersion: builtin +kind: LabelTransformer +metadata: + name: myLabeller +labels: + pager: 867-5301 +fieldSpecs: +- path: metadata/labels + create: true + kind: Deployment +`) + + // Combine these custom transformers in one kustomization file. + // This kustomization file contains only resources that + // all happen to be plugin configurations. This makes + // these plugins re-usable as a group in any number of other + // kustomizations. + th.WriteK("mytransformers", ` +resources: +- deploymentServicePrefixer.yaml +- roleAnnotator.yaml +- deploymentLabeller.yaml +`) + + // Finally, define the kustomization for the (arbitrarily named) + // staging environment. + th.WriteK("staging", ` + +# Bring in the custom transformers. +transformers: +- ../mytransformers + +# Also use the "classic" labeller, which behind the scenes uses +# the LabelTransformer, but with a broad configuration targeting +# many resources and fields (including selector fields). +# It's a big hammer - probably too big; the output shows all the +# places 'acmeCorp' now appears as a result. To avoid this, +# define your own config for your own LabelTransformer instance +# as shown above. +commonLabels: + company: acmeCorp + +# Specify the resources to modify. +resources: +- deployment.yaml +- role.yaml +- service.yaml +`) + th.WriteF("staging/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeployment +spec: + template: + metadata: + labels: + backend: flawless + spec: + containers: + - name: whatever + image: whatever +`) + th.WriteF("staging/role.yaml", ` +apiVersion: v1 +kind: Role +metadata: + name: myRole +`) + th.WriteF("staging/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +`) + + m := th.Run("staging", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + company: acmeCorp + pager: 867-5301 + name: bob-myDeployment +spec: + selector: + matchLabels: + company: acmeCorp + template: + metadata: + labels: + backend: flawless + company: acmeCorp + spec: + containers: + - image: whatever + name: whatever +--- +apiVersion: v1 +kind: Role +metadata: + annotations: + status: probationary + labels: + company: acmeCorp + name: myRole +--- +apiVersion: v1 +kind: Service +metadata: + labels: + company: acmeCorp + name: bob-myService +spec: + selector: + company: acmeCorp +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamondcomposition_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamondcomposition_test.go new file mode 100644 index 000000000..401afb1a9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamondcomposition_test.go @@ -0,0 +1,459 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "fmt" + "path/filepath" + "strings" + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/api/types" +) + +const patchAddProbe = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - name: my-deployment + livenessProbe: + httpGet: + path: /healthz + port: 8080 +` + +const container = `{ "image": "my-image", "livenessProbe": { "httpGet" : {"path": "/healthz", "port": 8080 } }, "name": "my-deployment"}` + +const patchJsonAddProbe = `[{"op": "replace", "path": "/spec/template/spec/containers/0", "value": ` + + container + `}]` + +const patchDnsPolicy = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + dnsPolicy: ClusterFirst +` +const patchJsonDnsPolicy = `[{"op": "add", "path": "/spec/template/spec/dnsPolicy", "value": "ClusterFirst"}]` + +const patchRestartPolicy = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + restartPolicy: Always +` +const patchJsonRestartPolicy = `[{"op": "add", "path": "/spec/template/spec/restartPolicy", "value": "Always"}]` + +func writeDeploymentBase(th kusttest_test.Harness) { + th.WriteK("base", ` +resources: +- deployment.yaml +`) + + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + dnsPolicy: "None" + containers: + - name: my-deployment + image: my-image +`) +} + +func writeProbeOverlay(th kusttest_test.Harness) { + th.WriteK("probe", ` +resources: +- ../base +patchesStrategicMerge: +- dep-patch.yaml +`) + th.WriteF("probe/dep-patch.yaml", patchAddProbe) +} + +func writeDNSOverlay(th kusttest_test.Harness) { + th.WriteK("dns", ` +resources: +- ../base +patchesStrategicMerge: +- dep-patch.yaml +`) + th.WriteF("dns/dep-patch.yaml", patchDnsPolicy) +} + +func writeRestartOverlay(th kusttest_test.Harness) { + th.WriteK("restart", ` +resources: +- ../base +patchesStrategicMerge: +- dep-patch.yaml +`) + th.WriteF("restart/dep-patch.yaml", patchRestartPolicy) +} + +// Here's a composite kustomization, that combines multiple overlays +// (replicas, dns and metadata) which patch the same base resource. +// +// The base resource is a deployment and the overlays patch aspects +// of it, without using any of the `namePrefix`, `nameSuffix` or `namespace` +// kustomization keywords. +// +// composite +// / | \ +// probe dns restart +// \ | / +// base +// +func TestIssue1251_CompositeDiamond_Failure(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeDeploymentBase(th) + writeProbeOverlay(th) + writeDNSOverlay(th) + writeRestartOverlay(th) + + th.WriteK("composite", ` +resources: +- ../probe +- ../dns +- ../restart +`) + + err := th.RunWithErr("composite", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("Expected resource accumulation error") + } + if !strings.Contains( + err.Error(), "already registered id: Deployment.v1.apps/my-deployment.[noNs]") { + t.Fatalf("Unexpected err: %v", err) + } +} + +const expectedPatchedDeployment = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - image: my-image + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: my-deployment + dnsPolicy: ClusterFirst + restartPolicy: Always +` + +// This test reuses some methods from TestIssue1251_CompositeDiamond, +// but overwrites the kustomization files in the overlays. +func TestIssue1251_Patches_Overlayed(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeDeploymentBase(th) + + // probe overlays base. + writeProbeOverlay(th) + + // dns overlays probe. + writeDNSOverlay(th) + th.WriteK("dns", ` +resources: +- ../probe +patchesStrategicMerge: +- dep-patch.yaml +`) + + // restart overlays dns. + writeRestartOverlay(th) + th.WriteK("restart", ` +resources: +- ../dns +patchesStrategicMerge: +- dep-patch.yaml +`) + + m := th.Run("restart", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expectedPatchedDeployment) +} + +func TestIssue1251_Patches_Local(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeDeploymentBase(th) + + th.WriteK("composite", ` +resources: +- ../base +patchesStrategicMerge: +- patchAddProbe.yaml +- patchDnsPolicy.yaml +- patchRestartPolicy.yaml +`) + th.WriteF("composite/patchRestartPolicy.yaml", patchRestartPolicy) + th.WriteF("composite/patchDnsPolicy.yaml", patchDnsPolicy) + th.WriteF("composite/patchAddProbe.yaml", patchAddProbe) + + m := th.Run("composite", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expectedPatchedDeployment) +} + +func definePatchDirStructure(th kusttest_test.Harness) { + writeDeploymentBase(th) + + th.WriteF("patches/patchRestartPolicy.yaml", patchRestartPolicy) + th.WriteF("patches/patchDnsPolicy.yaml", patchDnsPolicy) + th.WriteF("patches/patchAddProbe.yaml", patchAddProbe) +} + +// Fails due to file load restrictor. +func TestIssue1251_Patches_ProdVsDev_Failure(t *testing.T) { + th := kusttest_test.MakeHarness(t) + definePatchDirStructure(th) + + th.WriteK("prod", ` +resources: +- ../base +patchesStrategicMerge: +- ../patches/patchAddProbe.yaml +- ../patches/patchDnsPolicy.yaml +`) + + err := th.RunWithErr("prod", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains( + err.Error(), + "security; file '/patches/patchAddProbe.yaml' is not in or below '/prod'") { + t.Fatalf("unexpected error: %v", err) + } +} + +const prodDevMergeResult1 = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - image: my-image + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: my-deployment + dnsPolicy: ClusterFirst +` + +const prodDevMergeResult2 = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - image: my-image + name: my-deployment + dnsPolicy: ClusterFirst + restartPolicy: Always +` + +// This test does what +// TestIssue1251_Patches_ProdVsDev_Failure +// failed to do, because this test does the equivalent +// os specifying `--load_restrictor none` on the build. +// +// This allows the use patch files located outside the +// kustomization root, and not in a kustomization +// themselves. +// +// Doing so means the kustomization using them is no +// longer relocatable, not addressible via a git URL, +// and not git clonable. It's no longer self-contained. +// +// Likewise suppressing load restrictions happens for +// the entire build (i.e. everything can reach outside +// the kustomization root), opening the user to whatever +// threat the load restrictor was meant to address. +func TestIssue1251_Patches_ProdVsDev(t *testing.T) { + th := kusttest_test.MakeHarness(t) + definePatchDirStructure(th) + + th.WriteK("prod", ` +resources: +- ../base +patchesStrategicMerge: +- ../patches/patchAddProbe.yaml +- ../patches/patchDnsPolicy.yaml +`) + opts := th.MakeDefaultOptions() + opts.LoadRestrictions = types.LoadRestrictionsNone + + m := th.Run("prod", opts) + th.AssertActualEqualsExpected(m, prodDevMergeResult1) + + th = kusttest_test.MakeHarness(t) + definePatchDirStructure(th) + + th.WriteK("dev", ` +resources: +- ../base +patchesStrategicMerge: +- ../patches/patchDnsPolicy.yaml +- ../patches/patchRestartPolicy.yaml +`) + + m = th.Run("dev", opts) + th.AssertActualEqualsExpected(m, prodDevMergeResult2) +} + +func TestIssue1251_Plugins_ProdVsDev(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + PrepBuiltin("PatchJson6902Transformer") + defer th.Reset() + + defineTransformerDirStructure(th) + th.WriteK("prod", ` +resources: +- ../base +transformers: +- ../patches/addProbe +- ../patches/addDnsPolicy +`) + + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, prodDevMergeResult1) + + defineTransformerDirStructure(th) + th.WriteK("dev", ` +resources: +- ../base +transformers: +- ../patches/addRestartPolicy +- ../patches/addDnsPolicy +`) + + m = th.Run("dev", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, prodDevMergeResult2) +} + +func TestIssue1251_Plugins_Local(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + PrepBuiltin("PatchJson6902Transformer") + defer th.Reset() + + writeDeploymentBase(th.Harness) + + writeJsonTransformerPluginConfig( + th, "composite", "addDnsPolicy", patchJsonDnsPolicy) + writeJsonTransformerPluginConfig( + th, "composite", "addRestartPolicy", patchJsonRestartPolicy) + writeJsonTransformerPluginConfig( + th, "composite", "addProbe", patchJsonAddProbe) + + th.WriteK("composite", ` +resources: +- ../base +transformers: +- addDnsPolicyConfig.yaml +- addRestartPolicyConfig.yaml +- addProbeConfig.yaml +`) + m := th.Run("composite", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expectedPatchedDeployment) +} + +func writeJsonTransformerPluginConfig( + th *kusttest_test.HarnessEnhanced, path, name, patch string) { + th.WriteF(filepath.Join(path, name+"Config.yaml"), + fmt.Sprintf(` +apiVersion: builtin +kind: PatchJson6902Transformer +metadata: + name: %s +target: + group: apps + version: v1 + kind: Deployment + name: my-deployment +jsonOp: '%s' +`, name, patch)) +} + +// Remote in the sense that they are bundled in a different kustomization. +func TestIssue1251_Plugins_Bundled(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + PrepBuiltin("PatchJson6902Transformer") + defer th.Reset() + writeDeploymentBase(th.Harness) + + th.WriteK("patches", ` +resources: +- addDnsPolicyConfig.yaml +- addRestartPolicyConfig.yaml +- addProbeConfig.yaml +`) + writeJsonTransformerPluginConfig( + th, "patches", "addDnsPolicy", patchJsonDnsPolicy) + writeJsonTransformerPluginConfig( + th, "patches", "addRestartPolicy", patchJsonRestartPolicy) + writeJsonTransformerPluginConfig( + th, "patches", "addProbe", patchJsonAddProbe) + + th.WriteK("composite", ` +resources: +- ../base +transformers: +- ../patches +`) + m := th.Run("composite", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expectedPatchedDeployment) +} + +func defineTransformerDirStructure(th *kusttest_test.HarnessEnhanced) { + writeDeploymentBase(th.Harness) + + th.WriteK("patches/addDnsPolicy", ` +resources: +- addDnsPolicyConfig.yaml +`) + writeJsonTransformerPluginConfig( + th, "patches/addDnsPolicy", "addDnsPolicy", patchJsonDnsPolicy) + + th.WriteK("patches/addRestartPolicy", ` +resources: +- addRestartPolicyConfig.yaml +`) + writeJsonTransformerPluginConfig( + th, "patches/addRestartPolicy", "addRestartPolicy", patchJsonRestartPolicy) + + th.WriteK("patches/addProbe", ` +resources: +- addProbeConfig.yaml +`) + writeJsonTransformerPluginConfig( + th, "patches/addProbe", "addProbe", patchJsonAddProbe) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamondpatchref_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamondpatchref_test.go new file mode 100644 index 000000000..8129bbd8f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamondpatchref_test.go @@ -0,0 +1,227 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +// In the following structure `top` patches `left-deploy` and `right-deploy` to reference the +// configMap in `bottom`. This test verifies `configMapRef.name` in `left-deploy` and +// `right-deploy` is modified correctly. +// +// top +// / \ +// left right +// / \ / \ +// left-service bottom bottom right-service +// | \ / | +// left-deploy bottom right-deploy +// | +// configMap + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestConfMapNameResolutionInDiamondWithPatches(t *testing.T) { + th := kusttest_test.MakeHarness(t) + + th.WriteK("bottom", ` +configMapGenerator: +- name: bottom + literals: + - KEY=value +`) + + th.WriteK("left-service", ` +resources: +- deployment.yaml +`) + + th.WriteF("left-service/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: left-deploy + labels: + app.kubernetes.io/name: left-deploy +spec: + selector: + matchLabels: + app.kubernetes.io/name: left-pod + template: + metadata: + labels: + app.kubernetes.io/name: left-pod + spec: + containers: + - image: left-image:v1.0 + name: service +`) + + th.WriteK("right-service", ` +resources: +- deployment.yaml +`) + + th.WriteF("right-service/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: right-deploy + labels: + app.kubernetes.io/name: right-deploy +spec: + selector: + matchLabels: + app.kubernetes.io/name: right-pod + template: + metadata: + labels: + app.kubernetes.io/name: right-pod + spec: + containers: + - image: right-image:v1.0 + name: service +`) + + th.WriteK("top", ` +resources: +- ./left +- ./right +`) + + th.WriteK("top/left", ` +resources: +- ../../left-service +- ./bottom + +patches: +- target: + kind: Deployment + name: left-deploy + patch: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ignored-by-kustomize + spec: + template: + spec: + containers: + - name: service + env: + - name: MAPPED_CONFIG_ITEM + valueFrom: + configMapKeyRef: + name: left-bottom + key: KEY +`) + + th.WriteK("top/left/bottom", ` +namePrefix: left- + +resources: +- ../../../bottom +`) + + th.WriteK("top/right", ` +resources: +- ../../right-service +- ./bottom + +patches: +- target: + kind: Deployment + name: right-deploy + patch: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ignored-by-kustomize + spec: + template: + spec: + containers: + - name: service + env: + - name: MAPPED_CONFIG_ITEM + valueFrom: + configMapKeyRef: + name: right-bottom + key: KEY +`) + + th.WriteK("top/right/bottom", ` +namePrefix: right- + +resources: +- ../../../bottom +`) + + m := th.Run("top", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, `apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: left-deploy + name: left-deploy +spec: + selector: + matchLabels: + app.kubernetes.io/name: left-pod + template: + metadata: + labels: + app.kubernetes.io/name: left-pod + spec: + containers: + - env: + - name: MAPPED_CONFIG_ITEM + valueFrom: + configMapKeyRef: + key: KEY + name: left-bottom-9f2t6f5h6d + image: left-image:v1.0 + name: service +--- +apiVersion: v1 +data: + KEY: value +kind: ConfigMap +metadata: + name: left-bottom-9f2t6f5h6d +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: right-deploy + name: right-deploy +spec: + selector: + matchLabels: + app.kubernetes.io/name: right-pod + template: + metadata: + labels: + app.kubernetes.io/name: right-pod + spec: + containers: + - env: + - name: MAPPED_CONFIG_ITEM + valueFrom: + configMapKeyRef: + key: KEY + name: right-bottom-9f2t6f5h6d + image: right-image:v1.0 + name: service +--- +apiVersion: v1 +data: + KEY: value +kind: ConfigMap +metadata: + name: right-bottom-9f2t6f5h6d +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamonds_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamonds_test.go new file mode 100644 index 000000000..6160e62f7 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/diamonds_test.go @@ -0,0 +1,222 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// Here's a structure of two kustomizations, +// `dev` and `prod`, individually deployable, +// that depend on a diamond that combines +// multiple tenants (kirk, spock and bones), +// each sharing a common base. +// +// The objects used are contrived to avoid +// clouding the example with authentic +// but verbose Deployment boilerplate. +// +// Patches are applied at various levels, +// requiring more specificity as needed. +// +// dev prod +// \ / +// tenants +// / | \ +// kirk spock bones +// \ | / +// base +// +func writeDiamondBase(th kusttest_test.Harness) { + th.WriteK("base", ` +resources: +- deploy.yaml +`) + th.WriteF("base/deploy.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + numReplicas: 1 +`) +} + +func writeKirk(th kusttest_test.Harness) { + th.WriteK("kirk", ` +namePrefix: kirk- +resources: +- ../base +- configmap.yaml +patchesStrategicMerge: +- dep-patch.yaml +`) + th.WriteF("kirk/dep-patch.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + type: Confident +`) + th.WriteF("kirk/configmap.yaml", ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: settings +data: + phaser: caress +`) +} + +func writeSpock(th kusttest_test.Harness) { + th.WriteK("spock", ` +namePrefix: spock- +resources: +- ../base +patchesStrategicMerge: +- dep-patch.yaml +`) + th.WriteF("spock/dep-patch.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + type: Logical +`) +} + +func writeBones(th kusttest_test.Harness) { + th.WriteK("bones", ` +namePrefix: bones- +resources: +- ../base +patchesStrategicMerge: +- dep-patch.yaml +`) + th.WriteF("bones/dep-patch.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: storefront +spec: + type: Concerned +`) +} + +func writeTenants(th kusttest_test.Harness) { + th.WriteK("tenants", ` +namePrefix: t- +resources: +- ../kirk +- ../spock +- ../bones +- configMap.yaml +patchesStrategicMerge: +- bones-patch.yaml +`) + th.WriteF("tenants/bones-patch.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: bones-storefront +spec: + mood: Cantankerous +`) + th.WriteF("tenants/configMap.yaml", ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: federation +data: + zone: neutral + guardian: forever +`) +} + +func TestBasicDiamond(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeDiamondBase(th) + writeKirk(th) + writeSpock(th) + writeBones(th) + writeTenants(th) + th.WriteK("prod", ` +namePrefix: prod- +resources: +- ../tenants +patchesStrategicMerge: +- patches.yaml +`) + // The patch only has to be specific enough + // to match the item. + th.WriteF("prod/patches.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: t-kirk-storefront +spec: + numReplicas: 10000 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: federation +data: + guardian: ofTheGalaxy +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: t-federation +data: + zone: twilight +`) + + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Deployment +metadata: + name: prod-t-kirk-storefront +spec: + numReplicas: 10000 + type: Confident +--- +apiVersion: v1 +data: + phaser: caress +kind: ConfigMap +metadata: + name: prod-t-kirk-settings +--- +apiVersion: v1 +kind: Deployment +metadata: + name: prod-t-spock-storefront +spec: + numReplicas: 1 + type: Logical +--- +apiVersion: v1 +kind: Deployment +metadata: + name: prod-t-bones-storefront +spec: + mood: Cantankerous + numReplicas: 1 + type: Concerned +--- +apiVersion: v1 +data: + guardian: ofTheGalaxy + zone: twilight +kind: ConfigMap +metadata: + name: prod-t-federation +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/directoryarrangement_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/directoryarrangement_test.go new file mode 100644 index 000000000..e066b44f0 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/directoryarrangement_test.go @@ -0,0 +1,29 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestIssue596AllowDirectoriesThatAreSubstringsOfEachOther(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", "") + th.WriteK("overlays/aws", ` +resources: +- ../../base +`) + th.WriteK("overlays/aws-nonprod", ` +resources: +- ../aws +`) + th.WriteK("overlays/aws-sandbox2.us-east-1", ` +resources: +- ../aws-nonprod +`) + m := th.Run("overlays/aws-sandbox2.us-east-1", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, "") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/disablenamesuffix_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/disablenamesuffix_test.go new file mode 100644 index 000000000..9580a8914 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/disablenamesuffix_test.go @@ -0,0 +1,146 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func findSecret(m resmap.ResMap, prefix string) *resource.Resource { + for _, r := range m.Resources() { + if r.OrgId().Kind == "Secret" { + if prefix == "" || strings.HasPrefix(r.GetName(), prefix) { + return r + } + } + } + return nil +} + +func TestDisableNameSuffixHash(t *testing.T) { + th := kusttest_test.MakeHarness(t) + const kustomizationContent = ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namePrefix: foo- +nameSuffix: -bar +namespace: ns1 +commonLabels: + app: nginx +commonAnnotations: + note: This is a test annotation +resources: + - deployment.yaml + - namespace.yaml +generatorOptions: + disableNameSuffixHash: false +configMapGenerator: +- name: literalConfigMap + literals: + - DB_USERNAME=admin + - DB_PASSWORD=somepw +secretGenerator: +- name: secret + literals: + - DB_USERNAME=admin + - DB_PASSWORD=somepw + type: Opaque +patchesJson6902: +- target: + group: apps + version: v1 + kind: Deployment + name: dply1 + path: jsonpatch.json +` + + th.WriteK("/whatever", kustomizationContent) + th.WriteF("/whatever/deployment.yaml", ` +apiVersion: apps/v1 +metadata: + name: dply1 +kind: Deployment +`) + th.WriteF("/whatever/namespace.yaml", ` +apiVersion: v1 +kind: Namespace +metadata: + name: ns1 +`) + th.WriteF("/whatever/jsonpatch.json", `[ + {"op": "add", "path": "/spec/replica", "value": "3"} +]`) + + m := th.Run("/whatever", th.MakeDefaultOptions()) + + secret := findSecret(m, "") + if secret == nil { + t.Errorf("Expected to find a Secret") + } + if secret.GetName() != "foo-secret-bar-82c2g5f8f6" { + t.Errorf("unexpected secret resource name: %s", secret.GetName()) + } + + th.WriteK("/whatever", + strings.Replace(kustomizationContent, + "disableNameSuffixHash: false", + "disableNameSuffixHash: true", -1)) + m = th.Run("/whatever", th.MakeDefaultOptions()) + secret = findSecret(m, "") + if secret == nil { + t.Errorf("Expected to find a Secret") + } + if secret.GetName() != "foo-secret-bar" { // No hash at end. + t.Errorf("unexpected secret resource name: %s", secret.GetName()) + } +} +func TestDisableNameSuffixHashPerObject(t *testing.T) { + th := kusttest_test.MakeHarness(t) + const kustomizationContent = ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +generatorOptions: + disableNameSuffixHash: false +secretGenerator: +- name: nohash + options: + disableNameSuffixHash: true + literals: + - DB_USERNAME=admin + - DB_PASSWORD=somepw + type: Opaque +- name: yeshash + options: + disableNameSuffixHash: false + literals: + - DB_USERNAME=admin + - DB_PASSWORD=somepw + type: Opaque +` + + th.WriteK("/whatever", kustomizationContent) + + m := th.Run("/whatever", th.MakeDefaultOptions()) + + secret := findSecret(m, "nohash") + if secret == nil { + t.Errorf("Expected to find a Secret") + } + if secret.GetName() != "nohash" { + t.Errorf("unexpected secret resource name: %s", secret.GetName()) + } + + secret = findSecret(m, "yeshash") + if secret == nil { + t.Errorf("Expected to find a Secret") + } + if secret.GetName() != "yeshash-82c2g5f8f6" { + t.Errorf("unexpected secret resource name: %s", secret.GetName()) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/doc.go new file mode 100644 index 000000000..bf516ca94 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/doc.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package krusty is intended as the entry point package +// for those seeking to add kustomize ability to other +// programs. +// +// To use, follow the example of the kustomize CLI's 'build' +// command. Also, see the high level tests in this package, +// which serve a dual purpose as examples. +package krusty diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/duplicatekeys_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/duplicatekeys_test.go new file mode 100644 index 000000000..bf480ca73 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/duplicatekeys_test.go @@ -0,0 +1,43 @@ +package krusty_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestDuplicateKeys(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- resources.yaml +`) + th.WriteF("resources.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: podinfo +spec: + selector: + matchLabels: + app: podinfo + template: + spec: + containers: + - name: podinfod + image: ghcr.io/stefanprodan/podinfo:5.0.3 + command: + - ./podinfo + env: + - name: PODINFO_UI_COLOR + value: "#34577c" + env: + - name: PODINFO_UI_COLOR + value: "#34577c" +`) + m := th.Run(".", th.MakeDefaultOptions()) + _, err := m.AsYaml() + assert.Error(t, err) + assert.Contains(t, err.Error(), "mapping key \"env\" already defined") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/extendedpatch_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/extendedpatch_test.go new file mode 100644 index 000000000..955082f61 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/extendedpatch_test.go @@ -0,0 +1,1215 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func makeCommonFileForExtendedPatchTest(th kusttest_test.Harness) { + th.WriteF("base/deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx + labels: + app: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: nginx-persistent-storage + mountPath: /tmp/ps + volumes: + - name: nginx-persistent-storage + emptyDir: {} + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + labels: + app: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - name: busybox + image: busybox + volumeMounts: + - name: busybox-persistent-storage + mountPath: /tmp/ps + volumes: + - name: busybox-persistent-storage + emptyDir: {} + - configMap: + name: configmap-in-base + name: configmap-in-base +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: nginx + labels: + app: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: busybox + labels: + app: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchNameSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: busybox +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchGvkSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + kind: Deployment +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchLabelSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + labelSelector: app=nginx +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + new-key: new-value + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchNameGvkSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: busybox + kind: Deployment +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchNameLabelSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: .* + labelSelector: app=busybox +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchGvkLabelSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + kind: Deployment + labelSelector: app=busybox +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchNameGvkLabelSelector(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: busybox + kind: Deployment + labelSelector: app=busybox +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchNoMatch(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: no-match +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchWithoutTarget(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchNoMatchMultiplePatch(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: no-match +- path: patch.yaml + target: + name: busybox + kind: Job +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + +func TestExtendedPatchMultiplePatchOverlapping(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch1.yaml + target: + labelSelector: app=busybox +- path: patch2.yaml + target: + name: busybox + kind: Deployment +`) + th.WriteF("base/patch1.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key-from-patch1: new-value +`) + th.WriteF("base/patch2.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key-from-patch2: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + new-key-from-patch1: new-value + new-key-from-patch2: new-value + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + new-key-from-patch1: new-value + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/fnplugin_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/fnplugin_test.go new file mode 100644 index 000000000..88fa8dee8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/fnplugin_test.go @@ -0,0 +1,631 @@ +package krusty_test + +import ( + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +const generateDeploymentDotSh = `#!/bin/sh + +cat < + // is added to all the resources in the build out. + AddManagedbyLabel bool + + // Restrictions on what can be loaded from the file system. + // See type definition. + LoadRestrictions types.LoadRestrictions + + // Create an inventory object for pruning. + DoPrune bool + + // Options related to kustomize plugins. + PluginConfig *types.PluginConfig +} + +// MakeDefaultOptions returns a default instance of Options. +func MakeDefaultOptions() *Options { + return &Options{ + DoLegacyResourceSort: false, + AddManagedbyLabel: false, + LoadRestrictions: types.LoadRestrictionsRootOnly, + DoPrune: false, + PluginConfig: types.DisabledPluginConfig(), + } +} + +// GetBuiltinPluginNames returns a list of builtin plugin names +func GetBuiltinPluginNames() []string { + var ret []string + for k := range builtinhelpers.GeneratorFactories { + ret = append(ret, k.String()) + } + for k := range builtinhelpers.TransformerFactories { + ret = append(ret, k.String()) + } + return ret +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/originannotation_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/originannotation_test.go new file mode 100644 index 000000000..7adbc9f54 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/originannotation_test.go @@ -0,0 +1,1145 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/krusty" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestAnnoOriginLocalFiles(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +spec: + ports: + - port: 7002 +`) + th.WriteK(".", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- service.yaml +buildMetadata: [originAnnotations] +`) + options := th.MakeDefaultOptions() + m := th.Run(".", options) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + annotations: + config.kubernetes.io/origin: | + path: service.yaml + name: myService +spec: + ports: + - port: 7002 +`) +} + +func TestAnnoOriginLocalFilesWithOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +namePrefix: b- +resources: +- namespace.yaml +- role.yaml +- service.yaml +- deployment.yaml +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService +`) + th.WriteF("base/namespace.yaml", ` +apiVersion: v1 +kind: Namespace +metadata: + name: myNs +`) + th.WriteF("base/role.yaml", ` +apiVersion: v1 +kind: Role +metadata: + name: myRole +`) + th.WriteF("base/deployment.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: myDep +`) + th.WriteK("prod", ` +namePrefix: p- +resources: +- ../base +- service.yaml +- namespace.yaml +buildMetadata: [originAnnotations] +`) + th.WriteF("prod/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: myService2 +`) + th.WriteF("prod/namespace.yaml", ` +apiVersion: v1 +kind: Namespace +metadata: + name: myNs2 +`) + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Namespace +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/namespace.yaml + name: myNs +--- +apiVersion: v1 +kind: Role +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/role.yaml + name: p-b-myRole +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/service.yaml + name: p-b-myService +--- +apiVersion: v1 +kind: Deployment +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/deployment.yaml + name: p-b-myDep +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + config.kubernetes.io/origin: | + path: service.yaml + name: p-myService2 +--- +apiVersion: v1 +kind: Namespace +metadata: + annotations: + config.kubernetes.io/origin: | + path: namespace.yaml + name: myNs2 +`) +} + +// This is a copy of TestGeneratorBasics in configmaps_test.go, +// except that we've enabled the addAnnoOrigin option. +func TestGeneratorWithAnnoOrigin(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +namePrefix: blah- +configMapGenerator: +- name: bob + literals: + - fruit=apple + - vegetable=broccoli + envs: + - foo.env + env: bar.env + files: + - passphrase=phrase.dat + - forces.txt +- name: json + literals: + - 'v2=[{"path": "var/druid/segment-cache"}]' + - >- + druid_segmentCache_locations=[{"path": + "var/druid/segment-cache", + "maxSize": 32000000000, + "freeSpacePercent": 1.0}] +secretGenerator: +- name: bob + literals: + - fruit=apple + - vegetable=broccoli + envs: + - foo.env + files: + - passphrase=phrase.dat + - forces.txt + env: bar.env +buildMetadata: [originAnnotations] +`) + th.WriteF("foo.env", ` +MOUNTAIN=everest +OCEAN=pacific +`) + th.WriteF("bar.env", ` +BIRD=falcon +`) + th.WriteF("phrase.dat", ` +Life is short. +But the years are long. +Not while the evil days come not. +`) + th.WriteF("forces.txt", ` +gravitational +electromagnetic +strong nuclear +weak nuclear +`) + opts := th.MakeDefaultOptions() + m := th.Run(".", opts) + th.AssertActualEqualsExpected( + m, ` +apiVersion: v1 +data: + BIRD: falcon + MOUNTAIN: everest + OCEAN: pacific + forces.txt: |2 + + gravitational + electromagnetic + strong nuclear + weak nuclear + fruit: apple + passphrase: |2 + + Life is short. + But the years are long. + Not while the evil days come not. + vegetable: broccoli +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: blah-bob-g9df72cd5b +--- +apiVersion: v1 +data: + druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize": + 32000000000, "freeSpacePercent": 1.0}]' + v2: '[{"path": "var/druid/segment-cache"}]' +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: blah-json-5298bc8g99 +--- +apiVersion: v1 +data: + BIRD: ZmFsY29u + MOUNTAIN: ZXZlcmVzdA== + OCEAN: cGFjaWZpYw== + forces.txt: | + CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn + VjbGVhcgo= + fruit: YXBwbGU= + passphrase: | + CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG + UgZXZpbCBkYXlzIGNvbWUgbm90Lgo= + vegetable: YnJvY2NvbGk= +kind: Secret +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: SecretGenerator + name: blah-bob-58g62h555c +type: Opaque +`) +} + +func TestAnnoOriginLocalBuiltinGenerator(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- service.yaml +configMapGenerator: +- name: bob + literals: + - fruit=Indian Gooseberry + - year=2020 + - crisis=true +buildMetadata: [originAnnotations] + +`) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: demo +spec: + clusterIP: None +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected( + m, ` +apiVersion: v1 +kind: Service +metadata: + annotations: + config.kubernetes.io/origin: | + path: service.yaml + name: demo +spec: + clusterIP: None +--- +apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + year: "2020" +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: bob-79t79mt227 +`) +} + +func TestAnnoOriginConfigMapGeneratorMerge(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +configMapGenerator: +- name: bob + literals: + - fruit=Indian Gooseberry + - year=2020 + - crisis=true +`) + th.WriteK("overlay", ` +resources: +- ../base +configMapGenerator: +- name: bob + behavior: merge + literals: + - month=12 +buildMetadata: [originAnnotations] +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, `apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + month: "12" + year: "2020" +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: ../base/kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: bob-bk46gm59c6 +`) +} + +func TestAnnoOriginConfigMapGeneratorReplace(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +configMapGenerator: +- name: bob + literals: + - fruit=Indian Gooseberry + - year=2020 + - crisis=true +`) + th.WriteK("overlay", ` +resources: +- ../base +configMapGenerator: +- name: bob + behavior: replace + literals: + - month=12 +buildMetadata: [originAnnotations] +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, `apiVersion: v1 +data: + month: "12" +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: bob-f8t5fhtbhc +`) +} + +func TestAnnoOriginCustomExecGenerator(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + + th := kusttest_test.MakeHarnessWithFs(t, fSys) + o := th.MakeOptionsPluginsEnabled() + o.PluginConfig.FnpLoadingOptions.EnableExec = true + + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + th.WriteK(tmpDir.String(), ` +resources: +- short_secret.yaml +generators: +- gener.yaml +buildMetadata: [originAnnotations] +`) + + // Create some additional resource just to make sure everything is added + th.WriteF(filepath.Join(tmpDir.String(), "short_secret.yaml"), + ` +apiVersion: v1 +kind: Secret +metadata: + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +type: Opaque +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +`) + th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh) + + assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777)) + th.WriteF(filepath.Join(tmpDir.String(), "gener.yaml"), ` +kind: executable +metadata: + name: demo + annotations: + config.kubernetes.io/function: | + exec: + path: ./generateDeployment.sh +spec: +`) + + m := th.Run(tmpDir.String(), o) + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Secret +metadata: + annotations: + config.kubernetes.io/origin: | + path: short_secret.yaml + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +type: Opaque +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: gener.yaml + configuredBy: + kind: executable + name: demo + tshirt-size: small + labels: + app: nginx + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestAnnoOriginCustomInlineExecGenerator(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + + th := kusttest_test.MakeHarnessWithFs(t, fSys) + o := th.MakeOptionsPluginsEnabled() + o.PluginConfig.FnpLoadingOptions.EnableExec = true + + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + th.WriteK(tmpDir.String(), ` +resources: +- short_secret.yaml +generators: +- |- + kind: executable + metadata: + name: demo + annotations: + config.kubernetes.io/function: | + exec: + path: ./generateDeployment.sh + spec: +buildMetadata: [originAnnotations] +`) + + // Create some additional resource just to make sure everything is added + th.WriteF(filepath.Join(tmpDir.String(), "short_secret.yaml"), + ` +apiVersion: v1 +kind: Secret +metadata: + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +type: Opaque +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +`) + th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh) + assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777)) + m := th.Run(tmpDir.String(), o) + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Secret +metadata: + annotations: + config.kubernetes.io/origin: | + path: short_secret.yaml + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +type: Opaque +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + kind: executable + name: demo + tshirt-size: small + labels: + app: nginx + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestAnnoOriginCustomExecGeneratorWithOverlay(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + + th := kusttest_test.MakeHarnessWithFs(t, fSys) + o := th.MakeOptionsPluginsEnabled() + o.PluginConfig.FnpLoadingOptions.EnableExec = true + + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + base := filepath.Join(tmpDir.String(), "base") + prod := filepath.Join(tmpDir.String(), "prod") + assert.NoError(t, fSys.Mkdir(base)) + assert.NoError(t, fSys.Mkdir(prod)) + th.WriteK(base, ` +resources: +- short_secret.yaml +generators: +- gener.yaml +`) + th.WriteK(prod, ` +resources: +- ../base +buildMetadata: [originAnnotations] +`) + th.WriteF(filepath.Join(base, "short_secret.yaml"), + ` +apiVersion: v1 +kind: Secret +metadata: + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +type: Opaque +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +`) + th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh) + + assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777)) + th.WriteF(filepath.Join(base, "gener.yaml"), ` +kind: executable +metadata: + name: demo + annotations: + config.kubernetes.io/function: | + exec: + path: ./generateDeployment.sh +spec: +`) + + m := th.Run(prod, o) + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Secret +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/short_secret.yaml + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +type: Opaque +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: ../base/gener.yaml + configuredBy: + kind: executable + name: demo + tshirt-size: small + labels: + app: nginx + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestAnnoOriginCustomInlineExecGeneratorWithOverlay(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + + th := kusttest_test.MakeHarnessWithFs(t, fSys) + o := th.MakeOptionsPluginsEnabled() + o.PluginConfig.FnpLoadingOptions.EnableExec = true + + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + base := filepath.Join(tmpDir.String(), "base") + prod := filepath.Join(tmpDir.String(), "prod") + assert.NoError(t, fSys.Mkdir(base)) + assert.NoError(t, fSys.Mkdir(prod)) + th.WriteK(base, ` +resources: +- short_secret.yaml +generators: +- |- + kind: executable + metadata: + name: demo + annotations: + config.kubernetes.io/function: | + exec: + path: ./generateDeployment.sh + spec: +`) + th.WriteK(prod, ` +resources: +- ../base +buildMetadata: [originAnnotations] +`) + th.WriteF(filepath.Join(base, "short_secret.yaml"), + ` +apiVersion: v1 +kind: Secret +metadata: + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +type: Opaque +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +`) + th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh) + assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777)) + m := th.Run(prod, o) + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Secret +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/short_secret.yaml + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +type: Opaque +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: ../base/kustomization.yaml + configuredBy: + kind: executable + name: demo + tshirt-size: small + labels: + app: nginx + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestAnnoOriginRemoteBuiltinGenerator(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(` +resources: +- github.com/kubernetes-sigs/kustomize/examples/ldap/base/?ref=v1.0.6 +buildMetadata: [originAnnotations] +`))) + m, err := b.Run( + fSys, + tmpDir.String()) + if utils.IsErrTimeout(err) { + // Don't fail on timeouts. + t.SkipNow() + } + if !assert.NoError(t, err) { + t.FailNow() + } + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Contains(t, string(yml), `kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + repo: https://github.com/kubernetes-sigs/kustomize + ref: v1.0.6 + configuredIn: examples/ldap/base/kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: ldap-configmap-4d7m6k5b42`) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestAnnoOriginInlineBuiltinGenerator(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- service.yaml +generators: +- |- + apiVersion: builtin + kind: ConfigMapGenerator + metadata: + name: notImportantHere + name: bob + literals: + - fruit=Indian Gooseberry + - year=2020 + - crisis=true +buildMetadata: [originAnnotations] +`) + + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: apple +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + annotations: + config.kubernetes.io/origin: | + path: service.yaml + name: apple +--- +apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + year: "2020" +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: notImportantHere + name: bob-79t79mt227 +`) +} + +func TestAnnoOriginGeneratorFromFile(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- service.yaml +generators: +- configmap.yaml +buildMetadata: [originAnnotations] +`) + th.WriteF("configmap.yaml", ` +apiVersion: builtin +kind: ConfigMapGenerator +metadata: + name: notImportantHere +name: bob +literals: +- fruit=Indian Gooseberry +- year=2020 +- crisis=true +`) + th.WriteF("service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: apple +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + annotations: + config.kubernetes.io/origin: | + path: service.yaml + name: apple +--- +apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + year: "2020" +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: configmap.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: notImportantHere + name: bob-79t79mt227 +`) +} + +func TestAnnoOriginBuiltinGeneratorFromFileWithOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +resources: +- short_secret.yaml +generators: +- configmap.yaml +`) + th.WriteF("base/configmap.yaml", `apiVersion: builtin +kind: ConfigMapGenerator +metadata: + name: notImportantHere +name: bob +literals: +- fruit=Indian Gooseberry +- year=2020 +- crisis=true +`) + th.WriteK("prod", ` +resources: +- ../base +buildMetadata: [originAnnotations] +`) + th.WriteF("base/short_secret.yaml", + ` +apiVersion: v1 +kind: Secret +metadata: + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +type: Opaque +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +`) + m := th.Run("prod", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, `apiVersion: v1 +kind: Secret +metadata: + annotations: + config.kubernetes.io/origin: | + path: ../base/short_secret.yaml + labels: + airshipit.org/ephemeral-user-data: "true" + name: node1-bmc-secret +stringData: + userData: | + bootcmd: + - mkdir /mnt/vda +type: Opaque +--- +apiVersion: v1 +data: + crisis: "true" + fruit: Indian Gooseberry + year: "2020" +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: ../base/configmap.yaml + configuredBy: + apiVersion: builtin + kind: ConfigMapGenerator + name: notImportantHere + name: bob-79t79mt227 +`) +} + +func TestAnnoOriginGeneratorInTransformersField(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + + th := kusttest_test.MakeHarnessWithFs(t, fSys) + o := th.MakeOptionsPluginsEnabled() + o.PluginConfig.FnpLoadingOptions.EnableExec = true + + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + th.WriteK(tmpDir.String(), ` +transformers: +- gener.yaml +buildMetadata: [originAnnotations] +`) + + th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh) + + assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777)) + th.WriteF(filepath.Join(tmpDir.String(), "gener.yaml"), ` +kind: executable +metadata: + name: demo + annotations: + config.kubernetes.io/function: | + exec: + path: ./generateDeployment.sh +spec: +`) + + m := th.Run(tmpDir.String(), o) + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + config.kubernetes.io/origin: | + configuredIn: gener.yaml + configuredBy: + kind: executable + name: demo + tshirt-size: small + labels: + app: nginx + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestAnnoOriginGeneratorInTransformersFieldWithOverlay(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + + th := kusttest_test.MakeHarnessWithFs(t, fSys) + o := th.MakeOptionsPluginsEnabled() + o.PluginConfig.FnpLoadingOptions.EnableExec = true + + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + base := filepath.Join(tmpDir.String(), "base") + prod := filepath.Join(tmpDir.String(), "prod") + assert.NoError(t, fSys.Mkdir(base)) + assert.NoError(t, fSys.Mkdir(prod)) + + th.WriteK(base, ` +transformers: +- gener.yaml +`) + + th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh) + + assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777)) + th.WriteF(filepath.Join(base, "gener.yaml"), ` +kind: executable +metadata: + name: demo + annotations: + config.kubernetes.io/function: | + exec: + path: ./generateDeployment.sh +spec: +`) + th.WriteK(prod, ` +resources: +- ../base +nameSuffix: -foo +buildMetadata: [originAnnotations, transformerAnnotations] +`) + + m := th.Run(prod, o) + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + alpha.config.kubernetes.io/transformations: | + - configuredIn: kustomization.yaml + configuredBy: + apiVersion: builtin + kind: SuffixTransformer + config.kubernetes.io/origin: | + configuredIn: ../base/gener.yaml + configuredBy: + kind: executable + name: demo + tshirt-size: small + labels: + app: nginx + name: nginx-foo +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/patchdelete_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/patchdelete_test.go new file mode 100644 index 000000000..148f50d1b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/patchdelete_test.go @@ -0,0 +1,74 @@ +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestPatchDeleteOfNotExistingAttributesShouldNotAddExtraElements(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteF("resource.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whatever +spec: + template: + spec: + containers: + - env: + - name: EXISTING + value: EXISTING_VALUE + - name: FOR_REMOVAL + value: FOR_REMOVAL_VALUE + name: whatever + image: helloworld +`) + th.WriteF("patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whatever +spec: + template: + spec: + containers: + - name: whatever + env: + - name: FOR_REMOVAL + $patch: delete + - name: NOT_EXISTING_FOR_REMOVAL + $patch: delete +`) + th.WriteK(".", ` +resources: +- resource.yaml +patches: +- path: patch.yaml + target: + kind: Deployment +`) + + // It's expected that removal of not existing elements should not introduce extra values, + // as a patch can be applied to multiple resources, not all of them can have all the elements being deleted. + expected := ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whatever +spec: + template: + spec: + containers: + - env: + - name: EXISTING + value: EXISTING_VALUE + image: helloworld + name: whatever +` + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expected) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/pluginenv_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/pluginenv_test.go new file mode 100644 index 000000000..a4d8c230c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/pluginenv_test.go @@ -0,0 +1,79 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "sigs.k8s.io/kustomize/api/konfig" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// The PrintPluginEnv plugin is a toy plugin that emits +// its working directory and some environment variables, +// to add regression protection to plugin loading logic. +func TestPluginEnvironment(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t). + PrepExecPlugin( + "someteam.example.com", "v1", "PrintPluginEnv") + defer th.Reset() + + confirmBehavior( + kusttest_test.MakeHarnessWithFs(t, filesys.MakeFsInMemory()), + filesys.Separator) + + dir := makeTmpDir(t) + defer os.RemoveAll(dir) + confirmBehavior( + kusttest_test.MakeHarnessWithFs(t, filesys.MakeFsOnDisk()), + dir) +} + +func confirmBehavior(th kusttest_test.Harness, dir string) { + th.WriteK(dir, ` +generators: +- config.yaml +`) + th.WriteF(filepath.Join(dir, "config.yaml"), ` +apiVersion: someteam.example.com/v1 +kind: PrintPluginEnv +metadata: + name: irrelevantHere +`) + m := th.Run(dir, th.MakeOptionsPluginsEnabled()) + + pHome, ok := os.LookupEnv(konfig.KustomizePluginHomeEnv) + if !ok { + th.GetT().Fatalf( + "expected env var '%s' to be defined", + konfig.KustomizePluginHomeEnv) + } + + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +env: + kustomize_plugin_config_root: `+dir+` + kustomize_plugin_home: `+pHome+` + pwd: `+dir+` +kind: GeneratedEnv +metadata: + name: hello +`) +} + +func makeTmpDir(t *testing.T) string { + base, err := os.Getwd() + if err != nil { + t.Fatalf("err %v", err) + } + dir, err := ioutil.TempDir(base, "kustomize-tmp-test-") + if err != nil { + t.Fatalf("err %v", err) + } + return dir +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/poddisruptionbudget_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/poddisruptionbudget_test.go new file mode 100644 index 000000000..4f423fb96 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/poddisruptionbudget_test.go @@ -0,0 +1,127 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestPodDisruptionBudgetBasics(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("pdbLiteral.yaml", ` +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: pdbLiteral +spec: + maxUnavailable: 90 +`) + th.WriteF("pdbPercentage.yaml", ` +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: pdbPercentage +spec: + maxUnavailable: 90% +`) + th.WriteK(".", ` +resources: +- pdbLiteral.yaml +- pdbPercentage.yaml +`) + m := th.Run(".", th.MakeDefaultOptions()) + // In a PodDisruptionBudget, the fields maxUnavailable + // minAvailable are mutually exclusive, and both can hold + // either an integer, i.e. 10, or string that has to be + // an int followed by a percent sign, e.g. 10%. + th.AssertActualEqualsExpected(m, ` +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: pdbLiteral +spec: + maxUnavailable: 90 +--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: pdbPercentage +spec: + maxUnavailable: 90% +`) +} + +func TestPodDisruptionBudgetMerging(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("pdb-patch.yaml", ` +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: generic-pdb +spec: + maxUnavailable: 1 +`) + th.WriteF("my_file.yaml", ` +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: championships-api + labels: + faceit-pdb: default +spec: + maxUnavailable: 100% +--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: championships-api-2 + labels: + faceit-pdb: default +spec: + maxUnavailable: 100% +`) + th.WriteK(".", ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: +- path: pdb-patch.yaml + target: + kind: PodDisruptionBudget + labelSelector: faceit-pdb=default + +resources: +- my_file.yaml +`) + m := th.Run(".", th.MakeDefaultOptions()) + // In a PodDisruptionBudget, the fields maxUnavailable + // minAvailable are mutually exclusive, and both can hold + // either an integer, i.e. 10, or string that has to be + // an int followed by a percent sign, e.g. 10%. + // In the former case - bare integer - they should NOT be quoted + // as the api server will reject it. In the latter case with + // the percent sign, quotes can be added and the API server will + // accept it, but they don't have to be added. + th.AssertActualEqualsExpected( + m, ` +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + labels: + faceit-pdb: default + name: championships-api +spec: + maxUnavailable: 1 +--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + labels: + faceit-pdb: default + name: championships-api-2 +spec: + maxUnavailable: 1 +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/remoteload_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/remoteload_test.go new file mode 100644 index 000000000..081c0c301 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/remoteload_test.go @@ -0,0 +1,191 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "fmt" + "net/http" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestRemoteLoad(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + m, err := b.Run( + fSys, + "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6") + if utils.IsErrTimeout(err) { + // Don't fail on timeouts. + t.SkipNow() + } + if !assert.NoError(t, err) { + t.FailNow() + } + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Pod +metadata: + labels: + app: myapp + name: dev-myapp-pod +spec: + containers: + - image: nginx:1.7.9 + name: nginx +`, string(yml)) +} + +func TestRemoteResource(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(` +resources: +- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6 +`))) + m, err := b.Run( + fSys, + tmpDir.String()) + if utils.IsErrTimeout(err) { + // Don't fail on timeouts. + t.SkipNow() + } + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Pod +metadata: + labels: + app: myapp + name: dev-myapp-pod +spec: + containers: + - image: nginx:1.7.9 + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestRemoteResourceWithHTTPError(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + + url404 := "https://github.com/thisisa404.yaml" + kusto := filepath.Join(tmpDir.String(), "kustomization.yaml") + assert.NoError(t, fSys.WriteFile(kusto, []byte(fmt.Sprintf(` +resources: +- %s +`, url404)))) + + _, err = b.Run(fSys, tmpDir.String()) + if utils.IsErrTimeout(err) { + // Don't fail on timeouts. + t.SkipNow() + } + + httpErr := fmt.Errorf("%w: status code %d (%s)", loader.ErrorHTTP, 404, http.StatusText(404)) + accuFromErr := fmt.Errorf("accumulating resources from '%s': %w", url404, httpErr) + expectedErr := fmt.Errorf("accumulating resources: %w", accuFromErr) + assert.EqualErrorf(t, err, expectedErr.Error(), url404) +} + +func TestRemoteResourceAnnoOrigin(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(` +resources: +- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6 +buildMetadata: [originAnnotations] +`))) + m, err := b.Run( + fSys, + tmpDir.String()) + if utils.IsErrTimeout(err) { + // Don't fail on timeouts. + t.SkipNow() + } + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Pod +metadata: + annotations: + config.kubernetes.io/origin: | + path: examples/multibases/base/pod.yaml + repo: https://github.com/kubernetes-sigs/kustomize + ref: v1.0.6 + labels: + app: myapp + name: dev-myapp-pod +spec: + containers: + - image: nginx:1.7.9 + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} + +func TestRemoteResourceAsBaseWithAnnoOrigin(t *testing.T) { + fSys := filesys.MakeFsOnDisk() + b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + tmpDir, err := filesys.NewTmpConfirmedDir() + assert.NoError(t, err) + base := filepath.Join(tmpDir.String(), "base") + prod := filepath.Join(tmpDir.String(), "prod") + assert.NoError(t, fSys.Mkdir(base)) + assert.NoError(t, fSys.Mkdir(prod)) + assert.NoError(t, fSys.WriteFile(filepath.Join(base, "kustomization.yaml"), []byte(` +resources: +- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6 +`))) + assert.NoError(t, fSys.WriteFile(filepath.Join(prod, "kustomization.yaml"), []byte(` +resources: +- ../base +namePrefix: prefix- +buildMetadata: [originAnnotations] +`))) + + m, err := b.Run( + fSys, + prod) + if utils.IsErrTimeout(err) { + // Don't fail on timeouts. + t.SkipNow() + } + assert.NoError(t, err) + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Pod +metadata: + annotations: + config.kubernetes.io/origin: | + path: examples/multibases/base/pod.yaml + repo: https://github.com/kubernetes-sigs/kustomize + ref: v1.0.6 + labels: + app: myapp + name: prefix-dev-myapp-pod +spec: + containers: + - image: nginx:1.7.9 + name: nginx +`, string(yml)) + assert.NoError(t, fSys.RemoveAll(tmpDir.String())) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/repeatbase_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/repeatbase_test.go new file mode 100644 index 000000000..438b7af28 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/repeatbase_test.go @@ -0,0 +1,177 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "strings" + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// This is broken since kustomize v3.9.3. +// See https://github.com/kubernetes-sigs/kustomize/issues/3609 for details. + +// Here is a structure of a kustomization of one resource inheriting from +// two bases. One of those bases is shared between the canary base and the +// final resource. This is named canary as it is a simple pattern to +// duplicate a resource that can be used with canary deployments. +// +// base +// | deployment.yaml +// | kustomization.yaml +// canary +// | deployment-canary-patch.yaml +// | kustomization.yaml +// mango +// | deployment-mango-patch.yaml +// | deployment-mango-canary-patch.yaml +// | kustomization.yaml +func TestRepeatBase(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +resources: + - deployment.yaml +`) + th.WriteF("base/deployment.yaml", ` +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: banana +spec: + selector: + matchLabels: + component: banana + template: + metadata: + labels: + component: banana + spec: + containers: + - name: banana + image: image +`) + + th.WriteK("canary", ` +resources: + - ../base +patches: +- patch: | + - op: replace + path: /metadata/name + value: banana-canary + target: + kind: Deployment +- path: deployment-canary-patch.yaml +`) + th.WriteF("canary/deployment-canary-patch.yaml", ` +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: banana-canary + labels: + type: canary +spec: + selector: + matchLabels: + component: banana + type: canary + template: + metadata: + labels: + component: banana + type: canary + spec: + containers: + - name: banana + image: image-canary +`) + + th.WriteK("mango", ` +nameSuffix: -mango +resources: + - ../base + - ../canary +patches: +- path: deployment-mango-base-patch.yaml +- path: deployment-mango-canary-patch.yaml +`) + th.WriteF("mango/deployment-mango-base-patch.yaml", ` +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: banana +spec: + template: + spec: + containers: + - name: banana + image: image-mango +`) + th.WriteF("mango/deployment-mango-canary-patch.yaml", ` +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: banana-canary +spec: + template: + spec: + containers: + - name: banana + image: image-canary-mango +`) + + err := th.RunWithErr("mango", th.MakeDefaultOptions()) + if !strings.Contains( + err.Error(), "multiple matches for Id Deployment.v1.apps/banana.[noNs]; failed to find unique target for patch Deployment.v1.apps/banana.[noNs]") { + t.Fatalf("Unexpected err: %v", err) + } + + // Uncomenting the following makes it work with kustomize v3.9.2 and bellow. + + // m := th.Run("/app", th.MakeDefaultOptions()) + // th.AssertActualEqualsExpected(m, ` + //apiVersion: apps/v1 + //kind: Deployment + //metadata: + // name: deployment-a + //spec: + // selector: + // matchLabels: + // component: deployment + // template: + // metadata: + // labels: + // component: deployment + // spec: + // containers: + // - image: image-a + // name: container-a + //--- + //apiVersion: apps/v1 + //kind: Deployment + //metadata: + // labels: + // type: canary + // name: deployment-canary-a + //spec: + // selector: + // matchLabels: + // component: deployment + // type: canary + // template: + // metadata: + // labels: + // component: deployment + // spec: + // containers: + // - image: image-canary-a + // name: container-a + //`) + +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/replacementtransformer_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/replacementtransformer_test.go new file mode 100644 index 000000000..15c088818 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/replacementtransformer_test.go @@ -0,0 +1,484 @@ +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// end to end tests to demonstrate functionality of ReplacementTransformer +func TestReplacementsField(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK(".", ` +resources: +- resource.yaml + +replacements: +- source: + kind: Deployment + fieldPath: spec.template.spec.containers.0.image + targets: + - select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers.1.image +`) + th.WriteF("resource.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb +`) + expected := ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: foobar:1 + name: replaced-with-digest + - image: foobar:1 + name: postgresdb +` + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expected) +} + +func TestReplacementsFieldWithPath(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK(".", ` +resources: +- resource.yaml + +replacements: +- path: replacement.yaml +`) + th.WriteF("replacement.yaml", ` +source: + kind: Deployment + fieldPath: spec.template.spec.containers.0.image +targets: +- select: + kind: Deployment + fieldPaths: + - spec.template.spec.containers.1.image +`) + th.WriteF("resource.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb +`) + expected := ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: foobar:1 + name: replaced-with-digest + - image: foobar:1 + name: postgresdb +` + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, expected) +} + +func TestReplacementTransformerWithDiamondShape(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteF("base/deployments.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sourceA +spec: + template: + spec: + containers: + - image: nginx:newtagA + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sourceB +spec: + template: + spec: + containers: + - image: nginx:newtagB + name: nginx +`) + th.WriteK("base", ` +resources: +- deployments.yaml +`) + th.WriteK("a", ` +namePrefix: a- +resources: +- ../base +replacements: +- path: replacement.yaml +`) + th.WriteF("a/replacement.yaml", ` +source: + name: a-sourceA + fieldPath: spec.template.spec.containers.0.image +targets: +- select: + name: a-deploy + fieldPaths: + - spec.template.spec.containers.[name=nginx].image +`) + th.WriteK("b", ` +namePrefix: b- +resources: +- ../base +replacements: +- path: replacement.yaml +`) + th.WriteF("b/replacement.yaml", ` +source: + name: b-sourceB + fieldPath: spec.template.spec.containers.0.image +targets: +- select: + name: b-deploy + fieldPaths: + - spec.template.spec.containers.[name=nginx].image +`) + th.WriteK(".", ` +resources: +- a +- b +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: a-deploy +spec: + template: + spec: + containers: + - image: nginx:newtagA + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: a-sourceA +spec: + template: + spec: + containers: + - image: nginx:newtagA + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: a-sourceB +spec: + template: + spec: + containers: + - image: nginx:newtagB + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-deploy +spec: + template: + spec: + containers: + - image: nginx:newtagB + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-sourceA +spec: + template: + spec: + containers: + - image: nginx:newtagA + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: b-sourceB +spec: + template: + spec: + containers: + - image: nginx:newtagB + name: nginx +`) +} + +func TestReplacementTransformerWithOriginalName(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteF("base/deployments.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: target +spec: + template: + spec: + containers: + - image: nginx:oldtag + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: source +spec: + template: + spec: + containers: + - image: nginx:newtag + name: nginx +`) + th.WriteK("base", ` +resources: +- deployments.yaml +`) + th.WriteK("overlay", ` +namePrefix: prefix1- +resources: +- ../base +`) + + th.WriteK(".", ` +namePrefix: prefix2- +resources: +- overlay +replacements: +- path: replacement.yaml +`) + th.WriteF("replacement.yaml", ` +source: + name: source + fieldPath: spec.template.spec.containers.0.image +targets: +- select: + name: prefix1-target + fieldPaths: + - spec.template.spec.containers.[name=nginx].image +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prefix2-prefix1-target +spec: + template: + spec: + containers: + - image: nginx:newtag + name: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prefix2-prefix1-source +spec: + template: + spec: + containers: + - image: nginx:newtag + name: nginx +`) +} + +// TODO: Address namePrefix in overlay not applying to replacement targets +// The property `data.blue-name` should end up being `overlay-blue` instead of `blue` +// https://github.com/kubernetes-sigs/kustomize/issues/4034 +func TestReplacementTransformerWithNamePrefixOverlay(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK("base", ` +generatorOptions: + disableNameSuffixHash: true +configMapGenerator: +- name: blue +- name: red +replacements: +- source: + kind: ConfigMap + name: blue + fieldPath: metadata.name + targets: + - select: + name: red + fieldPaths: + - data.blue-name + options: + create: true +`) + + th.WriteK(".", ` +namePrefix: overlay- +resources: +- base +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: overlay-blue +--- +apiVersion: v1 +data: + blue-name: blue +kind: ConfigMap +metadata: + name: overlay-red +`) +} + +// TODO: Address namespace in overlay not applying to replacement targets +// The property `data.blue-namespace` should end up being `overlay-namespace` instead of `base-namespace` +// https://github.com/kubernetes-sigs/kustomize/issues/4034 +func TestReplacementTransformerWithNamespaceOverlay(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK("base", ` +namespace: base-namespace +generatorOptions: + disableNameSuffixHash: true +configMapGenerator: +- name: blue +- name: red +replacements: +- source: + kind: ConfigMap + name: blue + fieldPath: metadata.namespace + targets: + - select: + name: red + fieldPaths: + - data.blue-namespace + options: + create: true +`) + + th.WriteK(".", ` +namespace: overlay-namespace +resources: +- base +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: blue + namespace: overlay-namespace +--- +apiVersion: v1 +data: + blue-namespace: base-namespace +kind: ConfigMap +metadata: + name: red + namespace: overlay-namespace +`) +} + +// TODO: Address configMapGenerator suffix not applying to replacement targets +// The property `data.blue-name` should end up being `blue-6ct58987ht` instead of `blue` +// https://github.com/kubernetes-sigs/kustomize/issues/4034 +func TestReplacementTransformerWithConfigMapGenerator(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK(".", ` +configMapGenerator: +- name: blue +- name: red +replacements: +- source: + kind: ConfigMap + name: blue + fieldPath: metadata.name + targets: + - select: + name: red + fieldPaths: + - data.blue-name + options: + create: true +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: blue-6ct58987ht +--- +apiVersion: v1 +data: + blue-name: blue +kind: ConfigMap +metadata: + name: red-dc6gc5btkc +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/resourceconflict_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/resourceconflict_test.go new file mode 100644 index 000000000..713e21feb --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/resourceconflict_test.go @@ -0,0 +1,368 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "strings" + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func writeBase(th kusttest_test.Harness) { + th.WriteK("base", ` +resources: +- serviceaccount.yaml +- rolebinding.yaml +- clusterrolebinding.yaml +- clusterrole.yaml +namePrefix: pfx- +nameSuffix: -sfx +`) + th.WriteF("base/serviceaccount.yaml", ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: serviceaccount +`) + th.WriteF("base/rolebinding.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: role +subjects: +- kind: ServiceAccount + name: serviceaccount +`) + th.WriteF("base/clusterrolebinding.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: role +subjects: +- kind: ServiceAccount + name: serviceaccount +`) + th.WriteF("base/clusterrole.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: role +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] +`) +} + +func writeMidOverlays(th kusttest_test.Harness) { + // Mid-level overlays + th.WriteK("overlays/a", ` +resources: +- ../../base +namePrefix: a- +nameSuffix: -suffixA +`) + th.WriteK("overlays/b", ` +resources: +- ../../base +namePrefix: b- +nameSuffix: -suffixB +`) +} + +func writeTopOverlay(th kusttest_test.Harness) { + // Top overlay, combining the mid-level overlays + th.WriteK("combined", ` +resources: +- ../overlays/a +- ../overlays/b +`) +} + +func TestBase(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBase(th) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: pfx-serviceaccount-sfx +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: pfx-rolebinding-sfx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: pfx-role-sfx +subjects: +- kind: ServiceAccount + name: pfx-serviceaccount-sfx +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: pfx-rolebinding-sfx +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: pfx-role-sfx +subjects: +- kind: ServiceAccount + name: pfx-serviceaccount-sfx +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: pfx-role-sfx +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - watch + - list +`) +} + +func TestMidLevelA(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBase(th) + writeMidOverlays(th) + m := th.Run("overlays/a", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: a-pfx-serviceaccount-sfx-suffixA +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: a-pfx-rolebinding-sfx-suffixA +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: a-pfx-role-sfx-suffixA +subjects: +- kind: ServiceAccount + name: a-pfx-serviceaccount-sfx-suffixA +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: a-pfx-rolebinding-sfx-suffixA +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: a-pfx-role-sfx-suffixA +subjects: +- kind: ServiceAccount + name: a-pfx-serviceaccount-sfx-suffixA +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: a-pfx-role-sfx-suffixA +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - watch + - list +`) +} + +func TestMidLevelB(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBase(th) + writeMidOverlays(th) + m := th.Run("overlays/b", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: b-pfx-serviceaccount-sfx-suffixB +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: b-pfx-rolebinding-sfx-suffixB +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: b-pfx-role-sfx-suffixB +subjects: +- kind: ServiceAccount + name: b-pfx-serviceaccount-sfx-suffixB +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: b-pfx-rolebinding-sfx-suffixB +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: b-pfx-role-sfx-suffixB +subjects: +- kind: ServiceAccount + name: b-pfx-serviceaccount-sfx-suffixB +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: b-pfx-role-sfx-suffixB +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - watch + - list +`) +} + +func TestMultibasesNoConflict(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBase(th) + writeMidOverlays(th) + writeTopOverlay(th) + m := th.Run("combined", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: a-pfx-serviceaccount-sfx-suffixA +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: a-pfx-rolebinding-sfx-suffixA +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: a-pfx-role-sfx-suffixA +subjects: +- kind: ServiceAccount + name: a-pfx-serviceaccount-sfx-suffixA +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: a-pfx-rolebinding-sfx-suffixA +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: a-pfx-role-sfx-suffixA +subjects: +- kind: ServiceAccount + name: a-pfx-serviceaccount-sfx-suffixA +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: a-pfx-role-sfx-suffixA +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - watch + - list +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: b-pfx-serviceaccount-sfx-suffixB +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: b-pfx-rolebinding-sfx-suffixB +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: b-pfx-role-sfx-suffixB +subjects: +- kind: ServiceAccount + name: b-pfx-serviceaccount-sfx-suffixB +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: b-pfx-rolebinding-sfx-suffixB +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: b-pfx-role-sfx-suffixB +subjects: +- kind: ServiceAccount + name: b-pfx-serviceaccount-sfx-suffixB +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: b-pfx-role-sfx-suffixB +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - watch + - list +`) +} + +func TestMultibasesWithConflict(t *testing.T) { + th := kusttest_test.MakeHarness(t) + writeBase(th) + writeMidOverlays(th) + writeTopOverlay(th) + + th.WriteK("overlays/a", ` +namePrefix: a- +nameSuffix: -suffixA +resources: +- serviceaccount.yaml +- ../../base +`) + // Expect an error because this resource in the overlay + // matches a resource in the base. + th.WriteF("overlays/a/serviceaccount.yaml", ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: serviceaccount +`) + + err := th.RunWithErr("combined", th.MakeDefaultOptions()) + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), "found multiple possible referrals") { + t.Fatalf("unexpected error %v", err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/rolebindingacrossnamespace_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/rolebindingacrossnamespace_test.go new file mode 100644 index 000000000..e446c890a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/rolebindingacrossnamespace_test.go @@ -0,0 +1,344 @@ +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestRoleBindingAcrossNamespace(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK(".", ` +resources: +- resource.yaml +nameSuffix: -ns2 +`) + th.WriteF("resource.yaml", ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa1 + namespace: ns1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa2 + namespace: ns2 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa3 + namespace: ns3 +--- +apiVersion: v1 +kind: NotServiceAccount +metadata: + name: my-nsa + namespace: ns1 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: my-role + namespace: ns2 +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: my-role-binding + namespace: ns2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: my-role +subjects: + - kind: ServiceAccount + name: my-sa1 + namespace: ns1 + - kind: ServiceAccount + name: my-sa2 + namespace: ns2 + - kind: ServiceAccount + name: my-sa3 + namespace: ns3 + - kind: NotServiceAccount + name: my-nsa + namespace: ns1 +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa1-ns2 + namespace: ns1 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa2-ns2 + namespace: ns2 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa3-ns2 + namespace: ns3 +--- +apiVersion: v1 +kind: NotServiceAccount +metadata: + name: my-nsa-ns2 + namespace: ns1 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: my-role-ns2 + namespace: ns2 +rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: my-role-binding-ns2 + namespace: ns2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: my-role-ns2 +subjects: +- kind: ServiceAccount + name: my-sa1-ns2 + namespace: ns1 +- kind: ServiceAccount + name: my-sa2-ns2 + namespace: ns2 +- kind: ServiceAccount + name: my-sa3-ns2 + namespace: ns3 +- kind: NotServiceAccount + name: my-nsa + namespace: ns1 +`) +} + +func TestRoleBindingAcrossNamespaceWoSubjects(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK(".", ` +resources: +- resource.yaml +nameSuffix: -ns2 +`) + th.WriteF("resource.yaml", ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa1 + namespace: ns1 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: my-role + namespace: ns2 +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: my-role-binding + namespace: ns2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: my-role +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: my-sa1-ns2 + namespace: ns1 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: my-role-ns2 + namespace: ns2 +rules: +- apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: my-role-binding-ns2 + namespace: ns2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: my-role-ns2 +`) +} + +// The ServiceAccount in subjects in role binding can be across namespace +// but the roleRef is not. This test is used to cover such case. +func TestRoleBindingWhenSubjectsAcrossNamespace(t *testing.T) { + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + th.WriteK(".", ` +resources: +- ./ns1 +- ./ns2 +`) + th.WriteK("ns1", ` +namespace: namespace-1 +resources: +- role-ns1.yaml +- rolebinding-ns1.yaml +`) + th.WriteF("ns1/role-ns1.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: testRole +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] +`) + th.WriteF("ns1/rolebinding-ns1.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: testRoleBinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: testRole +subjects: + - kind: ServiceAccount + name: testAccount + namespace: namespace-2 +`) + th.WriteK("ns2", ` +namespace: namespace-2 +resources: +- role-ns2.yaml +- rolebinding-ns2.yaml +`) + th.WriteF("ns2/role-ns2.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: testRole +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get"] +`) + th.WriteF("ns2/rolebinding-ns2.yaml", ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: testRoleBinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: testRole +subjects: + - kind: ServiceAccount + name: testAccount + namespace: namespace-1 +`) + + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: testRole + namespace: namespace-1 +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: testRoleBinding + namespace: namespace-1 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: testRole +subjects: +- kind: ServiceAccount + name: testAccount + namespace: namespace-2 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: testRole + namespace: namespace-2 +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: testRoleBinding + namespace: namespace-2 +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: testRole +subjects: +- kind: ServiceAccount + name: testAccount + namespace: namespace-1 +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/simple_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/simple_test.go new file mode 100644 index 000000000..c9f2cd985 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/simple_test.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +func TestSimple1(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("dep.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`) + th.WriteF("patch.yaml", ` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`) + + th.WriteK(".", ` +resources: +- dep.yaml +patchesStrategicMerge: +- patch.yaml +`) + m := th.Run(".", th.MakeDefaultOptions()) + + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/stringquoteblank_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/stringquoteblank_test.go new file mode 100644 index 000000000..1ed4fc14b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/stringquoteblank_test.go @@ -0,0 +1,82 @@ +package krusty_test + +import ( + "testing" + + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" +) + +// This test is for output string style. +// Currently all quotes will be removed if the string is valid as plain (unquoted) style. +// If a string cannot be unquoted, it will be put into a pair of single quotes. +// See https://yaml.org/spec/1.2/spec.html#id2788859 for more details about what kind of string +// is invalid as plain style. +func TestLongLineBreaks(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteF("deployment.yaml", ` +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: test +spec: + template: + spec: + containers: + - name: mariadb + image: test + env: + - name: SHORT_STRING + value: short_string + - name: SHORT_STRING_WITH_SINGLE_QUOTE + value: 'short_string' + - name: SHORT_STRING_WITH_DOUBLE_QUOTE + value: "short_string" + - name: SHORT_STRING_BLANK + value: short string + - name: LONG_STRING_BLANK + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius. + - name: LONG_STRING_BLANK_WITH_SINGLE_QUOTE + value: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius.' + - name: LONG_STRING_BLANK_WITH_DOUBLE_QUOTE + value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius." + - name: INVALID_PLAIN_STYLE_STRING + value: ': test' +`) + th.WriteK(".", ` +resources: +- deployment.yaml +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: test +spec: + template: + spec: + containers: + - env: + - name: SHORT_STRING + value: short_string + - name: SHORT_STRING_WITH_SINGLE_QUOTE + value: short_string + - name: SHORT_STRING_WITH_DOUBLE_QUOTE + value: short_string + - name: SHORT_STRING_BLANK + value: short string + - name: LONG_STRING_BLANK + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas + suscipit ex non molestie varius. + - name: LONG_STRING_BLANK_WITH_SINGLE_QUOTE + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas + suscipit ex non molestie varius. + - name: LONG_STRING_BLANK_WITH_DOUBLE_QUOTE + value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas + suscipit ex non molestie varius. + - name: INVALID_PLAIN_STYLE_STRING + value: ': test' + image: test + name: mariadb +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/customschema.json b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/customschema.json new file mode 100644 index 000000000..04903e225 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/customschema.json @@ -0,0 +1,123 @@ +{ + "definitions": { + "v1alpha1.MyCRD": { + "properties": { + "apiVersion": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "metadata": { + "type": "object" + }, + "spec": { + "properties": { + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec" + } + }, + "type": "object" + }, + "status": { + "properties": { + "success": { + "type": "boolean" + } + }, + "type": "object" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "example.com", + "kind": "MyCRD", + "version": "v1alpha1" + }, + { + "group": "", + "kind": "MyCRD", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.core.v1.PodTemplateSpec": { + "properties": { + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodSpec" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "properties": { + "name": { + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.PodSpec": { + "properties": { + "containers": { + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.Container": { + "properties": { + "command": { + "items": { + "type": "string" + }, + "type": "array" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ports": { + "items": { + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "containerPort", + "protocol" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, + "io.k8s.api.core.v1.ContainerPort": { + "properties": { + "containerPort": { + "format": "int32", + "type": "integer" + }, + "name": { + "type": "string" + }, + "protocol": { + "type": "string" + } + }, + "type": "object" + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/customschema.yaml b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/customschema.yaml new file mode 100644 index 000000000..0f46cba92 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/customschema.yaml @@ -0,0 +1,78 @@ +definitions: + v1alpha1.MyCRD: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + template: + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec" + type: object + status: + properties: + success: + type: boolean + type: object + type: object + x-kubernetes-group-version-kind: + - group: example.com + kind: MyCRD + version: v1alpha1 + - group: "" + kind: MyCRD + version: v1alpha1 + io.k8s.api.core.v1.PodTemplateSpec: + properties: + metadata: + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + spec: + "$ref": "#/definitions/io.k8s.api.core.v1.PodSpec" + type: object + io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta: + properties: + name: + type: string + type: object + io.k8s.api.core.v1.PodSpec: + properties: + containers: + items: + "$ref": "#/definitions/io.k8s.api.core.v1.Container" + type: array + x-kubernetes-patch-merge-key: name + x-kubernetes-patch-strategy: merge + type: object + io.k8s.api.core.v1.Container: + properties: + command: + items: + type: string + type: array + image: + type: string + name: + type: string + ports: + items: + "$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort" + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + x-kubernetes-patch-merge-key: containerPort + x-kubernetes-patch-strategy: merge + type: object + io.k8s.api.core.v1.ContainerPort: + properties: + containerPort: + type: integer + name: + type: string + protocol: + type: string + type: object diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/openshiftschema.json b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/openshiftschema.json new file mode 100644 index 000000000..b2efc185d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/testdata/openshiftschema.json @@ -0,0 +1,76 @@ +{ + "definitions": { + "com.github.openshift.api.apps.v1.DeploymentConfig": { + "type": "object", + "required": [ + "spec" + ], + "properties": { + "apiVersion": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/com.github.openshift.api.apps.v1.DeploymentConfigSpec" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "apps.openshift.io", + "kind": "DeploymentConfig", + "version": "v1" + } + ] + }, + "com.github.openshift.api.apps.v1.DeploymentConfigSpec": { + "type": "object", + "properties": { + "template": { + "$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec" + } + } + }, + "io.k8s.api.core.v1.Volume": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "configMap": { + "$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapVolumeSource" + }, + "name": { + "type": "string" + } + } + }, + "io.k8s.api.core.v1.VolumeMount": { + "type": "object", + "required": [ + "name", + "mountPath" + ], + "properties": { + "mountPath": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/transformerannotation_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/transformerannotation_test.go new file mode 100644 index 000000000..d385fb94d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/krusty/transformerannotation_test.go @@ -0,0 +1,495 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/krusty" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +const generateDeploymentWithOriginDotSh = `#!/bin/sh + +cat < + # + # All addresses used to contact a node must be specified in the --addresses arg. + # + # In addition to the node certificate and key, the init-certs entrypoint will symlink + # the cluster CA to the certs directory. + - name: init-certs + image: cockroachdb/cockroach-k8s-request-cert:0.2 + imagePullPolicy: IfNotPresent + command: + - "/bin/ash" + - "-ecx" + - "/request-cert" + - -namespace=${POD_NAMESPACE} + - -certs-dir=/cockroach-certs + - -type=node + - -addresses=localhost,127.0.0.1,${POD_IP},$(hostname -f),$(hostname -f|cut -f 1-2 -d '.'),$(CDB_PUBLIC_SVC) + - -symlink-ca-from=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + env: + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - name: certs + mountPath: /cockroach-certs + + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - cockroachdb + topologyKey: kubernetes.io/hostname + containers: + - name: cockroachdb + image: cockroachdb/cockroach:v1.1.5 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 26257 + name: grpc + - containerPort: 8080 + name: http + volumeMounts: + - name: datadir + mountPath: /cockroach/cockroach-data + - name: certs + mountPath: /cockroach/cockroach-certs + command: + - "/bin/bash" + - "-ecx" + - "exec /cockroach/cockroach start --logtostderr" + - --certs-dir /cockroach/cockroach-certs + - --host $(hostname -f) + - --http-host 0.0.0.0 + - --join $(CDB_STATEFULSET_NAME)-0.$(CDB_STATEFULSET_SVC),$(CDB_STATEFULSET_NAME)-1.$(CDB_STATEFULSET_SVC),$(CDB_STATEFULSET_NAME)-2.$(CDB_STATEFULSET_SVC) + - --cache 25% + - --max-sql-memory 25% + # No pre-stop hook is required, a SIGTERM plus some time is all that's + # needed for graceful shutdown of a node. + terminationGracePeriodSeconds: 60 + volumes: + - name: datadir + persistentVolumeClaim: + claimName: datadir + - name: certs + emptyDir: {} + updateStrategy: + type: RollingUpdate + volumeClaimTemplates: + - metadata: + name: datadir + spec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: 1Gi +`) + th.WriteK("overlay/staging", ` +namePrefix: dev- +resources: +- ../../base +`) + m := th.Run("overlay/staging", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - get +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb +rules: +- apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests + verbs: + - create + - get + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: dev-base-cockroachdb +subjects: +- kind: ServiceAccount + name: dev-base-cockroachdb + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: dev-base-cockroachdb +subjects: +- kind: ServiceAccount + name: dev-base-cockroachdb + namespace: default +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + prometheus.io/path: _status/vars + prometheus.io/port: "8080" + prometheus.io/scrape: "true" + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" + labels: + app: cockroachdb + name: dev-base-cockroachdb +spec: + clusterIP: None + ports: + - name: grpc + port: 26257 + targetPort: 26257 + - name: http + port: 8080 + targetPort: 8080 + selector: + app: cockroachdb +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb-public +spec: + ports: + - name: grpc + port: 26257 + targetPort: 26257 + - name: http + port: 8080 + targetPort: 8080 + selector: + app: cockroachdb +--- +apiVersion: apps/v1beta1 +kind: StatefulSet +metadata: + name: dev-base-cockroachdb +spec: + replicas: 3 + serviceName: dev-base-cockroachdb + template: + metadata: + labels: + app: cockroachdb + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - cockroachdb + topologyKey: kubernetes.io/hostname + weight: 100 + containers: + - command: + - /bin/bash + - -ecx + - exec /cockroach/cockroach start --logtostderr + - --certs-dir /cockroach/cockroach-certs + - --host $(hostname -f) + - --http-host 0.0.0.0 + - --join dev-base-cockroachdb-0.dev-base-cockroachdb,dev-base-cockroachdb-1.dev-base-cockroachdb,dev-base-cockroachdb-2.dev-base-cockroachdb + - --cache 25% + - --max-sql-memory 25% + image: cockroachdb/cockroach:v1.1.5 + imagePullPolicy: IfNotPresent + name: cockroachdb + ports: + - containerPort: 26257 + name: grpc + - containerPort: 8080 + name: http + volumeMounts: + - mountPath: /cockroach/cockroach-data + name: datadir + - mountPath: /cockroach/cockroach-certs + name: certs + initContainers: + - command: + - /bin/ash + - -ecx + - /request-cert + - -namespace=${POD_NAMESPACE} + - -certs-dir=/cockroach-certs + - -type=node + - -addresses=localhost,127.0.0.1,${POD_IP},$(hostname -f),$(hostname -f|cut + -f 1-2 -d '.'),dev-base-cockroachdb-public + - -symlink-ca-from=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + env: + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: cockroachdb/cockroach-k8s-request-cert:0.2 + imagePullPolicy: IfNotPresent + name: init-certs + volumeMounts: + - mountPath: /cockroach-certs + name: certs + serviceAccountName: dev-base-cockroachdb + terminationGracePeriodSeconds: 60 + volumes: + - name: datadir + persistentVolumeClaim: + claimName: datadir + - emptyDir: {} + name: certs + updateStrategy: + type: RollingUpdate + volumeClaimTemplates: + - metadata: + name: datadir + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + name: dev-base-cronjob-example +spec: + concurrencyPolicy: Forbid + jobTemplate: + spec: + template: + spec: + containers: + - command: + - echo + - dev-base-cockroachdb + - dev-base-test-config-map-6b85g79g7g + env: + - name: CDB_PUBLIC_SVC + value: dev-base-cockroachdb-public + image: cockroachdb/cockroach:v1.1.5 + name: cronjob-example + schedule: '*/1 * * * *' +--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + labels: + app: cockroachdb + name: dev-base-cockroachdb-budget +spec: + maxUnavailable: 1 + selector: + matchLabels: + app: cockroachdb +--- +apiVersion: v1 +data: + baz: qux + foo: bar +kind: ConfigMap +metadata: + name: dev-base-test-config-map-6b85g79g7g +`) +} + +func TestVariableRefIngressBasic(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +resources: +- ingress.yaml +- deployment.yaml + +vars: +- name: DEPLOYMENT_NAME + objref: + apiVersion: apps/v1 + kind: Deployment + name: nginxDep + fieldref: + fieldpath: metadata.name +`) + th.WriteF("deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginxDep +`) + + th.WriteF("ingress.yaml", ` +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: nginxIngress +spec: + rules: + - host: $(DEPLOYMENT_NAME).example.com + tls: + - hosts: + - $(DEPLOYMENT_NAME).example.com + secretName: $(DEPLOYMENT_NAME).example.com-tls +`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: nginxIngress +spec: + rules: + - host: nginxDep.example.com + tls: + - hosts: + - nginxDep.example.com + secretName: nginxDep.example.com-tls +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginxDep +`) +} + +func TestVariableRefIngressOverlay(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +resources: +- service.yaml +- deployment.yaml +- ingress.yaml + +vars: +- name: DEPLOYMENT_NAME + objref: + apiVersion: apps/v1 + kind: Deployment + name: nginx + fieldref: + fieldpath: metadata.name +`) + th.WriteF("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 +`) + th.WriteF("base/ingress.yaml", ` +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: nginx + labels: + app.kubernetes.io/component: nginx +spec: + rules: + - host: $(DEPLOYMENT_NAME).example.com + http: + paths: + - backend: + serviceName: nginx + servicePort: 80 + path: / + tls: + - hosts: + - $(DEPLOYMENT_NAME).example.com + secretName: $(DEPLOYMENT_NAME).example.com-tls +`) + th.WriteF("base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: nginx + labels: + app.kubernetes.io/component: nginx +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http +`) + th.WriteK("overlay", ` +nameprefix: kustomized- +resources: +- ../base +`) + m := th.Run("overlay", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: nginx + name: kustomized-nginx +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http +--- +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 +--- +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + labels: + app.kubernetes.io/component: nginx + name: kustomized-nginx +spec: + rules: + - host: kustomized-nginx.example.com + http: + paths: + - backend: + serviceName: kustomized-nginx + servicePort: 80 + path: / + tls: + - hosts: + - kustomized-nginx.example.com + secretName: kustomized-nginx.example.com-tls +`) +} + +func TestVariableRefMountPath(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +resources: +- deployment.yaml +- namespace.yaml + +vars: +- name: NAMESPACE + objref: + apiVersion: v1 + kind: Namespace + name: my-namespace + +`) + th.WriteF("base/deployment.yaml", ` + apiVersion: apps/v1 + kind: Deployment + metadata: + name: my-deployment + spec: + template: + spec: + containers: + - name: app + image: busybox + volumeMounts: + - name: my-volume + mountPath: "/$(NAMESPACE)" + env: + - name: NAMESPACE + value: $(NAMESPACE) + volumes: + - name: my-volume + emptyDir: {} +`) + th.WriteF("base/namespace.yaml", ` + apiVersion: v1 + kind: Namespace + metadata: + name: my-namespace +`) + + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment +spec: + template: + spec: + containers: + - env: + - name: NAMESPACE + value: my-namespace + image: busybox + name: app + volumeMounts: + - mountPath: /my-namespace + name: my-volume + volumes: + - emptyDir: {} + name: my-volume +--- +apiVersion: v1 +kind: Namespace +metadata: + name: my-namespace +`) +} + +func TestVariableRefMaps(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +resources: +- deployment.yaml +- namespace.yaml +vars: +- name: NAMESPACE + objref: + apiVersion: v1 + kind: Namespace + name: my-namespace +`) + th.WriteF("base/deployment.yaml", ` + apiVersion: apps/v1 + kind: Deployment + metadata: + name: my-deployment + labels: + my-label: $(NAMESPACE) + annotations: + my-annotation: $(NAMESPACE) + spec: + template: + spec: + containers: + - name: app + image: busybox +`) + th.WriteF("base/namespace.yaml", ` + apiVersion: v1 + kind: Namespace + metadata: + name: my-namespace +`) + + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + my-annotation: my-namespace + labels: + my-label: my-namespace + name: my-deployment +spec: + template: + spec: + containers: + - image: busybox + name: app +--- +apiVersion: v1 +kind: Namespace +metadata: + name: my-namespace +`) +} + +func TestVaribaleRefDifferentPrefix(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("base", ` +namePrefix: base- +resources: +- dev +- test +`) + + th.WriteK("base/dev", ` +namePrefix: dev- +resources: +- elasticsearch-dev-service.yml +vars: +- name: elasticsearch-dev-service-name + objref: + kind: Service + name: elasticsearch + apiVersion: v1 + fieldref: + fieldpath: metadata.name + +`) + th.WriteF("base/dev/elasticsearch-dev-service.yml", ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: elasticsearch +spec: + template: + spec: + containers: + - name: elasticsearch + env: + - name: DISCOVERY_SERVICE + value: "$(elasticsearch-dev-service-name).monitoring.svc.cluster.local" +--- +apiVersion: v1 +kind: Service +metadata: + name: elasticsearch +spec: + ports: + - name: transport + port: 9300 + protocol: TCP + clusterIP: None +`) + + th.WriteK("base/test", ` +namePrefix: test- +resources: +- elasticsearch-test-service.yml +vars: +- name: elasticsearch-test-service-name + objref: + kind: Service + name: elasticsearch + apiVersion: v1 + fieldref: + fieldpath: metadata.name +`) + th.WriteF("base/test/elasticsearch-test-service.yml", ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: elasticsearch +spec: + template: + spec: + containers: + - name: elasticsearch + env: + - name: DISCOVERY_SERVICE + value: "$(elasticsearch-test-service-name).monitoring.svc.cluster.local" +--- +apiVersion: v1 +kind: Service +metadata: + name: elasticsearch +spec: + ports: + - name: transport + port: 9300 + protocol: TCP + clusterIP: None +`) + + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: base-dev-elasticsearch +spec: + template: + spec: + containers: + - env: + - name: DISCOVERY_SERVICE + value: base-dev-elasticsearch.monitoring.svc.cluster.local + name: elasticsearch +--- +apiVersion: v1 +kind: Service +metadata: + name: base-dev-elasticsearch +spec: + clusterIP: None + ports: + - name: transport + port: 9300 + protocol: TCP +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: base-test-elasticsearch +spec: + template: + spec: + containers: + - env: + - name: DISCOVERY_SERVICE + value: base-test-elasticsearch.monitoring.svc.cluster.local + name: elasticsearch +--- +apiVersion: v1 +kind: Service +metadata: + name: base-test-elasticsearch +spec: + clusterIP: None + ports: + - name: transport + port: 9300 + protocol: TCP +`) +} + +func TestVariableRefNFSServer(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK("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("base/pv_pvc.yaml", ` +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: shared-volume-claim +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +`) + th.WriteF("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("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("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: 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("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: 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("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: 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("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: 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("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: 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("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("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: shared-files + volumes: + - name: nfs-files-vol + nfs: + server: $(NFS_SERVER_SERVICE_NAME).default.srv.cluster.local + path: / + readOnly: false +`) + th.WriteF("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("overlay", ` +nameprefix: kustomized- +resources: +- ../base +`) + m := th.Run("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: 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: 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: 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: 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: 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: 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 +`) +} + +func TestDeploymentAnnotations(t *testing.T) { + th := kusttest_test.MakeHarness(t) + th.WriteK(".", ` +configMapGenerator: +- name: theConfigMap + envs: + - test.properties + +vars: +- name: SOMERIVER + objref: + kind: ConfigMap + name: theConfigMap + apiVersion: v1 + fieldref: + fieldpath: data.waterway + +commonAnnotations: + river: $(SOMERIVER) + +resources: +- deployment.yaml +`) + + th.WriteF("deployment.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: theDeployment +spec: + template: + spec: + containers: + - name: test +`) + th.WriteF("test.properties", `waterway=mississippi`) + m := th.Run(".", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + river: mississippi + name: theDeployment +spec: + template: + metadata: + annotations: + river: mississippi + spec: + containers: + - name: test +--- +apiVersion: v1 +data: + waterway: mississippi +kind: ConfigMap +metadata: + annotations: + river: mississippi + name: theConfigMap-hdd8h8cgdt +`) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/kv/kv.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/kv/kv.go new file mode 100644 index 000000000..719b761c9 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/kv/kv.go @@ -0,0 +1,225 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kv + +import ( + "bufio" + "bytes" + "fmt" + "os" + "path" + "strings" + "unicode" + "unicode/utf8" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/types" +) + +var utf8bom = []byte{0xEF, 0xBB, 0xBF} + +// loader reads and validates KV pairs. +type loader struct { + // Used to read the filesystem. + ldr ifc.Loader + + // Used to validate various k8s data fields. + validator ifc.Validator +} + +func NewLoader(ldr ifc.Loader, v ifc.Validator) ifc.KvLoader { + return &loader{ldr: ldr, validator: v} +} + +func (kvl *loader) Validator() ifc.Validator { + return kvl.validator +} + +func (kvl *loader) Load( + args types.KvPairSources) (all []types.Pair, err error) { + pairs, err := kvl.keyValuesFromEnvFiles(args.EnvSources) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf( + "env source files: %v", + args.EnvSources)) + } + all = append(all, pairs...) + + pairs, err = keyValuesFromLiteralSources(args.LiteralSources) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf( + "literal sources %v", args.LiteralSources)) + } + all = append(all, pairs...) + + pairs, err = kvl.keyValuesFromFileSources(args.FileSources) + if err != nil { + return nil, errors.Wrap(err, fmt.Sprintf( + "file sources: %v", args.FileSources)) + } + return append(all, pairs...), nil +} + +func keyValuesFromLiteralSources(sources []string) ([]types.Pair, error) { + var kvs []types.Pair + for _, s := range sources { + k, v, err := parseLiteralSource(s) + if err != nil { + return nil, err + } + kvs = append(kvs, types.Pair{Key: k, Value: v}) + } + return kvs, nil +} + +func (kvl *loader) keyValuesFromFileSources(sources []string) ([]types.Pair, error) { + var kvs []types.Pair + for _, s := range sources { + k, fPath, err := parseFileSource(s) + if err != nil { + return nil, err + } + content, err := kvl.ldr.Load(fPath) + if err != nil { + return nil, err + } + kvs = append(kvs, types.Pair{Key: k, Value: string(content)}) + } + return kvs, nil +} + +func (kvl *loader) keyValuesFromEnvFiles(paths []string) ([]types.Pair, error) { + var kvs []types.Pair + for _, p := range paths { + content, err := kvl.ldr.Load(p) + if err != nil { + return nil, err + } + more, err := kvl.keyValuesFromLines(content) + if err != nil { + return nil, err + } + kvs = append(kvs, more...) + } + return kvs, nil +} + +// keyValuesFromLines parses given content in to a list of key-value pairs. +func (kvl *loader) keyValuesFromLines(content []byte) ([]types.Pair, error) { + var kvs []types.Pair + + scanner := bufio.NewScanner(bytes.NewReader(content)) + currentLine := 0 + for scanner.Scan() { + // Process the current line, retrieving a key/value pair if + // possible. + scannedBytes := scanner.Bytes() + kv, err := kvl.keyValuesFromLine(scannedBytes, currentLine) + if err != nil { + return nil, err + } + currentLine++ + + if len(kv.Key) == 0 { + // no key means line was empty or a comment + continue + } + + kvs = append(kvs, kv) + } + return kvs, nil +} + +// KeyValuesFromLine returns a kv with blank key if the line is empty or a comment. +// The value will be retrieved from the environment if necessary. +func (kvl *loader) keyValuesFromLine(line []byte, currentLine int) (types.Pair, error) { + kv := types.Pair{} + + if !utf8.Valid(line) { + return kv, fmt.Errorf("line %d has invalid utf8 bytes : %v", line, string(line)) + } + + // We trim UTF8 BOM from the first line of the file but no others + if currentLine == 0 { + line = bytes.TrimPrefix(line, utf8bom) + } + + // trim the line from all leading whitespace first + line = bytes.TrimLeftFunc(line, unicode.IsSpace) + + // If the line is empty or a comment, we return a blank key/value pair. + if len(line) == 0 || line[0] == '#' { + return kv, nil + } + + data := strings.SplitN(string(line), "=", 2) + key := data[0] + if err := kvl.validator.IsEnvVarName(key); err != nil { + return kv, err + } + + if len(data) == 2 { + kv.Value = data[1] + } else { + // No value (no `=` in the line) is a signal to obtain the value + // from the environment. + kv.Value = os.Getenv(key) + } + kv.Key = key + return kv, nil +} + +// ParseFileSource parses the source given. +// +// Acceptable formats include: +// 1. source-path: the basename will become the key name +// 2. source-name=source-path: the source-name will become the key name and +// source-path is the path to the key file. +// +// Key names cannot include '='. +func parseFileSource(source string) (keyName, filePath string, err error) { + numSeparators := strings.Count(source, "=") + switch { + case numSeparators == 0: + return path.Base(source), source, nil + case numSeparators == 1 && strings.HasPrefix(source, "="): + return "", "", fmt.Errorf("key name for file path %v missing", strings.TrimPrefix(source, "=")) + case numSeparators == 1 && strings.HasSuffix(source, "="): + return "", "", fmt.Errorf("file path for key name %v missing", strings.TrimSuffix(source, "=")) + case numSeparators > 1: + return "", "", errors.New("key names or file paths cannot contain '='") + default: + components := strings.Split(source, "=") + return components[0], components[1], nil + } +} + +// ParseLiteralSource parses the source key=val pair into its component pieces. +// This functionality is distinguished from strings.SplitN(source, "=", 2) since +// it returns an error in the case of empty keys, values, or a missing equals sign. +func parseLiteralSource(source string) (keyName, value string, err error) { + // leading equal is invalid + if strings.Index(source, "=") == 0 { + return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source) + } + // split after the first equal (so values can have the = character) + items := strings.SplitN(source, "=", 2) + if len(items) != 2 { + return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source) + } + return items[0], removeQuotes(items[1]), nil +} + +// removeQuotes removes the surrounding quotes from the provided string only if it is surrounded on both sides +// rather than blindly trimming all quotation marks on either side. +func removeQuotes(str string) string { + if len(str) == 0 || str[0] != str[len(str)-1] { + return str + } + if str[0] == '"' || str[0] == '\'' { + return str[1 : len(str)-1] + } + return str +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/kv/kv_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/kv/kv_test.go new file mode 100644 index 000000000..5f9631c65 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/kv/kv_test.go @@ -0,0 +1,99 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kv + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" + ldr "sigs.k8s.io/kustomize/api/loader" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func makeKvLoader(fSys filesys.FileSystem) *loader { + return &loader{ + ldr: ldr.NewFileLoaderAtRoot(fSys), + validator: valtest_test.MakeFakeValidator()} +} + +func TestKeyValuesFromLines(t *testing.T) { + tests := []struct { + desc string + content string + expectedPairs []types.Pair + expectedErr bool + }{ + { + desc: "valid kv content parse", + content: ` + k1=v1 + k2=v2 + `, + expectedPairs: []types.Pair{ + {Key: "k1", Value: "v1"}, + {Key: "k2", Value: "v2"}, + }, + expectedErr: false, + }, + { + desc: "content with comments", + content: ` + k1=v1 + #k2=v2 + `, + expectedPairs: []types.Pair{ + {Key: "k1", Value: "v1"}, + }, + expectedErr: false, + }, + // TODO: add negative testcases + } + + kvl := makeKvLoader(filesys.MakeFsInMemory()) + for _, test := range tests { + pairs, err := kvl.keyValuesFromLines([]byte(test.content)) + if test.expectedErr && err == nil { + t.Fatalf("%s should not return error", test.desc) + } + if !reflect.DeepEqual(pairs, test.expectedPairs) { + t.Errorf("%s should succeed, got:%v exptected:%v", test.desc, pairs, test.expectedPairs) + } + } +} + +func TestKeyValuesFromFileSources(t *testing.T) { + tests := []struct { + description string + sources []string + expected []types.Pair + }{ + { + description: "create kvs from file sources", + sources: []string{"files/app-init.ini"}, + expected: []types.Pair{ + { + Key: "app-init.ini", + Value: "FOO=bar", + }, + }, + }, + } + + fSys := filesys.MakeFsInMemory() + err := fSys.WriteFile("/files/app-init.ini", []byte("FOO=bar")) + require.NoError(t, err) + kvl := makeKvLoader(fSys) + for _, tc := range tests { + kvs, err := kvl.keyValuesFromFileSources(tc.sources) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(kvs, tc.expected) { + t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, kvs, tc.expected) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/errors.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/errors.go new file mode 100644 index 000000000..5281a1cd7 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/errors.go @@ -0,0 +1,5 @@ +package loader + +import "fmt" + +var ErrorHTTP = fmt.Errorf("HTTP Error") diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/fileloader.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/fileloader.go new file mode 100644 index 000000000..4426097cf --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/fileloader.go @@ -0,0 +1,338 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package loader + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "path/filepath" + "strings" + + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/git" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// fileLoader is a kustomization's interface to files. +// +// The directory in which a kustomization file sits +// is referred to below as the kustomization's _root_. +// +// An instance of fileLoader has an immutable root, +// and offers a `New` method returning a new loader +// with a new root. +// +// A kustomization file refers to two kinds of files: +// +// * supplemental data paths +// +// `Load` is used to visit these paths. +// +// These paths refer to resources, patches, +// data for ConfigMaps and Secrets, etc. +// +// The loadRestrictor may disallow certain paths +// or classes of paths. +// +// * bases (other kustomizations) +// +// `New` is used to load bases. +// +// A base can be either a remote git repo URL, or +// a directory specified relative to the current +// root. In the former case, the repo is locally +// cloned, and the new loader is rooted on a path +// in that clone. +// +// As loaders create new loaders, a root history +// is established, and used to disallow: +// +// - A base that is a repository that, in turn, +// specifies a base repository seen previously +// in the loading stack (a cycle). +// +// - An overlay depending on a base positioned at +// or above it. I.e. '../foo' is OK, but '.', +// '..', '../..', etc. are disallowed. Allowing +// such a base has no advantages and encourages +// cycles, particularly if some future change +// were to introduce globbing to file +// specifications in the kustomization file. +// +// These restrictions assure that kustomizations +// are self-contained and relocatable, and impose +// some safety when relying on remote kustomizations, +// e.g. a remotely loaded ConfigMap generator specified +// to read from /etc/passwd will fail. +// +type fileLoader struct { + // Loader that spawned this loader. + // Used to avoid cycles. + referrer *fileLoader + + // An absolute, cleaned path to a directory. + // The Load function will read non-absolute + // paths relative to this directory. + root filesys.ConfirmedDir + + // Restricts behavior of Load function. + loadRestrictor LoadRestrictorFunc + + // If this is non-nil, the files were + // obtained from the given repository. + repoSpec *git.RepoSpec + + // File system utilities. + fSys filesys.FileSystem + + // Used to load from HTTP + http *http.Client + + // Used to clone repositories. + cloner git.Cloner + + // Used to clean up, as needed. + cleaner func() error +} + +// NewFileLoaderAtCwd returns a loader that loads from PWD. +// A convenience for kustomize edit commands. +func NewFileLoaderAtCwd(fSys filesys.FileSystem) *fileLoader { + return newLoaderOrDie( + RestrictionRootOnly, fSys, filesys.SelfDir) +} + +// NewFileLoaderAtRoot returns a loader that loads from "/". +// A convenience for tests. +func NewFileLoaderAtRoot(fSys filesys.FileSystem) *fileLoader { + return newLoaderOrDie( + RestrictionRootOnly, fSys, filesys.Separator) +} + +// Root returns the absolute path that is prepended to any +// relative paths used in Load. +func (fl *fileLoader) Root() string { + return fl.root.String() +} + +func newLoaderOrDie( + lr LoadRestrictorFunc, + fSys filesys.FileSystem, path string) *fileLoader { + root, err := demandDirectoryRoot(fSys, path) + if err != nil { + log.Fatalf("unable to make loader at '%s'; %v", path, err) + } + return newLoaderAtConfirmedDir( + lr, root, fSys, nil, git.ClonerUsingGitExec) +} + +// newLoaderAtConfirmedDir returns a new fileLoader with given root. +func newLoaderAtConfirmedDir( + lr LoadRestrictorFunc, + root filesys.ConfirmedDir, fSys filesys.FileSystem, + referrer *fileLoader, cloner git.Cloner) *fileLoader { + return &fileLoader{ + loadRestrictor: lr, + root: root, + referrer: referrer, + fSys: fSys, + cloner: cloner, + cleaner: func() error { return nil }, + } +} + +// Assure that the given path is in fact a directory. +func demandDirectoryRoot( + fSys filesys.FileSystem, path string) (filesys.ConfirmedDir, error) { + if path == "" { + return "", fmt.Errorf( + "loader root cannot be empty") + } + d, f, err := fSys.CleanedAbs(path) + if err != nil { + return "", err + } + if f != "" { + return "", fmt.Errorf( + "got file '%s', but '%s' must be a directory to be a root", + f, path) + } + return d, nil +} + +// New returns a new Loader, rooted relative to current loader, +// or rooted in a temp directory holding a git repo clone. +func (fl *fileLoader) New(path string) (ifc.Loader, error) { + if path == "" { + return nil, fmt.Errorf("new root cannot be empty") + } + + repoSpec, err := git.NewRepoSpecFromUrl(path) + if err == nil { + // Treat this as git repo clone request. + if err = fl.errIfRepoCycle(repoSpec); err != nil { + return nil, err + } + return newLoaderAtGitClone( + repoSpec, fl.fSys, fl, fl.cloner) + } + + if filepath.IsAbs(path) { + return nil, fmt.Errorf("new root '%s' cannot be absolute", path) + } + root, err := demandDirectoryRoot(fl.fSys, fl.root.Join(path)) + if err != nil { + return nil, err + } + if err = fl.errIfGitContainmentViolation(root); err != nil { + return nil, err + } + if err = fl.errIfArgEqualOrHigher(root); err != nil { + return nil, err + } + return newLoaderAtConfirmedDir( + fl.loadRestrictor, root, fl.fSys, fl, fl.cloner), nil +} + +// newLoaderAtGitClone returns a new Loader pinned to a temporary +// directory holding a cloned git repo. +func newLoaderAtGitClone( + repoSpec *git.RepoSpec, fSys filesys.FileSystem, + referrer *fileLoader, cloner git.Cloner) (ifc.Loader, error) { + cleaner := repoSpec.Cleaner(fSys) + err := cloner(repoSpec) + if err != nil { + cleaner() + return nil, err + } + root, f, err := fSys.CleanedAbs(repoSpec.AbsPath()) + if err != nil { + cleaner() + return nil, err + } + // We don't know that the path requested in repoSpec + // is a directory until we actually clone it and look + // inside. That just happened, hence the error check + // is here. + if f != "" { + cleaner() + return nil, fmt.Errorf( + "'%s' refers to file '%s'; expecting directory", + repoSpec.AbsPath(), f) + } + return &fileLoader{ + // Clones never allowed to escape root. + loadRestrictor: RestrictionRootOnly, + root: root, + referrer: referrer, + repoSpec: repoSpec, + fSys: fSys, + cloner: cloner, + cleaner: cleaner, + }, nil +} + +func (fl *fileLoader) errIfGitContainmentViolation( + base filesys.ConfirmedDir) error { + containingRepo := fl.containingRepo() + if containingRepo == nil { + return nil + } + if !base.HasPrefix(containingRepo.CloneDir()) { + return fmt.Errorf( + "security; bases in kustomizations found in "+ + "cloned git repos must be within the repo, "+ + "but base '%s' is outside '%s'", + base, containingRepo.CloneDir()) + } + return nil +} + +// Looks back through referrers for a git repo, returning nil +// if none found. +func (fl *fileLoader) containingRepo() *git.RepoSpec { + if fl.repoSpec != nil { + return fl.repoSpec + } + if fl.referrer == nil { + return nil + } + return fl.referrer.containingRepo() +} + +// errIfArgEqualOrHigher tests whether the argument, +// is equal to or above the root of any ancestor. +func (fl *fileLoader) errIfArgEqualOrHigher( + candidateRoot filesys.ConfirmedDir) error { + if fl.root.HasPrefix(candidateRoot) { + return fmt.Errorf( + "cycle detected: candidate root '%s' contains visited root '%s'", + candidateRoot, fl.root) + } + if fl.referrer == nil { + return nil + } + return fl.referrer.errIfArgEqualOrHigher(candidateRoot) +} + +// TODO(monopole): Distinguish branches? +// I.e. Allow a distinction between git URI with +// path foo and tag bar and a git URI with the same +// path but a different tag? +func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error { + // TODO(monopole): Use parsed data instead of Raw(). + if fl.repoSpec != nil && + strings.HasPrefix(fl.repoSpec.Raw(), newRepoSpec.Raw()) { + return fmt.Errorf( + "cycle detected: URI '%s' referenced by previous URI '%s'", + newRepoSpec.Raw(), fl.repoSpec.Raw()) + } + if fl.referrer == nil { + return nil + } + return fl.referrer.errIfRepoCycle(newRepoSpec) +} + +// Load returns the content of file at the given path, +// 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() + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return nil, fmt.Errorf("%w: status code %d (%s)", ErrorHTTP, resp.StatusCode, http.StatusText(resp.StatusCode)) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, nil + } + if !filepath.IsAbs(path) { + path = fl.root.Join(path) + } + path, err := fl.loadRestrictor(fl.fSys, fl.root, path) + if err != nil { + return nil, err + } + return fl.fSys.ReadFile(path) +} + +// Cleanup runs the cleaner. +func (fl *fileLoader) Cleanup() error { + return fl.cleaner() +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/fileloader_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/fileloader_test.go new file mode 100644 index 000000000..aedfd1693 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/fileloader_test.go @@ -0,0 +1,673 @@ +/// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package loader + +import ( + "bytes" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "reflect" + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/git" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +type testData struct { + path string + expectedContent string +} + +var testCases = []testData{ + { + path: "foo/project/fileA.yaml", + expectedContent: "fileA content", + }, + { + path: "foo/project/subdir1/fileB.yaml", + expectedContent: "fileB content", + }, + { + path: "foo/project/subdir2/fileC.yaml", + expectedContent: "fileC content", + }, + { + path: "foo/project/fileD.yaml", + expectedContent: "fileD content", + }, +} + +func MakeFakeFs(td []testData) filesys.FileSystem { + fSys := filesys.MakeFsInMemory() + for _, x := range td { + fSys.WriteFile(x.path, []byte(x.expectedContent)) + } + return fSys +} + +func makeLoader() *fileLoader { + return NewFileLoaderAtRoot(MakeFakeFs(testCases)) +} + +func TestLoaderLoad(t *testing.T) { + l1 := makeLoader() + if "/" != l1.Root() { + t.Fatalf("incorrect root: '%s'\n", l1.Root()) + } + for _, x := range testCases { + 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) + } + } + l2, err := l1.New("foo/project") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if "/foo/project" != l2.Root() { + t.Fatalf("incorrect root: %s\n", l2.Root()) + } + for _, x := range testCases { + b, err := l2.Load(strings.TrimPrefix(x.path, "foo/project/")) + 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) + } + } + l2, err = l1.New("foo/project/") // Assure trailing slash stripped + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if "/foo/project" != l2.Root() { + t.Fatalf("incorrect root: %s\n", l2.Root()) + } +} + +func TestLoaderNewSubDir(t *testing.T) { + l1, err := makeLoader().New("foo/project") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + l2, err := l1.New("subdir1") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if "/foo/project/subdir1" != l2.Root() { + t.Fatalf("incorrect root: %s\n", l2.Root()) + } + x := testCases[1] + b, err := l2.Load("fileB.yaml") + 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) + } +} + +func TestLoaderBadRelative(t *testing.T) { + l1, err := makeLoader().New("foo/project/subdir1") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if "/foo/project/subdir1" != l1.Root() { + t.Fatalf("incorrect root: %s\n", l1.Root()) + } + + // Cannot cd into a file. + l2, err := l1.New("fileB.yaml") + if err == nil { + t.Fatalf("expected err, but got root %s", l2.Root()) + } + + // It's not okay to stay at the same place. + l2, err = l1.New(filesys.SelfDir) + if err == nil { + t.Fatalf("expected err, but got root %s", l2.Root()) + } + + // It's not okay to go up and back down into same place. + l2, err = l1.New("../subdir1") + if err == nil { + t.Fatalf("expected err, but got root %s", l2.Root()) + } + + // It's not okay to go up via a relative path. + l2, err = l1.New("..") + if err == nil { + t.Fatalf("expected err, but got root %s", l2.Root()) + } + + // It's not okay to go up via an absolute path. + l2, err = l1.New("/foo/project") + if err == nil { + t.Fatalf("expected err, but got root %s", l2.Root()) + } + + // It's not okay to go to the root. + l2, err = l1.New("/") + if err == nil { + t.Fatalf("expected err, but got root %s", l2.Root()) + } + + // It's okay to go up and down to a sibling. + l2, err = l1.New("../subdir2") + if err != nil { + t.Fatalf("unexpected new error %v", err) + } + if "/foo/project/subdir2" != l2.Root() { + t.Fatalf("incorrect root: %s\n", l2.Root()) + } + x := testCases[2] + b, err := l2.Load("fileC.yaml") + 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) + } + + // It's not OK to go over to a previously visited directory. + // Must disallow going back and forth in a cycle. + l1, err = l2.New("../subdir1") + if err == nil { + t.Fatalf("expected err, but got root %s", l1.Root()) + } +} + +func TestLoaderMisc(t *testing.T) { + l := makeLoader() + _, err := l.New("") + if err == nil { + t.Fatalf("Expected error for empty root location not returned") + } + _, err = l.New("https://google.com/project") + if err == nil { + t.Fatalf("Expected error") + } +} + +const ( + contentOk = "hi there, i'm OK data" + contentExteriorData = "i am data from outside the root" +) + +// Create a structure like this +// +// /tmp/kustomize-test-random +// ├── base +// │ ├── okayData +// │ ├── symLinkToOkayData -> okayData +// │ └── symLinkToExteriorData -> ../exteriorData +// └── exteriorData +// +func commonSetupForLoaderRestrictionTest() (string, filesys.FileSystem, error) { + dir, err := ioutil.TempDir("", "kustomize-test-") + if err != nil { + return "", nil, err + } + fSys := filesys.MakeFsOnDisk() + fSys.Mkdir(filepath.Join(dir, "base")) + + fSys.WriteFile( + filepath.Join(dir, "base", "okayData"), []byte(contentOk)) + + fSys.WriteFile( + filepath.Join(dir, "exteriorData"), []byte(contentExteriorData)) + + os.Symlink( + filepath.Join(dir, "base", "okayData"), + filepath.Join(dir, "base", "symLinkToOkayData")) + os.Symlink( + filepath.Join(dir, "exteriorData"), + filepath.Join(dir, "base", "symLinkToExteriorData")) + return dir, fSys, nil +} + +// Make sure everything works when loading files +// in or below the loader root. +func doSanityChecksAndDropIntoBase( + t *testing.T, l ifc.Loader) ifc.Loader { + data, err := l.Load(path.Join("base", "okayData")) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if string(data) != contentOk { + t.Fatalf("unexpected content: %v", data) + } + data, err = l.Load("exteriorData") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if string(data) != contentExteriorData { + t.Fatalf("unexpected content: %v", data) + } + + // Drop in. + l, err = l.New("base") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Reading okayData works. + data, err = l.Load("okayData") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if string(data) != contentOk { + t.Fatalf("unexpected content: %v", data) + } + + // Reading local symlink to okayData works. + data, err = l.Load("symLinkToOkayData") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if string(data) != contentOk { + t.Fatalf("unexpected content: %v", data) + } + return l +} + +func TestRestrictionRootOnlyInRealLoader(t *testing.T) { + dir, fSys, err := commonSetupForLoaderRestrictionTest() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer os.RemoveAll(dir) + + var l ifc.Loader + + l = newLoaderOrDie(RestrictionRootOnly, fSys, dir) + + l = doSanityChecksAndDropIntoBase(t, l) + + // Reading symlink to exteriorData fails. + _, err = l.Load("symLinkToExteriorData") + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), "is not in or below") { + t.Fatalf("unexpected err: %v", err) + } + + // Attempt to read "up" fails, though earlier we were + // able to read this file when root was "..". + _, err = l.Load("../exteriorData") + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), "is not in or below") { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestRestrictionNoneInRealLoader(t *testing.T) { + dir, fSys, err := commonSetupForLoaderRestrictionTest() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer os.RemoveAll(dir) + + var l ifc.Loader + + l = newLoaderOrDie(RestrictionNone, fSys, dir) + + l = doSanityChecksAndDropIntoBase(t, l) + + // Reading symlink to exteriorData works. + _, err = l.Load("symLinkToExteriorData") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Attempt to read "up" works. + _, err = l.Load("../exteriorData") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + +func splitOnNthSlash(v string, n int) (string, string) { + left := "" + for i := 0; i < n; i++ { + k := strings.Index(v, "/") + if k < 0 { + break + } + left = left + v[:k+1] + v = v[k+1:] + } + return left[:len(left)-1], v +} + +func TestSplit(t *testing.T) { + p := "a/b/c/d/e/f/g" + if left, right := splitOnNthSlash(p, 2); left != "a/b" || right != "c/d/e/f/g" { + t.Fatalf("got left='%s', right='%s'", left, right) + } + if left, right := splitOnNthSlash(p, 3); left != "a/b/c" || right != "d/e/f/g" { + t.Fatalf("got left='%s', right='%s'", left, right) + } + if left, right := splitOnNthSlash(p, 6); left != "a/b/c/d/e/f" || right != "g" { + t.Fatalf("got left='%s', right='%s'", left, right) + } +} + +func TestNewLoaderAtGitClone(t *testing.T) { + rootUrl := "github.com/someOrg/someRepo" + pathInRepo := "foo/base" + url := rootUrl + "/" + pathInRepo + coRoot := "/tmp" + fSys := filesys.MakeFsInMemory() + fSys.MkdirAll(coRoot) + fSys.MkdirAll(coRoot + "/" + pathInRepo) + fSys.WriteFile( + coRoot+"/"+pathInRepo+"/"+ + konfig.DefaultKustomizationFileName(), + []byte(` +whatever +`)) + + repoSpec, err := git.NewRepoSpecFromUrl(url) + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + l, err := newLoaderAtGitClone( + repoSpec, fSys, nil, + git.DoNothingCloner(filesys.ConfirmedDir(coRoot))) + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if coRoot+"/"+pathInRepo != l.Root() { + t.Fatalf("expected root '%s', got '%s'\n", + coRoot+"/"+pathInRepo, l.Root()) + } + if _, err = l.New(url); err == nil { + t.Fatalf("expected cycle error 1") + } + if _, err = l.New(rootUrl + "/" + "foo"); err == nil { + t.Fatalf("expected cycle error 2") + } + + pathInRepo = "foo/overlay" + fSys.MkdirAll(coRoot + "/" + pathInRepo) + url = rootUrl + "/" + pathInRepo + l2, err := l.New(url) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if coRoot+"/"+pathInRepo != l2.Root() { + t.Fatalf("expected root '%s', got '%s'\n", + coRoot+"/"+pathInRepo, l2.Root()) + } +} + +func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) { + // Define an overlay-base structure in the file system. + topDir := "/whatever" + cloneRoot := topDir + "/someClone" + fSys := filesys.MakeFsInMemory() + fSys.MkdirAll(topDir + "/highBase") + fSys.MkdirAll(cloneRoot + "/foo/base") + fSys.MkdirAll(cloneRoot + "/foo/overlay") + + var l1 ifc.Loader + + // Establish that a local overlay can navigate + // to the local bases. + l1 = newLoaderOrDie( + RestrictionRootOnly, fSys, cloneRoot+"/foo/overlay") + if l1.Root() != cloneRoot+"/foo/overlay" { + t.Fatalf("unexpected root %s", l1.Root()) + } + l2, err := l1.New("../base") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if l2.Root() != cloneRoot+"/foo/base" { + t.Fatalf("unexpected root %s", l2.Root()) + } + l3, err := l2.New("../../../highBase") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if l3.Root() != topDir+"/highBase" { + t.Fatalf("unexpected root %s", l3.Root()) + } + + // Establish that a Kustomization found in cloned + // repo can reach (non-remote) bases inside the clone + // but cannot reach a (non-remote) base outside the + // clone but legitimately on the local file system. + // This is to avoid a surprising interaction between + // a remote K and local files. The remote K would be + // non-functional on its own since by definition it + // would refer to a non-remote base file that didn't + // exist in its own repository, so presumably the + // remote K would be deliberately designed to phish + // for local K's. + repoSpec, err := git.NewRepoSpecFromUrl( + "github.com/someOrg/someRepo/foo/overlay") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + l1, err = newLoaderAtGitClone( + repoSpec, fSys, nil, + git.DoNothingCloner(filesys.ConfirmedDir(cloneRoot))) + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if l1.Root() != cloneRoot+"/foo/overlay" { + t.Fatalf("unexpected root %s", l1.Root()) + } + // This is okay. + l2, err = l1.New("../base") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if l2.Root() != cloneRoot+"/foo/base" { + t.Fatalf("unexpected root %s", l2.Root()) + } + // This is not okay. + _, err = l2.New("../../../highBase") + if err == nil { + t.Fatalf("expected err") + } + if !strings.Contains(err.Error(), + "base '/whatever/highBase' is outside '/whatever/someClone'") { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestLocalLoaderReferencingGitBase(t *testing.T) { + topDir := "/whatever" + cloneRoot := topDir + "/someClone" + fSys := filesys.MakeFsInMemory() + fSys.MkdirAll(topDir) + fSys.MkdirAll(cloneRoot + "/foo/base") + + root, err := demandDirectoryRoot(fSys, topDir) + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + l1 := newLoaderAtConfirmedDir( + RestrictionRootOnly, root, fSys, nil, + git.DoNothingCloner(filesys.ConfirmedDir(cloneRoot))) + if l1.Root() != topDir { + t.Fatalf("unexpected root %s", l1.Root()) + } + l2, err := l1.New("github.com/someOrg/someRepo/foo/base") + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + if l2.Root() != cloneRoot+"/foo/base" { + t.Fatalf("unexpected root %s", l2.Root()) + } +} + +func TestRepoDirectCycleDetection(t *testing.T) { + topDir := "/cycles" + cloneRoot := topDir + "/someClone" + fSys := filesys.MakeFsInMemory() + fSys.MkdirAll(topDir) + fSys.MkdirAll(cloneRoot) + + root, err := demandDirectoryRoot(fSys, topDir) + if err != nil { + t.Fatalf("unexpected err: %v\n", err) + } + l1 := newLoaderAtConfirmedDir( + RestrictionRootOnly, root, fSys, nil, + git.DoNothingCloner(filesys.ConfirmedDir(cloneRoot))) + p1 := "github.com/someOrg/someRepo/foo" + rs1, err := git.NewRepoSpecFromUrl(p1) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + l1.repoSpec = rs1 + _, err = l1.New(p1) + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), "cycle detected") { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestRepoIndirectCycleDetection(t *testing.T) { + topDir := "/cycles" + cloneRoot := topDir + "/someClone" + fSys := filesys.MakeFsInMemory() + fSys.MkdirAll(topDir) + fSys.MkdirAll(cloneRoot) + + root, err := demandDirectoryRoot(fSys, topDir) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + l0 := newLoaderAtConfirmedDir( + RestrictionRootOnly, root, fSys, nil, + git.DoNothingCloner(filesys.ConfirmedDir(cloneRoot))) + + p1 := "github.com/someOrg/someRepo1" + p2 := "github.com/someOrg/someRepo2" + + l1, err := l0.New(p1) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + l2, err := l1.New(p2) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + _, err = l2.New(p1) + if err == nil { + t.Fatalf("expected error") + } + if !strings.Contains(err.Error(), "cycle detected") { + 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) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loader.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loader.go new file mode 100644 index 000000000..7a2b75f20 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loader.go @@ -0,0 +1,34 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package loader has a data loading interface and various implementations. +package loader + +import ( + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/git" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// NewLoader returns a Loader pointed at the given target. +// If the target is remote, the loader will be restricted +// to the root and below only. If the target is local, the +// loader will have the restrictions passed in. Regardless, +// if a local target attempts to transitively load remote bases, +// the remote bases will all be root-only restricted. +func NewLoader( + lr LoadRestrictorFunc, + target string, fSys filesys.FileSystem) (ifc.Loader, error) { + repoSpec, err := git.NewRepoSpecFromUrl(target) + if err == nil { + // The target qualifies as a remote git target. + return newLoaderAtGitClone( + repoSpec, fSys, nil, git.ClonerUsingGitExec) + } + root, err := demandDirectoryRoot(fSys, target) + if err != nil { + return nil, err + } + return newLoaderAtConfirmedDir( + lr, root, fSys, nil, git.ClonerUsingGitExec), nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loadrestrictions.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loadrestrictions.go new file mode 100644 index 000000000..a016a9625 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loadrestrictions.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package loader + +import ( + "fmt" + + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +type LoadRestrictorFunc func( + filesys.FileSystem, filesys.ConfirmedDir, string) (string, error) + +func RestrictionRootOnly( + fSys filesys.FileSystem, root filesys.ConfirmedDir, path string) (string, error) { + d, f, err := fSys.CleanedAbs(path) + if err != nil { + return "", err + } + if f == "" { + return "", fmt.Errorf("'%s' must resolve to a file", path) + } + if !d.HasPrefix(root) { + return "", fmt.Errorf( + "security; file '%s' is not in or below '%s'", + path, root) + } + return d.Join(f), nil +} + +func RestrictionNone( + _ filesys.FileSystem, _ filesys.ConfirmedDir, path string) (string, error) { + return path, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loadrestrictions_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loadrestrictions_test.go new file mode 100644 index 000000000..de9ded6f4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/loader/loadrestrictions_test.go @@ -0,0 +1,67 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package loader + +import ( + "path/filepath" + "strings" + "testing" + + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func TestRestrictionNone(t *testing.T) { + fSys := filesys.MakeFsInMemory() + root := filesys.ConfirmedDir("irrelevant") + path := "whatever" + p, err := RestrictionNone(fSys, root, path) + if err != nil { + t.Fatal(err) + } + if p != path { + t.Fatalf("expected '%s', got '%s'", path, p) + } +} + +func TestRestrictionRootOnly(t *testing.T) { + fSys := filesys.MakeFsInMemory() + root := filesys.ConfirmedDir( + filesys.Separator + filepath.Join("tmp", "foo")) + path := filepath.Join(string(root), "whatever", "beans") + + fSys.Create(path) + p, err := RestrictionRootOnly(fSys, root, path) + if err != nil { + t.Fatal(err) + } + if p != path { + t.Fatalf("expected '%s', got '%s'", path, p) + } + + // Legal. + path = filepath.Join( + string(root), "whatever", "..", "..", "foo", "whatever", "beans") + p, err = RestrictionRootOnly(fSys, root, path) + if err != nil { + t.Fatal(err) + } + path = filepath.Join( + string(root), "whatever", "beans") + if p != path { + t.Fatalf("expected '%s', got '%s'", path, p) + } + + // Illegal; file exists but is out of bounds. + path = filepath.Join(filesys.Separator+"tmp", "illegal") + fSys.Create(path) + _, err = RestrictionRootOnly(fSys, root, path) + if err == nil { + t.Fatal("should have an error") + } + if !strings.Contains( + err.Error(), + "file '/tmp/illegal' is not in or below '/tmp/foo'") { + t.Fatalf("unexpected err: %s", err) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/main.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/main.go new file mode 100644 index 000000000..ba84e6f5d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/main.go @@ -0,0 +1,20 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// A dummy main to help with releasing the kustomize API module. +package main + +import ( + "fmt" + + "sigs.k8s.io/kustomize/api/provenance" +) + +// TODO: delete this when we find a better way to generate release notes. +func main() { + fmt.Println(` +This 'main' exists only to make goreleaser create release notes for the API. +See https://github.com/goreleaser/goreleaser/issues/981 +and https://github.com/kubernetes-sigs/kustomize/tree/master/releasing`) + fmt.Println(provenance.GetProvenance()) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/provenance/provenance.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/provenance/provenance.go new file mode 100644 index 000000000..4ad40395a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/provenance/provenance.go @@ -0,0 +1,68 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package provenance + +import ( + "fmt" + "runtime" + "strings" +) + +var ( + version = "unknown" + // sha1 from git, output of $(git rev-parse HEAD) + gitCommit = "$Format:%H$" + // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') + buildDate = "1970-01-01T00:00:00Z" + goos = runtime.GOOS + goarch = runtime.GOARCH +) + +// Provenance holds information about the build of an executable. +type Provenance struct { + // Version of the kustomize binary. + Version string `json:"version,omitempty"` + // GitCommit is a git commit + GitCommit string `json:"gitCommit,omitempty"` + // BuildDate is date of the build. + BuildDate string `json:"buildDate,omitempty"` + // GoOs holds OS name. + GoOs string `json:"goOs,omitempty"` + // GoArch holds architecture name. + GoArch string `json:"goArch,omitempty"` +} + +// GetProvenance returns an instance of Provenance. +func GetProvenance() Provenance { + return Provenance{ + version, + gitCommit, + buildDate, + goos, + goarch, + } +} + +// Full returns the full provenance stamp. +func (v Provenance) Full() string { + return fmt.Sprintf("%+v", v) +} + +// Short returns the shortened provenance stamp. +func (v Provenance) Short() string { + return fmt.Sprintf( + "%v", + Provenance{ + Version: v.Version, + BuildDate: v.BuildDate, + }) +} + +// Semver returns the semantic version of kustomize. +// kustomize version is set in format "kustomize/vX.X.X" in every release. +// X.X.X is a semver. If the version string is not in this format, +// return the original version string +func (v Provenance) Semver() string { + return strings.TrimPrefix(v.Version, "kustomize/") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/provider/depprovider.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/provider/depprovider.go new file mode 100644 index 000000000..0102c89ce --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/provider/depprovider.go @@ -0,0 +1,42 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package provider + +import ( + "sigs.k8s.io/kustomize/api/hasher" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/validate" + "sigs.k8s.io/kustomize/api/resource" +) + +// DepProvider is a dependency provider, injecting different +// implementations depending on the context. +type DepProvider struct { + resourceFactory *resource.Factory + // implemented by api/internal/validate.FieldValidator + // See TODO inside the validator for status. + // At time of writing, this is a do-nothing + // validator as it's not critical to kustomize function. + fieldValidator ifc.Validator +} + +func NewDepProvider() *DepProvider { + rf := resource.NewFactory(&hasher.Hasher{}) + return &DepProvider{ + resourceFactory: rf, + fieldValidator: validate.NewFieldValidator(), + } +} + +func NewDefaultDepProvider() *DepProvider { + return NewDepProvider() +} + +func (dp *DepProvider) GetResourceFactory() *resource.Factory { + return dp.resourceFactory +} + +func (dp *DepProvider) GetFieldValidator() ifc.Validator { + return dp.fieldValidator +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/factory.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/factory.go new file mode 100644 index 000000000..9ec0fe396 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/factory.go @@ -0,0 +1,145 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resmap + +import ( + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/kusterr" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Factory makes instances of ResMap. +type Factory struct { + // Makes resources. + resF *resource.Factory +} + +// NewFactory returns a new resmap.Factory. +func NewFactory(rf *resource.Factory) *Factory { + return &Factory{resF: rf} +} + +// RF returns a resource.Factory. +func (rmF *Factory) RF() *resource.Factory { + return rmF.resF +} + +func New() ResMap { + return newOne() +} + +// FromResource returns a ResMap with one entry. +func (rmF *Factory) FromResource(res *resource.Resource) ResMap { + m, err := newResMapFromResourceSlice([]*resource.Resource{res}) + if err != nil { + panic(err) + } + return m +} + +// FromResourceSlice returns a ResMap with a slice of resources. +func (rmF *Factory) FromResourceSlice(ress []*resource.Resource) ResMap { + m, err := newResMapFromResourceSlice(ress) + if err != nil { + panic(err) + } + return m +} + +// FromFile returns a ResMap given a resource path. +func (rmF *Factory) FromFile( + loader ifc.Loader, path string) (ResMap, error) { + content, err := loader.Load(path) + if err != nil { + return nil, err + } + m, err := rmF.NewResMapFromBytes(content) + if err != nil { + return nil, kusterr.Handler(err, path) + } + return m, nil +} + +// NewResMapFromBytes decodes a list of objects in byte array format. +func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) { + resources, err := rmF.resF.SliceFromBytes(b) + if err != nil { + return nil, err + } + return newResMapFromResourceSlice(resources) +} + +// NewResMapFromConfigMapArgs returns a Resource slice given +// a configmap metadata slice from kustomization file. +func (rmF *Factory) NewResMapFromConfigMapArgs( + kvLdr ifc.KvLoader, argList []types.ConfigMapArgs) (ResMap, error) { + var resources []*resource.Resource + for _, args := range argList { + res, err := rmF.resF.MakeConfigMap(kvLdr, &args) + if err != nil { + return nil, errors.Wrap(err, "NewResMapFromConfigMapArgs") + } + resources = append(resources, res) + } + return newResMapFromResourceSlice(resources) +} + +// FromConfigMapArgs creates a new ResMap containing one ConfigMap. +func (rmF *Factory) FromConfigMapArgs( + kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) { + res, err := rmF.resF.MakeConfigMap(kvLdr, &args) + if err != nil { + return nil, err + } + return rmF.FromResource(res), nil +} + +// NewResMapFromSecretArgs takes a SecretArgs slice, generates +// secrets from each entry, and accumulates them in a ResMap. +func (rmF *Factory) NewResMapFromSecretArgs( + kvLdr ifc.KvLoader, argsList []types.SecretArgs) (ResMap, error) { + var resources []*resource.Resource + for _, args := range argsList { + res, err := rmF.resF.MakeSecret(kvLdr, &args) + if err != nil { + return nil, errors.Wrap(err, "NewResMapFromSecretArgs") + } + resources = append(resources, res) + } + return newResMapFromResourceSlice(resources) +} + +// FromSecretArgs creates a new ResMap containing one secret. +func (rmF *Factory) FromSecretArgs( + kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) { + res, err := rmF.resF.MakeSecret(kvLdr, &args) + if err != nil { + return nil, err + } + return rmF.FromResource(res), nil +} + +func newResMapFromResourceSlice( + resources []*resource.Resource) (ResMap, error) { + result := New() + for _, res := range resources { + err := result.Append(res) + if err != nil { + return nil, err + } + } + return result, nil +} + +// NewResMapFromRNodeSlice returns a ResMap from a slice of RNodes +func (rmF *Factory) NewResMapFromRNodeSlice(s []*yaml.RNode) (ResMap, error) { + rs, err := rmF.resF.ResourcesFromRNodes(s) + if err != nil { + return nil, err + } + return newResMapFromResourceSlice(rs) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/factory_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/factory_test.go new file mode 100644 index 000000000..489c99424 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/factory_test.go @@ -0,0 +1,354 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resmap_test + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/kv" + "sigs.k8s.io/kustomize/api/loader" + . "sigs.k8s.io/kustomize/api/resmap" + resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestFromFile(t *testing.T) { + resourceStr := `apiVersion: apps/v1 +kind: Deployment +metadata: + name: dply1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dply2 +--- +# some comment +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dply2 + namespace: test +--- +` + expected := resmaptest_test.NewRmBuilder(t, rf). + Add(map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "dply1", + }}). + Add(map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "dply2", + }}). + Add(map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "dply2", + "namespace": "test", + }}).ResMap() + expYaml, err := expected.AsYaml() + assert.NoError(t, err) + + fSys := filesys.MakeFsInMemory() + assert.NoError(t, fSys.WriteFile("deployment.yaml", []byte(resourceStr))) + + ldr, err := loader.NewLoader( + loader.RestrictionRootOnly, filesys.Separator, fSys) + assert.NoError(t, err) + + m, err := rmF.FromFile(ldr, "deployment.yaml") + assert.NoError(t, err) + mYaml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, expYaml, mYaml) +} + +func TestFromBytes(t *testing.T) { + encoded := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm2 +`) + expected := resmaptest_test.NewRmBuilder(t, rf). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }}). + Add(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + }}).ResMap() + expYaml, err := expected.AsYaml() + assert.NoError(t, err) + m, err := rmF.NewResMapFromBytes(encoded) + assert.NoError(t, err) + mYaml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, expYaml, mYaml) +} + +func TestNewFromConfigMaps(t *testing.T) { + type testCase struct { + description string + input []types.ConfigMapArgs + filepath string + content string + expected ResMap + } + + fSys := filesys.MakeFsInMemory() + ldr, err := loader.NewLoader( + loader.RestrictionRootOnly, filesys.Separator, fSys) + if err != nil { + t.Fatal(err) + } + kvLdr := kv.NewLoader(ldr, valtest_test.MakeFakeValidator()) + testCases := []testCase{ + { + description: "construct config map from env", + input: []types.ConfigMapArgs{ + { + GeneratorArgs: types.GeneratorArgs{ + Name: "envConfigMap", + KvPairSources: types.KvPairSources{ + EnvSources: []string{"app.env"}, + }, + }, + }, + }, + filepath: "app.env", + content: "DB_USERNAME=admin\nDB_PASSWORD=somepw", + expected: resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "envConfigMap", + }, + "data": map[string]interface{}{ + "DB_USERNAME": "admin", + "DB_PASSWORD": "somepw", + }}).ResMap(), + }, + + { + description: "construct config map from file", + input: []types.ConfigMapArgs{{ + GeneratorArgs: types.GeneratorArgs{ + Name: "fileConfigMap", + KvPairSources: types.KvPairSources{ + FileSources: []string{"app-init.ini"}, + }, + }, + }, + }, + filepath: "app-init.ini", + content: "FOO=bar\nBAR=baz\n", + expected: resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "fileConfigMap", + }, + "data": map[string]interface{}{ + "app-init.ini": `FOO=bar +BAR=baz +`, + }, + }).ResMap(), + }, + { + description: "construct config map from literal", + input: []types.ConfigMapArgs{ + { + GeneratorArgs: types.GeneratorArgs{ + Name: "literalConfigMap", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{"a=x", "b=y", "c=\"Good Morning\"", "d=\"false\""}, + }, + }, + }, + }, + expected: resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "literalConfigMap", + }, + "data": map[string]interface{}{ + "a": "x", + "b": "y", + "c": "Good Morning", + "d": "false", + }, + }).ResMap(), + }, + + // TODO: add testcase for data coming from multiple sources like + // files/literal/env etc. + } + for _, tc := range testCases { + if tc.filepath != "" { + if fErr := fSys.WriteFile(tc.filepath, []byte(tc.content)); fErr != nil { + t.Fatalf("error adding file '%s': %v\n", tc.filepath, fErr) + } + } + r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input) + assert.NoError(t, err, tc.description) + r.RemoveBuildAnnotations() + rYaml, err := r.AsYaml() + assert.NoError(t, err, tc.description) + tc.expected.RemoveBuildAnnotations() + expYaml, err := tc.expected.AsYaml() + assert.NoError(t, err, tc.description) + assert.Equal(t, expYaml, rYaml) + } +} + +func TestNewResMapFromSecretArgs(t *testing.T) { + secrets := []types.SecretArgs{ + { + GeneratorArgs: types.GeneratorArgs{ + Name: "apple", + KvPairSources: types.KvPairSources{ + LiteralSources: []string{ + "DB_USERNAME=admin", + "DB_PASSWORD=somepw", + }, + }, + }, + Type: ifc.SecretTypeOpaque, + }, + } + fSys := filesys.MakeFsInMemory() + fSys.Mkdir(filesys.SelfDir) + + actual, err := rmF.NewResMapFromSecretArgs( + kv.NewLoader( + loader.NewFileLoaderAtRoot(fSys), + valtest_test.MakeFakeValidator()), secrets) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + actual.RemoveBuildAnnotations() + actYaml, err := actual.AsYaml() + assert.NoError(t, err) + + expected := resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "apple", + }, + "type": ifc.SecretTypeOpaque, + "data": map[string]interface{}{ + "DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")), + "DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")), + }, + }).ResMap() + expYaml, err := expected.AsYaml() + assert.NoError(t, err) + + assert.Equal(t, string(expYaml), string(actYaml)) +} + +func TestFromRNodeSlice(t *testing.T) { + type testcase struct { + input string + expected ResMap + } + testcases := map[string]testcase{ + "no resource": { + input: "---", + expected: resmaptest_test.NewRmBuilder(t, rf).ResMap(), + }, + "single resource": { + input: `apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: namespace-reader +rules: +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - watch + - list + `, + expected: resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRole", + "metadata": map[string]interface{}{ + "name": "namespace-reader", + }, + "rules": []interface{}{ + map[string]interface{}{ + "apiGroups": []interface{}{ + "", + }, + "resources": []interface{}{ + "namespaces", + }, + "verbs": []interface{}{ + "get", + "watch", + "list", + }, + }, + }, + }).ResMap(), + }, + "local config": { + // local config should be ignored + input: `apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config + annotations: + config.kubernetes.io/local-config: 'true' +`, + expected: resmaptest_test.NewRmBuilder(t, rf).ResMap(), + }, + } + for name := range testcases { + tc := testcases[name] + t.Run(name, func(t *testing.T) { + rm, err := rmF.NewResMapFromRNodeSlice( + []*yaml.RNode{yaml.MustParse(tc.input)}) + if err != nil { + t.Fatalf("unexpected error in test case [%s]: %v", name, err) + } + if err = tc.expected.ErrorIfNotEqualLists(rm); err != nil { + t.Fatalf("error in test case [%s]: %s", name, err) + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/idslice.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/idslice.go new file mode 100644 index 000000000..8c25cbb2a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/idslice.go @@ -0,0 +1,37 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resmap + +import ( + "sort" + + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// IdSlice implements the sort interface. +type IdSlice []resid.ResId + +var _ sort.Interface = IdSlice{} + +func (a IdSlice) Len() int { return len(a) } +func (a IdSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a IdSlice) Less(i, j int) bool { + if !a[i].Gvk.Equals(a[j].Gvk) { + return a[i].Gvk.IsLessThan(a[j].Gvk) + } + return a[i].LegacySortString() < a[j].LegacySortString() +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/idslice_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/idslice_test.go new file mode 100644 index 000000000..6e4e0fe8e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/idslice_test.go @@ -0,0 +1,52 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resmap + +import ( + "reflect" + "sort" + "testing" + + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestLess(t *testing.T) { + ids := IdSlice{ + resid.NewResIdKindOnly("ConfigMap", "cm"), + resid.NewResIdKindOnly("Pod", "pod"), + resid.NewResIdKindOnly("Namespace", "ns1"), + resid.NewResIdKindOnly("Namespace", "ns2"), + resid.NewResIdKindOnly("Role", "ro"), + resid.NewResIdKindOnly("RoleBinding", "rb"), + resid.NewResIdKindOnly("CustomResourceDefinition", "crd"), + resid.NewResIdKindOnly("ServiceAccount", "sa"), + } + expected := IdSlice{ + resid.NewResIdKindOnly("Namespace", "ns1"), + resid.NewResIdKindOnly("Namespace", "ns2"), + resid.NewResIdKindOnly("CustomResourceDefinition", "crd"), + resid.NewResIdKindOnly("ServiceAccount", "sa"), + resid.NewResIdKindOnly("Role", "ro"), + resid.NewResIdKindOnly("RoleBinding", "rb"), + resid.NewResIdKindOnly("ConfigMap", "cm"), + resid.NewResIdKindOnly("Pod", "pod"), + } + sort.Sort(ids) + if !reflect.DeepEqual(ids, expected) { + t.Fatalf("expected %+v but got %+v", expected, ids) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/resmap.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/resmap.go new file mode 100644 index 000000000..ea913ba6b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/resmap.go @@ -0,0 +1,333 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package resmap implements a map from ResId to Resource that +// tracks all resources in a kustomization. +package resmap + +import ( + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// A Transformer modifies an instance of ResMap. +type Transformer interface { + // Transform modifies data in the argument, + // e.g. adding labels to resources that can be labelled. + Transform(m ResMap) error +} + +// A TransformerWithProperties contains a Transformer and stores +// some of its properties +type TransformerWithProperties struct { + Transformer + Origin *resource.Origin +} + +// A Generator creates an instance of ResMap. +type Generator interface { + Generate() (ResMap, error) +} + +// A GeneratorWithProperties contains a Generator and stores +// some of its properties +type GeneratorWithProperties struct { + Generator + Origin *resource.Origin +} + +// Something that's configurable accepts an +// instance of PluginHelpers and a raw config +// object (YAML in []byte form). +type Configurable interface { + Config(h *PluginHelpers, config []byte) error +} + +// NewPluginHelpers makes an instance of PluginHelpers. +func NewPluginHelpers( + ldr ifc.Loader, v ifc.Validator, rf *Factory, + pc *types.PluginConfig) *PluginHelpers { + return &PluginHelpers{ldr: ldr, v: v, rf: rf, pc: pc} +} + +// PluginHelpers holds things that any or all plugins might need. +// This should be available to each plugin, in addition to +// any plugin-specific configuration. +type PluginHelpers struct { + ldr ifc.Loader + v ifc.Validator + rf *Factory + pc *types.PluginConfig +} + +func (c *PluginHelpers) GeneralConfig() *types.PluginConfig { + return c.pc +} + +func (c *PluginHelpers) Loader() ifc.Loader { + return c.ldr +} + +func (c *PluginHelpers) ResmapFactory() *Factory { + return c.rf +} + +func (c *PluginHelpers) Validator() ifc.Validator { + return c.v +} + +type GeneratorPlugin interface { + Generator + Configurable +} + +type TransformerPlugin interface { + Transformer + Configurable +} + +// ResMap is an interface describing operations on the +// core kustomize data structure, a list of Resources. +// +// Every Resource has two ResIds: OrgId and CurId. +// +// In a ResMap, no two resources may have the same CurId, +// but they may have the same OrgId. The latter can happen +// when mixing two or more different overlays apply different +// transformations to a common base. When looking for a +// resource to transform, try the OrgId first, and if this +// fails or finds too many, it might make sense to then try +// the CurrId. Depends on the situation. +// +// TODO: get rid of this interface (use bare resWrangler). +// There aren't multiple implementations any more. +type ResMap interface { + // Size reports the number of resources. + Size() int + + // Resources provides a discardable slice + // of resource pointers, returned in the order + // as appended. + Resources() []*resource.Resource + + // Append adds a Resource. Error on CurId collision. + // + // A class invariant of ResMap is that all of its + // resources must differ in their value of + // CurId(), aka current Id. The Id is the tuple + // of {namespace, group, version, kind, name} + // (see ResId). + // + // This invariant reflects the invariant of a + // kubernetes cluster, where if one tries to add + // a resource to the cluster whose Id matches + // that of a resource already in the cluster, + // only two outcomes are allowed. Either the + // incoming resource is _merged_ into the existing + // one, or the incoming resource is rejected. + // One cannot end up with two resources + // in the cluster with the same Id. + Append(*resource.Resource) error + + // AppendAll appends another ResMap to self, + // failing on any CurId collision. + AppendAll(ResMap) error + + // AbsorbAll appends, replaces or merges the contents + // of another ResMap into self, + // allowing and sometimes demanding ID collisions. + // A collision would be demanded, say, when a generated + // ConfigMap has the "replace" option in its generation + // instructions, meaning it _must_ replace + // something in the known set of resources. + // If a resource id for resource X is found to already + // be in self, then the behavior field for X must + // be BehaviorMerge or BehaviorReplace. If X is not in + // self, then its behavior _cannot_ be merge or replace. + AbsorbAll(ResMap) error + + // AddOriginAnnotation will add the provided origin as + // an origin annotation to all resources in the ResMap, if + // the origin is not nil. + AddOriginAnnotation(origin *resource.Origin) error + + // RemoveOriginAnnotation will remove the origin annotation + // from all resources in the ResMap + RemoveOriginAnnotations() error + + // AddTransformerAnnotation will add the provided origin as + // an origin annotation if the resource doesn't have one; a + // transformer annotation otherwise; to all resources in + // ResMap + AddTransformerAnnotation(origin *resource.Origin) error + + // RemoveTransformerAnnotation will remove the transformer annotation + // from all resources in the ResMap + RemoveTransformerAnnotations() error + + // AnnotateAll annotates all resources in the ResMap with + // the provided key value pair. + AnnotateAll(key string, value string) error + + // AsYaml returns the yaml form of resources. + AsYaml() ([]byte, error) + + // GetByIndex returns a resource at the given index, + // nil if out of range. + GetByIndex(int) *resource.Resource + + // GetIndexOfCurrentId returns the index of the resource + // with the given CurId. + // Returns error if there is more than one match. + // Returns (-1, nil) if there is no match. + GetIndexOfCurrentId(id resid.ResId) (int, error) + + // GetMatchingResourcesByCurrentId returns the resources + // who's CurId is matched by the argument. + GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource + + // GetMatchingResourcesByAnyId returns the resources + // who's current or previous IDs is matched by the argument. + GetMatchingResourcesByAnyId(matches IdMatcher) []*resource.Resource + + // GetByCurrentId is shorthand for calling + // GetMatchingResourcesByCurrentId with a matcher requiring + // an exact match, returning an error on multiple or no matches. + GetByCurrentId(resid.ResId) (*resource.Resource, error) + + // GetById is shorthand for calling + // GetMatchingResourcesByAnyId with a matcher requiring + // an exact match, returning an error on multiple or no matches. + GetById(resid.ResId) (*resource.Resource, error) + + // GroupedByCurrentNamespace returns a map of namespace + // to a slice of *Resource in that namespace. + // Cluster-scoped Resources are not included (see ClusterScoped). + // Resources with an empty namespace are placed + // in the resid.DefaultNamespace entry. + GroupedByCurrentNamespace() map[string][]*resource.Resource + + // GroupedByOriginalNamespace performs as GroupByNamespace + // but use the original namespace instead of the current + // one to perform the grouping. + GroupedByOriginalNamespace() map[string][]*resource.Resource + + // ClusterScoped returns a slice of resources that + // cannot be placed in a namespace, e.g. + // Node, ClusterRole, Namespace itself, etc. + ClusterScoped() []*resource.Resource + + // AllIds returns all CurrentIds. + AllIds() []resid.ResId + + // Replace replaces the resource with the matching CurId. + // Error if there's no match or more than one match. + // Returns the index where the replacement happened. + Replace(*resource.Resource) (int, error) + + // Remove removes the resource whose CurId matches the argument. + // Error if not found. + Remove(resid.ResId) error + + // Clear removes all resources and Ids. + Clear() + + // DropEmpties drops empty resources from the ResMap. + DropEmpties() + + // SubsetThatCouldBeReferencedByResource returns a ResMap subset + // of self with resources that could be referenced by the + // resource argument. + // This is a filter; it excludes things that cannot be + // referenced by the resource, e.g. objects in other + // namespaces. Cluster wide objects are never excluded. + SubsetThatCouldBeReferencedByResource(*resource.Resource) (ResMap, error) + + // DeAnchor replaces YAML aliases with structured data copied from anchors. + // This cannot be undone; if desired, call DeepCopy first. + // Subsequent marshalling to YAML will no longer have anchor + // definitions ('&') or aliases ('*'). + // + // Anchors are not expected to work across YAML 'documents'. + // If three resources are loaded from one file containing three YAML docs: + // + // {resourceA} + // --- + // {resourceB} + // --- + // {resourceC} + // + // then anchors defined in A cannot be seen from B and C and vice versa. + // OTOH, cross-resource links (a field in B referencing fields in A) will + // work if the resources are gathered in a ResourceList: + // + // apiVersion: config.kubernetes.io/v1 + // kind: ResourceList + // metadata: + // name: someList + // items: + // - {resourceA} + // - {resourceB} + // - {resourceC} + // + DeAnchor() error + + // DeepCopy copies the ResMap and underlying resources. + DeepCopy() ResMap + + // ShallowCopy copies the ResMap but + // not the underlying resources. + ShallowCopy() ResMap + + // ErrorIfNotEqualSets returns an error if the + // argument doesn't have the same resources as self. + // Ordering is _not_ taken into account, + // as this function was solely used in tests written + // before internal resource order was maintained, + // and those tests are initialized with maps which + // by definition have random ordering, and will + // fail spuriously. + // TODO: modify tests to not use resmap.FromMap, + // TODO: - and replace this with a stricter equals. + ErrorIfNotEqualSets(ResMap) error + + // ErrorIfNotEqualLists returns an error if the + // argument doesn't have the resource objects + // data as self, in the same order. + // Meta information is ignored; this is similar + // to comparing the AsYaml() strings, but allows + // for more informed errors on not equals. + ErrorIfNotEqualLists(ResMap) error + + // Debug prints the ResMap. + Debug(title string) + + // Select returns a list of resources that + // are selected by a Selector + Select(types.Selector) ([]*resource.Resource, error) + + // ToRNodeSlice returns a copy of the resources as RNodes. + ToRNodeSlice() []*yaml.RNode + + // ApplySmPatch applies a strategic-merge patch to the + // selected set of resources. + ApplySmPatch( + selectedSet *resource.IdSet, patch *resource.Resource) error + + // RemoveBuildAnnotations removes annotations created by the build process. + RemoveBuildAnnotations() + + // ApplyFilter applies an RNode filter to all Resources in the ResMap. + // TODO: Send/recover ancillary Resource data to/from subprocesses. + // Assure that the ancillary data in Resource (everything not in the RNode) + // is sent to and re-captured from transformer subprocess (as the process + // might edit that information). One way to do this would be to solely use + // RNode metadata annotation reading and writing instead of using Resource + // struct data members, i.e. the Resource struct is replaced by RNode + // and use of (slow) k8s metadata annotations inside the RNode. + ApplyFilter(f kio.Filter) error +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/resmap_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/resmap_test.go new file mode 100644 index 000000000..f250c9775 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/resmap_test.go @@ -0,0 +1,6 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resmap_test + +// See reswrangler_test.go diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/reswrangler.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/reswrangler.go new file mode 100644 index 000000000..5ce98dc77 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/reswrangler.go @@ -0,0 +1,765 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resmap + +import ( + "bytes" + "fmt" + "reflect" + + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/filters/annotations" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/resid" + kyaml "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// resWrangler implements ResMap. +type resWrangler struct { + // Resource list maintained in load (append) order. + // This is important for transformers, which must + // be performed in a specific order, and for users + // who for whatever reasons wish the order they + // specify in kustomizations to be maintained and + // available as an option for final YAML rendering. + rList []*resource.Resource +} + +func newOne() *resWrangler { + result := &resWrangler{} + result.Clear() + return result +} + +// Clear implements ResMap. +func (m *resWrangler) Clear() { + m.rList = nil +} + +// DropEmpties quickly drops empty resources. +// It doesn't use Append, which checks for Id collisions. +func (m *resWrangler) DropEmpties() { + var rList []*resource.Resource + for _, r := range m.rList { + if !r.IsNilOrEmpty() { + rList = append(rList, r) + } + } + m.rList = rList +} + +// Size implements ResMap. +func (m *resWrangler) Size() int { + return len(m.rList) +} + +func (m *resWrangler) indexOfResource(other *resource.Resource) int { + for i, r := range m.rList { + if r == other { + return i + } + } + return -1 +} + +// Resources implements ResMap. +func (m *resWrangler) Resources() []*resource.Resource { + tmp := make([]*resource.Resource, len(m.rList)) + copy(tmp, m.rList) + return tmp +} + +// Append implements ResMap. +func (m *resWrangler) Append(res *resource.Resource) error { + id := res.CurId() + if r := m.GetMatchingResourcesByCurrentId(id.Equals); len(r) > 0 { + return fmt.Errorf( + "may not add resource with an already registered id: %s", id) + } + m.append(res) + return nil +} + +// append appends without performing an Id check +func (m *resWrangler) append(res *resource.Resource) { + m.rList = append(m.rList, res) +} + +// Remove implements ResMap. +func (m *resWrangler) Remove(adios resid.ResId) error { + var rList []*resource.Resource + for _, r := range m.rList { + if r.CurId() != adios { + rList = append(rList, r) + } + } + if len(rList) != m.Size()-1 { + return fmt.Errorf("id %s not found in removal", adios) + } + m.rList = rList + return nil +} + +// Replace implements ResMap. +func (m *resWrangler) Replace(res *resource.Resource) (int, error) { + id := res.CurId() + i, err := m.GetIndexOfCurrentId(id) + if err != nil { + return -1, errors.Wrap(err, "in Replace") + } + if i < 0 { + return -1, fmt.Errorf("cannot find resource with id %s to replace", id) + } + m.rList[i] = res + return i, nil +} + +// AllIds implements ResMap. +func (m *resWrangler) AllIds() (ids []resid.ResId) { + ids = make([]resid.ResId, m.Size()) + for i, r := range m.rList { + ids[i] = r.CurId() + } + return +} + +// Debug implements ResMap. +func (m *resWrangler) Debug(title string) { + fmt.Println("--------------------------- " + title) + firstObj := true + for i, r := range m.rList { + if firstObj { + firstObj = false + } else { + fmt.Println("---") + } + fmt.Printf("# %d %s\n%s\n", i, r.OrgId(), r.String()) + } +} + +type IdMatcher func(resid.ResId) bool + +// GetByIndex implements ResMap. +func (m *resWrangler) GetByIndex(i int) *resource.Resource { + if i < 0 || i >= m.Size() { + return nil + } + return m.rList[i] +} + +// GetIndexOfCurrentId implements ResMap. +func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) { + count := 0 + result := -1 + for i, r := range m.rList { + if id.Equals(r.CurId()) { + count++ + result = i + } + } + if count > 1 { + return -1, fmt.Errorf("id matched %d resources", count) + } + return result, nil +} + +type IdFromResource func(r *resource.Resource) resid.ResId + +func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() } + +// GetMatchingResourcesByCurrentId implements ResMap. +func (m *resWrangler) GetMatchingResourcesByCurrentId( + matches IdMatcher) []*resource.Resource { + return m.filteredById(matches, GetCurrentId) +} + +// GetMatchingResourcesByAnyId implements ResMap. +func (m *resWrangler) GetMatchingResourcesByAnyId( + matches IdMatcher) []*resource.Resource { + var result []*resource.Resource + for _, r := range m.rList { + for _, id := range append(r.PrevIds(), r.CurId()) { + if matches(id) { + result = append(result, r) + break + } + } + } + return result +} + +func (m *resWrangler) filteredById( + matches IdMatcher, idGetter IdFromResource) []*resource.Resource { + var result []*resource.Resource + for _, r := range m.rList { + if matches(idGetter(r)) { + result = append(result, r) + } + } + return result +} + +// GetByCurrentId implements ResMap. +func (m *resWrangler) GetByCurrentId( + id resid.ResId) (*resource.Resource, error) { + return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current") +} + +// GetById implements ResMap. +func (m *resWrangler) GetById( + id resid.ResId) (*resource.Resource, error) { + r, err := demandOneMatch(m.GetMatchingResourcesByAnyId, id, "Id") + if err != nil { + return nil, fmt.Errorf( + "%s; failed to find unique target for patch %s", + err.Error(), id.String()) + } + return r, nil +} + +type resFinder func(IdMatcher) []*resource.Resource + +func demandOneMatch( + f resFinder, id resid.ResId, s string) (*resource.Resource, error) { + r := f(id.Equals) + if len(r) == 1 { + return r[0], nil + } + if len(r) > 1 { + return nil, fmt.Errorf("multiple matches for %s %s", s, id) + } + return nil, fmt.Errorf("no matches for %s %s", s, id) +} + +// GroupedByCurrentNamespace implements ResMap. +func (m *resWrangler) GroupedByCurrentNamespace() map[string][]*resource.Resource { + items := m.groupedByCurrentNamespace() + delete(items, resid.TotallyNotANamespace) + return items +} + +// ClusterScoped implements ResMap. +func (m *resWrangler) ClusterScoped() []*resource.Resource { + return m.groupedByCurrentNamespace()[resid.TotallyNotANamespace] +} + +func (m *resWrangler) groupedByCurrentNamespace() map[string][]*resource.Resource { + byNamespace := make(map[string][]*resource.Resource) + for _, res := range m.rList { + namespace := res.CurId().EffectiveNamespace() + if _, found := byNamespace[namespace]; !found { + byNamespace[namespace] = []*resource.Resource{} + } + byNamespace[namespace] = append(byNamespace[namespace], res) + } + return byNamespace +} + +// GroupedByOriginalNamespace implements ResMap. +func (m *resWrangler) GroupedByOriginalNamespace() map[string][]*resource.Resource { + items := m.groupedByOriginalNamespace() + delete(items, resid.TotallyNotANamespace) + return items +} + +func (m *resWrangler) groupedByOriginalNamespace() map[string][]*resource.Resource { + byNamespace := make(map[string][]*resource.Resource) + for _, res := range m.rList { + namespace := res.OrgId().EffectiveNamespace() + if _, found := byNamespace[namespace]; !found { + byNamespace[namespace] = []*resource.Resource{} + } + byNamespace[namespace] = append(byNamespace[namespace], res) + } + return byNamespace +} + +// AsYaml implements ResMap. +func (m *resWrangler) AsYaml() ([]byte, error) { + firstObj := true + var b []byte + buf := bytes.NewBuffer(b) + for _, res := range m.rList { + out, err := res.AsYAML() + if err != nil { + m, _ := res.Map() + return nil, errors.Wrapf(err, "%#v", m) + } + if firstObj { + firstObj = false + } else { + if _, err = buf.WriteString("---\n"); err != nil { + return nil, err + } + } + if _, err = buf.Write(out); err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} + +// ErrorIfNotEqualSets implements ResMap. +func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error { + m2, ok := other.(*resWrangler) + if !ok { + return fmt.Errorf("bad cast to resWrangler 1") + } + if m.Size() != m2.Size() { + return fmt.Errorf( + "lists have different number of entries: %#v doesn't equal %#v", + m.rList, m2.rList) + } + seen := make(map[int]bool) + for _, r1 := range m.rList { + id := r1.CurId() + others := m2.GetMatchingResourcesByCurrentId(id.Equals) + if len(others) == 0 { + return fmt.Errorf( + "id in self missing from other; id: %s", id) + } + if len(others) > 1 { + return fmt.Errorf( + "id in self matches %d in other; id: %s", len(others), id) + } + r2 := others[0] + if !reflect.DeepEqual(r1.RNode, r2.RNode) { + return fmt.Errorf( + "nodes unequal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n", + r1, r2, r1, r2) + } + seen[m2.indexOfResource(r2)] = true + } + if len(seen) != m.Size() { + return fmt.Errorf("counting problem %d != %d", len(seen), m.Size()) + } + return nil +} + +// ErrorIfNotEqualLists implements ResMap. +func (m *resWrangler) ErrorIfNotEqualLists(other ResMap) error { + m2, ok := other.(*resWrangler) + if !ok { + return fmt.Errorf("bad cast to resWrangler 2") + } + if m.Size() != m2.Size() { + return fmt.Errorf( + "lists have different number of entries: %#v doesn't equal %#v", + m.rList, m2.rList) + } + for i, r1 := range m.rList { + r2 := m2.rList[i] + if err := r1.ErrIfNotEquals(r2); err != nil { + return err + } + } + return nil +} + +type resCopier func(r *resource.Resource) *resource.Resource + +// ShallowCopy implements ResMap. +func (m *resWrangler) ShallowCopy() ResMap { + return m.makeCopy( + func(r *resource.Resource) *resource.Resource { + return r + }) +} + +// DeepCopy implements ResMap. +func (m *resWrangler) DeepCopy() ResMap { + return m.makeCopy( + func(r *resource.Resource) *resource.Resource { + return r.DeepCopy() + }) +} + +// makeCopy copies the ResMap. +func (m *resWrangler) makeCopy(copier resCopier) ResMap { + result := &resWrangler{} + result.rList = make([]*resource.Resource, m.Size()) + for i, r := range m.rList { + result.rList[i] = copier(r) + } + return result +} + +// SubsetThatCouldBeReferencedByResource implements ResMap. +func (m *resWrangler) SubsetThatCouldBeReferencedByResource( + referrer *resource.Resource) (ResMap, error) { + referrerId := referrer.CurId() + if referrerId.IsClusterScoped() { + // A cluster scoped resource can refer to anything. + return m, nil + } + result := newOne() + roleBindingNamespaces, err := getNamespacesForRoleBinding(referrer) + if err != nil { + return nil, err + } + for _, possibleTarget := range m.rList { + id := possibleTarget.CurId() + if id.IsClusterScoped() { + // A cluster-scoped resource can be referred to by anything. + result.append(possibleTarget) + continue + } + if id.IsNsEquals(referrerId) { + // The two objects are in the same namespace. + result.append(possibleTarget) + continue + } + // The two objects are namespaced (not cluster-scoped), AND + // are in different namespaces. + // There's still a chance they can refer to each other. + if roleBindingNamespaces[possibleTarget.GetNamespace()] { + result.append(possibleTarget) + } + } + return result, nil +} + +// getNamespacesForRoleBinding returns referenced ServiceAccount namespaces +// if the resource is a RoleBinding +func getNamespacesForRoleBinding(r *resource.Resource) (map[string]bool, error) { + result := make(map[string]bool) + if r.GetKind() != "RoleBinding" { + return result, nil + } + //nolint staticcheck + subjects, err := r.GetSlice("subjects") + if err != nil || subjects == nil { + return result, nil + } + for _, s := range subjects { + subject := s.(map[string]interface{}) + if ns, ok1 := subject["namespace"]; ok1 { + if kind, ok2 := subject["kind"]; ok2 { + if kind.(string) == "ServiceAccount" { + if n, ok3 := ns.(string); ok3 { + result[n] = true + } else { + return nil, errors.New(fmt.Sprintf("Invalid Input: namespace is blank for resource %q\n", r.CurId())) + } + } + } + } + } + return result, nil +} + +// AppendAll implements ResMap. +func (m *resWrangler) AppendAll(other ResMap) error { + if other == nil { + return nil + } + m2, ok := other.(*resWrangler) + if !ok { + return fmt.Errorf("bad cast to resWrangler 3") + } + return m.appendAll(m2.rList) +} + +// appendAll appends all the resources, error on Id collision. +func (m *resWrangler) appendAll(list []*resource.Resource) error { + for _, res := range list { + if err := m.Append(res); err != nil { + return err + } + } + return nil +} + +// AbsorbAll implements ResMap. +func (m *resWrangler) AbsorbAll(other ResMap) error { + if other == nil { + return nil + } + m2, ok := other.(*resWrangler) + if !ok { + return fmt.Errorf("bad cast to resWrangler 4") + } + for _, r := range m2.rList { + err := m.appendReplaceOrMerge(r) + if err != nil { + return err + } + } + return nil +} + +// AddOriginAnnotation implements ResMap. +func (m *resWrangler) AddOriginAnnotation(origin *resource.Origin) error { + if origin == nil { + return nil + } + for _, res := range m.rList { + or, err := res.GetOrigin() + if or != nil || err != nil { + // if any resources already have an origin annotation, + // skip it + continue + } + if err := res.SetOrigin(origin); err != nil { + return err + } + } + return nil +} + +// RemoveOriginAnnotation implements ResMap +func (m *resWrangler) RemoveOriginAnnotations() error { + for _, res := range m.rList { + if err := res.SetOrigin(nil); err != nil { + return err + } + } + return nil +} + +// AddTransformerAnnotation implements ResMap +func (m *resWrangler) AddTransformerAnnotation(origin *resource.Origin) error { + for _, res := range m.rList { + or, err := res.GetOrigin() + if err != nil { + return err + } + if or == nil { + // the resource does not have an origin annotation, so + // we assume that the transformer generated the resource + // rather than modifying it + err = res.SetOrigin(origin) + } else { + // the resource already has an origin annotation, so we + // record the provided origin as a transformation + err = res.AddTransformation(origin) + } + if err != nil { + return err + } + } + return nil +} + +// RemoveTransformerAnnotations implements ResMap +func (m *resWrangler) RemoveTransformerAnnotations() error { + for _, res := range m.rList { + if err := res.ClearTransformations(); err != nil { + return err + } + } + return nil +} + +func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error { + id := res.CurId() + matches := m.GetMatchingResourcesByAnyId(id.Equals) + switch len(matches) { + case 0: + switch res.Behavior() { + case types.BehaviorMerge, types.BehaviorReplace: + return fmt.Errorf( + "id %#v does not exist; cannot merge or replace", id) + default: + // presumably types.BehaviorCreate + return m.Append(res) + } + case 1: + old := matches[0] + if old == nil { + return fmt.Errorf("id lookup failure") + } + index := m.indexOfResource(old) + if index < 0 { + return fmt.Errorf("indexing problem") + } + switch res.Behavior() { + case types.BehaviorReplace: + res.CopyMergeMetaDataFieldsFrom(old) + case types.BehaviorMerge: + // ensure the origin annotation doesn't get overwritten + orig, err := old.GetOrigin() + if err != nil { + return err + } + res.CopyMergeMetaDataFieldsFrom(old) + res.MergeDataMapFrom(old) + res.MergeBinaryDataMapFrom(old) + if orig != nil { + res.SetOrigin(orig) + } + + default: + return fmt.Errorf( + "id %#v exists; behavior must be merge or replace", id) + } + i, err := m.Replace(res) + if err != nil { + return err + } + if i != index { + return fmt.Errorf("unexpected target index in replacement") + } + return nil + default: + return fmt.Errorf( + "found multiple objects %v that could accept merge of %v", + matches, id) + } +} + +// AnnotateAll implements ResMap +func (m *resWrangler) AnnotateAll(key string, value string) error { + return m.ApplyFilter(annotations.Filter{ + Annotations: map[string]string{ + key: value, + }, + FsSlice: []types.FieldSpec{{ + Path: "metadata/annotations", + CreateIfNotPresent: true, + }}, + }) +} + +// Select returns a list of resources that +// are selected by a Selector +func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) { + var result []*resource.Resource + sr, err := types.NewSelectorRegex(&s) + if err != nil { + return nil, err + } + for _, r := range m.rList { + curId := r.CurId() + orgId := r.OrgId() + + // It first tries to match with the original namespace + // then matches with the current namespace + if !sr.MatchNamespace(orgId.EffectiveNamespace()) && + !sr.MatchNamespace(curId.EffectiveNamespace()) { + continue + } + + // It first tries to match with the original name + // then matches with the current name + if !sr.MatchName(orgId.Name) && + !sr.MatchName(curId.Name) { + continue + } + + // matches the GVK + if !sr.MatchGvk(r.GetGvk()) { + continue + } + + // matches the label selector + matched, err := r.MatchesLabelSelector(s.LabelSelector) + if err != nil { + return nil, err + } + if !matched { + continue + } + + // matches the annotation selector + matched, err = r.MatchesAnnotationSelector(s.AnnotationSelector) + if err != nil { + return nil, err + } + if !matched { + continue + } + result = append(result, r) + } + return result, nil +} + +// ToRNodeSlice returns a copy of the resources as RNodes. +func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode { + result := make([]*kyaml.RNode, len(m.rList)) + for i := range m.rList { + result[i] = m.rList[i].Copy() + } + return result +} + +// DeAnchor implements ResMap. +func (m *resWrangler) DeAnchor() (err error) { + for i := range m.rList { + if err = m.rList[i].DeAnchor(); err != nil { + return err + } + } + return nil +} + +// ApplySmPatch applies the patch, and errors on Id collisions. +func (m *resWrangler) ApplySmPatch( + selectedSet *resource.IdSet, patch *resource.Resource) error { + var list []*resource.Resource + for _, res := range m.rList { + if selectedSet.Contains(res.CurId()) { + patchCopy := patch.DeepCopy() + patchCopy.CopyMergeMetaDataFieldsFrom(patch) + patchCopy.SetGvk(res.GetGvk()) + patchCopy.SetKind(patch.GetKind()) + if err := res.ApplySmPatch(patchCopy); err != nil { + return err + } + } + if !res.IsNilOrEmpty() { + list = append(list, res) + } + } + m.Clear() + return m.appendAll(list) +} + +func (m *resWrangler) RemoveBuildAnnotations() { + for _, r := range m.rList { + r.RemoveBuildAnnotations() + } +} + +// ApplyFilter implements ResMap. +func (m *resWrangler) ApplyFilter(f kio.Filter) error { + reverseLookup := make(map[*kyaml.RNode]*resource.Resource, len(m.rList)) + nodes := make([]*kyaml.RNode, len(m.rList)) + for i, r := range m.rList { + ptr := &(r.RNode) + nodes[i] = ptr + reverseLookup[ptr] = r + } + // The filter can modify nodes, but also delete and create them. + // The filtered list might be smaller or larger than the nodes list. + filtered, err := f.Filter(nodes) + if err != nil { + return err + } + // Rebuild the resmap from the filtered RNodes. + var nRList []*resource.Resource + for _, rn := range filtered { + if rn.IsNilOrEmpty() { + // A node might make it through the filter as an object, + // but still be empty. Drop such entries. + continue + } + res, ok := reverseLookup[rn] + if !ok { + // A node was created; make a Resource to wrap it. + res = &resource.Resource{ + RNode: *rn, + // Leave remaining fields empty. + // At at time of writing, seeking to eliminate those fields. + // Alternatively, could just return error on creation attempt + // until remaining fields eliminated. + } + } + nRList = append(nRList, res) + } + m.rList = nRList + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/reswrangler_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/reswrangler_test.go new file mode 100644 index 000000000..acf0cf73a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/reswrangler_test.go @@ -0,0 +1,1723 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resmap_test + +import ( + "bytes" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/filters/labels" + "sigs.k8s.io/kustomize/api/provider" + . "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var depProvider = provider.NewDefaultDepProvider() +var rf = depProvider.GetResourceFactory() +var rmF = NewFactory(rf) +var origin1 = &resource.Origin{ + Repo: "github.com/myrepo", + Ref: "master", + ConfiguredIn: "config.yaml", + ConfiguredBy: yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "builtin", + Kind: "Generator", + }, + NameMeta: yaml.NameMeta{ + Name: "my-name", + Namespace: "my-namespace", + }, + }, +} +var origin2 = &resource.Origin{ + ConfiguredIn: "../base/config.yaml", + ConfiguredBy: yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "builtin", + Kind: "Generator", + }, + NameMeta: yaml.NameMeta{ + Name: "my-name", + Namespace: "my-namespace", + }, + }, +} + +func doAppend(t *testing.T, w ResMap, r *resource.Resource) { + err := w.Append(r) + if err != nil { + t.Fatalf("append error: %v", err) + } +} +func doRemove(t *testing.T, w ResMap, id resid.ResId) { + err := w.Remove(id) + if err != nil { + t.Fatalf("remove error: %v", err) + } +} + +// Make a resource with a predictable name. +func makeCm(i int) *resource.Resource { + return rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": fmt.Sprintf("cm%03d", i), + }, + }) +} + +// Maintain the class invariant that no two +// resources can have the same CurId(). +func TestAppendRejectsDuplicateResId(t *testing.T) { + w := New() + if err := w.Append(makeCm(1)); err != nil { + t.Fatalf("append error: %v", err) + } + err := w.Append(makeCm(1)) + if err == nil { + t.Fatalf("expected append error") + } + if !strings.Contains( + err.Error(), + "may not add resource with an already registered id") { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestAppendRemove(t *testing.T) { + w1 := New() + doAppend(t, w1, makeCm(1)) + doAppend(t, w1, makeCm(2)) + doAppend(t, w1, makeCm(3)) + doAppend(t, w1, makeCm(4)) + doAppend(t, w1, makeCm(5)) + doAppend(t, w1, makeCm(6)) + doAppend(t, w1, makeCm(7)) + doRemove(t, w1, makeCm(1).OrgId()) + doRemove(t, w1, makeCm(3).OrgId()) + doRemove(t, w1, makeCm(5).OrgId()) + doRemove(t, w1, makeCm(7).OrgId()) + + w2 := New() + doAppend(t, w2, makeCm(2)) + doAppend(t, w2, makeCm(4)) + doAppend(t, w2, makeCm(6)) + if !reflect.DeepEqual(w1, w1) { + w1.Debug("w1") + w2.Debug("w2") + t.Fatalf("mismatch") + } + + err := w2.Append(makeCm(6)) + if err == nil { + t.Fatalf("expected error") + } +} + +func TestRemove(t *testing.T) { + w := New() + r := makeCm(1) + err := w.Remove(r.OrgId()) + if err == nil { + t.Fatalf("expected error") + } + err = w.Append(r) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + err = w.Remove(r.OrgId()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + err = w.Remove(r.OrgId()) + if err == nil { + t.Fatalf("expected error") + } +} + +func TestReplace(t *testing.T) { + cm5 := makeCm(5) + cm700 := makeCm(700) + otherCm5 := makeCm(5) + + w := New() + doAppend(t, w, makeCm(1)) + doAppend(t, w, makeCm(2)) + doAppend(t, w, makeCm(3)) + doAppend(t, w, makeCm(4)) + doAppend(t, w, cm5) + doAppend(t, w, makeCm(6)) + doAppend(t, w, makeCm(7)) + + oldSize := w.Size() + _, err := w.Replace(otherCm5) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if w.Size() != oldSize { + t.Fatalf("unexpected size %d", w.Size()) + } + if r, err := w.GetByCurrentId(cm5.OrgId()); err != nil || r != otherCm5 { + t.Fatalf("unexpected result r=%s, err=%v", r.CurId(), err) + } + if err := w.Append(cm5); err == nil { + t.Fatalf("expected id already there error") + } + if err := w.Remove(cm5.OrgId()); err != nil { + t.Fatalf("unexpected err: %v", err) + } + if err := w.Append(cm700); err != nil { + t.Fatalf("unexpected err: %v", err) + } + if err := w.Append(cm5); err != nil { + t.Fatalf("unexpected err: %v", err) + } +} + +func TestEncodeAsYaml(t *testing.T) { + encoded := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm2 +`) + input := resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + }).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + }, + }).ResMap() + out, err := input.AsYaml() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(out, encoded) { + t.Fatalf("%s doesn't match expected %s", out, encoded) + } +} + +func TestGetMatchingResourcesByCurrentId(t *testing.T) { + cmap := resid.NewGvk("", "v1", "ConfigMap") + + r1 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "alice", + }, + }) + r2 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "bob", + }, + }) + r3 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "bob", + "namespace": "happy", + }, + }) + r4 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "charlie", + "namespace": "happy", + }, + }) + r5 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "charlie", + "namespace": "happy", + }, + }) + + m := resmaptest_test.NewRmBuilder(t, rf). + AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap() + + result := m.GetMatchingResourcesByCurrentId( + resid.NewResId(cmap, "alice").GvknEquals) + if len(result) != 1 { + t.Fatalf("Expected single map entry but got %v", result) + } + result = m.GetMatchingResourcesByCurrentId( + resid.NewResId(cmap, "bob").GvknEquals) + if len(result) != 2 { + t.Fatalf("Expected two, got %v", result) + } + result = m.GetMatchingResourcesByCurrentId( + resid.NewResIdWithNamespace(cmap, "bob", "system").GvknEquals) + if len(result) != 2 { + t.Fatalf("Expected two but got %v", result) + } + result = m.GetMatchingResourcesByCurrentId( + resid.NewResIdWithNamespace(cmap, "bob", "happy").Equals) + if len(result) != 1 { + t.Fatalf("Expected single map entry but got %v", result) + } + result = m.GetMatchingResourcesByCurrentId( + resid.NewResId(cmap, "charlie").GvknEquals) + if len(result) != 1 { + t.Fatalf("Expected single map entry but got %v", result) + } + + // nolint:goconst + tests := []struct { + name string + matcher IdMatcher + count int + }{ + { + "match everything", + func(resid.ResId) bool { return true }, + 5, + }, + { + "match nothing", + func(resid.ResId) bool { return false }, + 0, + }, + { + "name is alice", + func(x resid.ResId) bool { return x.Name == "alice" }, + 1, + }, + { + "name is charlie", + func(x resid.ResId) bool { return x.Name == "charlie" }, + 2, + }, + { + "name is bob", + func(x resid.ResId) bool { return x.Name == "bob" }, + 2, + }, + { + "happy namespace", + func(x resid.ResId) bool { + return x.Namespace == "happy" + }, + 3, + }, + { + "happy deployment", + func(x resid.ResId) bool { + return x.Namespace == "happy" && + x.Gvk.Kind == "Deployment" + }, + 1, + }, + { + "happy ConfigMap", + func(x resid.ResId) bool { + return x.Namespace == "happy" && + x.Gvk.Kind == "ConfigMap" + }, + 2, + }, + } + for _, tst := range tests { + result := m.GetMatchingResourcesByCurrentId(tst.matcher) + if len(result) != tst.count { + t.Fatalf("test '%s'; actual: %d, expected: %d", + tst.name, len(result), tst.count) + } + } +} + +func TestGetMatchingResourcesByAnyId(t *testing.T) { + r1 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "new-alice", + "annotations": map[string]interface{}{ + "internal.config.kubernetes.io/previousKinds": "ConfigMap", + "internal.config.kubernetes.io/previousNames": "alice", + "internal.config.kubernetes.io/previousNamespaces": "default", + }, + }, + }) + r2 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "new-bob", + "annotations": map[string]interface{}{ + "internal.config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap", + "internal.config.kubernetes.io/previousNames": "bob,bob2", + "internal.config.kubernetes.io/previousNamespaces": "default,default", + }, + }, + }) + r3 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "new-bob", + "namespace": "new-happy", + "annotations": map[string]interface{}{ + "internal.config.kubernetes.io/previousKinds": "ConfigMap", + "internal.config.kubernetes.io/previousNames": "bob", + "internal.config.kubernetes.io/previousNamespaces": "happy", + }, + }, + }) + r4 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "charlie", + "namespace": "happy", + "annotations": map[string]interface{}{ + "internal.config.kubernetes.io/previousKinds": "ConfigMap", + "internal.config.kubernetes.io/previousNames": "charlie", + "internal.config.kubernetes.io/previousNamespaces": "default", + }, + }, + }) + r5 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "charlie", + "namespace": "happy", + }, + }) + + m := resmaptest_test.NewRmBuilder(t, rf). + AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap() + + // nolint:goconst + tests := []struct { + name string + matcher IdMatcher + count int + }{ + { + "match everything", + func(resid.ResId) bool { return true }, + 5, + }, + { + "match nothing", + func(resid.ResId) bool { return false }, + 0, + }, + { + "name is alice", + func(x resid.ResId) bool { return x.Name == "alice" }, + 1, + }, + { + "name is charlie", + func(x resid.ResId) bool { return x.Name == "charlie" }, + 2, + }, + { + "name is bob", + func(x resid.ResId) bool { return x.Name == "bob" }, + 2, + }, + { + "happy namespace", + func(x resid.ResId) bool { + return x.Namespace == "happy" + }, + 3, + }, + { + "happy deployment", + func(x resid.ResId) bool { + return x.Namespace == "happy" && + x.Gvk.Kind == "Deployment" + }, + 1, + }, + { + "happy ConfigMap", + func(x resid.ResId) bool { + return x.Namespace == "happy" && + x.Gvk.Kind == "ConfigMap" + }, + 2, + }, + } + for _, tst := range tests { + result := m.GetMatchingResourcesByAnyId(tst.matcher) + if len(result) != tst.count { + t.Fatalf("test '%s'; actual: %d, expected: %d", + tst.name, len(result), tst.count) + } + } +} + +func TestSubsetThatCouldBeReferencedByResource(t *testing.T) { + r1 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "alice", + }, + }) + r2 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "bob", + }, + }) + r3 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "bob", + "namespace": "happy", + }, + }) + r4 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "charlie", + "namespace": "happy", + }, + }) + r5 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "charlie", + "namespace": "happy", + }, + }) + r5.AddNamePrefix("little-") + r6 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "domino", + "namespace": "happy", + }, + }) + r6.AddNamePrefix("little-") + r7 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": map[string]interface{}{ + "name": "meh", + }, + }) + + tests := map[string]struct { + filter *resource.Resource + expected ResMap + }{ + "default namespace 1": { + filter: r2, + expected: resmaptest_test.NewRmBuilder(t, rf). + AddR(r1).AddR(r2).AddR(r7).ResMap(), + }, + "default namespace 2": { + filter: r1, + expected: resmaptest_test.NewRmBuilder(t, rf). + AddR(r1).AddR(r2).AddR(r7).ResMap(), + }, + "happy namespace no prefix": { + filter: r3, + expected: resmaptest_test.NewRmBuilder(t, rf). + AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(), + }, + "happy namespace with prefix": { + filter: r5, + expected: resmaptest_test.NewRmBuilder(t, rf). + AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(), + }, + "cluster level": { + filter: r7, + expected: resmaptest_test.NewRmBuilder(t, rf). + AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(), + }, + } + m := resmaptest_test.NewRmBuilder(t, rf). + AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap() + for name, test := range tests { + test := test + t.Run(name, func(t *testing.T) { + got, err1 := m.SubsetThatCouldBeReferencedByResource(test.filter) + if err1 != nil { + t.Fatalf("Expected error %v: ", err1) + } + err := test.expected.ErrorIfNotEqualLists(got) + if err != nil { + test.expected.Debug("expected") + got.Debug("actual") + t.Fatalf("Expected match") + } + }) + } +} + +func TestDeepCopy(t *testing.T) { + rm1 := resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + }).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + }, + }).ResMap() + + rm2 := rm1.DeepCopy() + + if &rm1 == &rm2 { + t.Fatal("DeepCopy returned a reference to itself instead of a copy") + } + err := rm1.ErrorIfNotEqualLists(rm1) + if err != nil { + t.Fatal(err) + } +} + +func TestErrorIfNotEqualSets(t *testing.T) { + r1 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + }) + r2 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + }, + }) + r3 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + "namespace": "system", + }, + }) + + m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap() + if err := m1.ErrorIfNotEqualSets(m1); err != nil { + t.Fatalf("object should equal itself %v", err) + } + + m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap() + if err := m1.ErrorIfNotEqualSets(m2); err == nil { + t.Fatalf("%v should not equal %v %v", m1, m2, err) + } + + m3 := resmaptest_test.NewRmBuilder(t, rf).AddR(r2).ResMap() + if err := m2.ErrorIfNotEqualSets(m3); err == nil { + t.Fatalf("%v should not equal %v %v", m2, m3, err) + } + + m3 = resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }}).ResMap() + if err := m2.ErrorIfNotEqualSets(m3); err != nil { + t.Fatalf("%v should equal %v %v", m2, m3, err) + } + + m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap() + if err := m1.ErrorIfNotEqualSets(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } + + m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap() + if err := m1.ErrorIfNotEqualSets(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } + + m4 = m1.ShallowCopy() + if err := m1.ErrorIfNotEqualSets(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } + m4 = m1.DeepCopy() + if err := m1.ErrorIfNotEqualSets(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } +} + +func TestErrorIfNotEqualLists(t *testing.T) { + r1 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + }) + r2 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + }, + }) + r3 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm2", + "namespace": "system", + }, + }) + + m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap() + if err := m1.ErrorIfNotEqualLists(m1); err != nil { + t.Fatalf("object should equal itself %v", err) + } + + m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap() + if err := m1.ErrorIfNotEqualLists(m2); err == nil { + t.Fatalf("%v should not equal %v %v", m1, m2, err) + } + + m3 := resmaptest_test.NewRmBuilder(t, rf).Add( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }}).ResMap() + if err := m2.ErrorIfNotEqualLists(m3); err != nil { + t.Fatalf("%v should equal %v %v", m2, m3, err) + } + + m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap() + if err := m1.ErrorIfNotEqualLists(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } + + m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap() + if err := m1.ErrorIfNotEqualLists(m4); err == nil { + t.Fatalf("expected inequality between %v and %v, %v", m1, m4, err) + } + + m4 = m1.ShallowCopy() + if err := m1.ErrorIfNotEqualLists(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } + m4 = m1.DeepCopy() + if err := m1.ErrorIfNotEqualLists(m4); err != nil { + t.Fatalf("expected equality between %v and %v, %v", m1, m4, err) + } +} + +func TestAppendAll(t *testing.T) { + r1 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "foo-deploy1", + }, + }) + input1 := rmF.FromResource(r1) + r2 := rf.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "StatefulSet", + "metadata": map[string]interface{}{ + "name": "bar-stateful", + }, + }) + input2 := rmF.FromResource(r2) + + expected := New() + if err := expected.Append(r1); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if err := expected.Append(r2); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if err := input1.AppendAll(input2); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if err := expected.ErrorIfNotEqualLists(input1); err != nil { + input1.Debug("1") + expected.Debug("ex") + t.Fatalf("%#v doesn't equal expected %#v", input1, expected) + } + if err := input1.AppendAll(nil); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if err := expected.ErrorIfNotEqualLists(input1); err != nil { + t.Fatalf("%#v doesn't equal expected %#v", input1, expected) + } +} + +func makeMap1() ResMap { + return rmF.FromResource(rf.FromMapAndOption( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cmap", + }, + "data": map[string]interface{}{ + "a": "x", + "b": "y", + }, + }, &types.GeneratorArgs{ + Behavior: "create", + })) +} + +func makeMap2(b types.GenerationBehavior) ResMap { + return rmF.FromResource(rf.FromMapAndOption( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cmap", + }, + "data": map[string]interface{}{ + "a": "u", + "b": "v", + "c": "w", + }, + }, &types.GeneratorArgs{ + Behavior: b.String(), + })) +} + +func TestAbsorbAll(t *testing.T) { + metadata := map[string]interface{}{ + "name": "cmap", + } + expected := rmF.FromResource(rf.FromMapAndOption( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "ConfigMap", + "metadata": metadata, + "data": map[string]interface{}{ + "a": "u", + "b": "v", + "c": "w", + }, + }, + &types.GeneratorArgs{ + Behavior: "create", + })) + w := makeMap1() + assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge))) + expected.RemoveBuildAnnotations() + w.RemoveBuildAnnotations() + assert.NoError(t, expected.ErrorIfNotEqualLists(w)) + w = makeMap1() + assert.NoError(t, w.AbsorbAll(nil)) + assert.NoError(t, w.ErrorIfNotEqualLists(makeMap1())) + + w = makeMap1() + w2 := makeMap2(types.BehaviorReplace) + assert.NoError(t, w.AbsorbAll(w2)) + w2.RemoveBuildAnnotations() + assert.NoError(t, w2.ErrorIfNotEqualLists(w)) + w = makeMap1() + w2 = makeMap2(types.BehaviorUnspecified) + err := w.AbsorbAll(w2) + assert.Error(t, err) + assert.True( + t, strings.Contains(err.Error(), "behavior must be merge or replace")) +} + +func TestToRNodeSlice(t *testing.T) { + input := `apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: namespace-reader +rules: +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - watch + - list +` + rm, err := rmF.NewResMapFromBytes([]byte(input)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + b := bytes.NewBufferString("") + for i, n := range rm.ToRNodeSlice() { + if i != 0 { + b.WriteString("---\n") + } + s, err := n.String() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + b.WriteString(s) + } + + if !reflect.DeepEqual(input, b.String()) { + t.Fatalf("actual doesn't match expected.\nActual:\n%s\n===\nExpected:\n%s\n", + b.String(), input) + } +} + +func TestDeAnchorSingleDoc(t *testing.T) { + input := `apiVersion: v1 +kind: ConfigMap +metadata: + name: wildcard +data: + color: &color-used blue + feeling: *color-used +` + rm, err := rmF.NewResMapFromBytes([]byte(input)) + assert.NoError(t, err) + assert.NoError(t, rm.DeAnchor()) + yaml, err := rm.AsYaml() + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(` +apiVersion: v1 +data: + color: blue + feeling: blue +kind: ConfigMap +metadata: + name: wildcard +`), strings.TrimSpace(string(yaml))) +} + +// Anchor references don't cross YAML document boundaries. +func TestDeAnchorMultiDoc(t *testing.T) { + input := `apiVersion: v1 +kind: ConfigMap +metadata: + name: betty +data: + color: &color-used blue + feeling: *color-used +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bob +data: + color: red + feeling: *color-used +` + _, err := rmF.NewResMapFromBytes([]byte(input)) + assert.Error(t, err) + assert.Contains(t, err.Error(), "unknown anchor 'color-used' referenced") +} + +// Anchor references cross list elements in a ResourceList. +func TestDeAnchorResourceList(t *testing.T) { + input := `apiVersion: config.kubernetes.io/v1 +kind: ResourceList +metadata: + name: aShortList +items: +- apiVersion: v1 + kind: ConfigMap + metadata: + name: betty + data: + color: &color-used blue + feeling: *color-used +- apiVersion: v1 + kind: ConfigMap + metadata: + name: bob + data: + color: red + feeling: *color-used +` + rm, err := rmF.NewResMapFromBytes([]byte(input)) + assert.NoError(t, err) + assert.NoError(t, rm.DeAnchor()) + yaml, err := rm.AsYaml() + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(` +apiVersion: v1 +data: + color: blue + feeling: blue +kind: ConfigMap +metadata: + name: betty +--- +apiVersion: v1 +data: + color: red + feeling: blue +kind: ConfigMap +metadata: + name: bob +`), strings.TrimSpace(string(yaml))) +} + +func TestApplySmPatch_General(t *testing.T) { + const ( + myDeployment = "Deployment" + myCRD = "myCRD" + expectedResultSMP = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + image: nginx + name: nginx +` + ) + tests := map[string]struct { + base []string + patches []string + expected []string + errorExpected bool + errorMsg string + }{ + "clown": { + base: []string{`apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`, + }, + patches: []string{`apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`, + }, + errorExpected: false, + expected: []string{ + `apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`, + }, + }, + "confusion": { + base: []string{`apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + A: X + B: Y +`, + }, + patches: []string{`apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + B: + C: Z +`, `apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +`, + }, + errorExpected: false, + expected: []string{ + `apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + A: X + C: Z + D: W + baz: + hello: world +`, + }, + }, + "withschema-ns1-ns2-one": { + base: []string{ + addNamespace("ns1", baseResource(myDeployment)), + addNamespace("ns2", baseResource(myDeployment)), + }, + patches: []string{ + addNamespace("ns1", addLabelAndEnvPatch(myDeployment)), + addNamespace("ns2", addLabelAndEnvPatch(myDeployment)), + }, + errorExpected: false, + expected: []string{ + addNamespace("ns1", expectedResultSMP), + addNamespace("ns2", expectedResultSMP), + }, + }, + "withschema-ns1-ns2-two": { + base: []string{ + addNamespace("ns1", baseResource(myDeployment)), + }, + patches: []string{ + addNamespace("ns2", changeImagePatch(myDeployment)), + }, + expected: []string{ + addNamespace("ns1", baseResource(myDeployment)), + }, + }, + "withschema-ns1-ns2-three": { + base: []string{ + addNamespace("ns1", baseResource(myDeployment)), + }, + patches: []string{ + addNamespace("ns1", changeImagePatch(myDeployment)), + }, + expected: []string{ + `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 + namespace: ns1 +spec: + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - image: nginx:1.7.9 + name: nginx +`, + }, + }, + "withschema-nil-ns2": { + base: []string{ + baseResource(myDeployment), + }, + patches: []string{ + addNamespace("ns2", changeImagePatch(myDeployment)), + }, + expected: []string{ + baseResource(myDeployment), + }, + }, + "withschema-ns1-nil": { + base: []string{ + addNamespace("ns1", baseResource(myDeployment)), + }, + patches: []string{ + changeImagePatch(myDeployment), + }, + expected: []string{ + addNamespace("ns1", baseResource(myDeployment)), + }, + }, + "noschema-ns1-ns2-one": { + base: []string{ + addNamespace("ns1", baseResource(myCRD)), + addNamespace("ns2", baseResource(myCRD)), + }, + patches: []string{ + addNamespace("ns1", addLabelAndEnvPatch(myCRD)), + addNamespace("ns2", addLabelAndEnvPatch(myCRD)), + }, + errorExpected: false, + expected: []string{ + addNamespace("ns1", expectedResultJMP("")), + addNamespace("ns2", expectedResultJMP("")), + }, + }, + "noschema-ns1-ns2-two": { + base: []string{addNamespace("ns1", baseResource(myCRD))}, + patches: []string{addNamespace("ns2", changeImagePatch(myCRD))}, + expected: []string{addNamespace("ns1", baseResource(myCRD))}, + }, + "noschema-nil-ns2": { + base: []string{baseResource(myCRD)}, + patches: []string{addNamespace("ns2", changeImagePatch(myCRD))}, + expected: []string{baseResource(myCRD)}, + }, + "noschema-ns1-nil": { + base: []string{addNamespace("ns1", baseResource(myCRD))}, + patches: []string{changeImagePatch(myCRD)}, + expected: []string{addNamespace("ns1", baseResource(myCRD))}, + }, + } + for n := range tests { + tc := tests[n] + t.Run(n, func(t *testing.T) { + m, err := rmF.NewResMapFromBytes([]byte(strings.Join(tc.base, "\n---\n"))) + assert.NoError(t, err) + foundError := false + for _, patch := range tc.patches { + rp, err := rf.FromBytes([]byte(patch)) + assert.NoError(t, err) + idSet := resource.MakeIdSet([]*resource.Resource{rp}) + if err = m.ApplySmPatch(idSet, rp); err != nil { + foundError = true + break + } + } + if foundError { + assert.True(t, tc.errorExpected) + // compare error message? + return + } + assert.False(t, tc.errorExpected) + m.RemoveBuildAnnotations() + yml, err := m.AsYaml() + assert.NoError(t, err) + assert.Equal(t, strings.Join(tc.expected, "---\n"), string(yml)) + }) + } +} + +// simple utility function to add an namespace in a resource +// used as base, patch or expected result. Simply looks +// for specs: in order to add namespace: xxxx before this line +func addNamespace(namespace string, base string) string { + res := strings.Replace(base, + "\nspec:\n", + "\n namespace: "+namespace+"\nspec:\n", + 1) + return res +} + +// DeleteOddsFilter deletes the odd entries, removing nodes. +// This is a ridiculous filter for testing. +type DeleteOddsFilter struct{} + +func (f DeleteOddsFilter) Filter( + nodes []*yaml.RNode) (result []*yaml.RNode, err error) { + for i := range nodes { + if i%2 == 0 { + // Keep the even entries, drop the odd entries. + result = append(result, nodes[i]) + } + } + return +} + +// CloneOddsFilter deletes even entries and clones odd entries, +// making new nodes. +// This is a ridiculous filter for testing. +type CloneOddsFilter struct{} + +func (f CloneOddsFilter) Filter( + nodes []*yaml.RNode) (result []*yaml.RNode, err error) { + for i := range nodes { + if i%2 != 0 { + newNode := nodes[i].Copy() + // Add suffix to the name, so that it's unique (w/r to this test). + newNode.SetName(newNode.GetName() + "Clone") + // Return a ptr to the copy. + result = append(result, nodes[i], newNode) + } + } + return +} + +func TestApplyFilter(t *testing.T) { + tests := map[string]struct { + input string + f kio.Filter + expected string + }{ + "labels": { + input: ` +apiVersion: example.com/v1 +kind: Beans +metadata: + name: myBeans +--- +apiVersion: example.com/v1 +kind: Franks +metadata: + name: myFranks +`, + f: labels.Filter{ + Labels: map[string]string{ + "a": "foo", + "b": "bar", + }, + FsSlice: types.FsSlice{ + { + Gvk: resid.NewGvk("example.com", "v1", "Beans"), + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + expected: ` +apiVersion: example.com/v1 +kind: Beans +metadata: + labels: + a: foo + b: bar + name: myBeans +--- +apiVersion: example.com/v1 +kind: Franks +metadata: + name: myFranks +`, + }, + "deleteOddNodes": { + input: ` +apiVersion: example.com/v1 +kind: Zero +metadata: + name: r0 +--- +apiVersion: example.com/v1 +kind: One +metadata: + name: r1 +--- +apiVersion: example.com/v1 +kind: Two +metadata: + name: r2 +--- +apiVersion: example.com/v1 +kind: Three +metadata: + name: r3 +`, + f: DeleteOddsFilter{}, + expected: ` +apiVersion: example.com/v1 +kind: Zero +metadata: + name: r0 +--- +apiVersion: example.com/v1 +kind: Two +metadata: + name: r2 +`, + }, + "cloneOddNodes": { + // input list has five entries + input: ` +apiVersion: example.com/v1 +kind: Zero +metadata: + name: r0 +--- +apiVersion: example.com/v1 +kind: One +metadata: + name: r1 +--- +apiVersion: example.com/v1 +kind: Two +metadata: + name: r2 +--- +apiVersion: example.com/v1 +kind: Three +metadata: + name: r3 +--- +apiVersion: example.com/v1 +kind: Four +metadata: + name: r4 +`, + f: CloneOddsFilter{}, + // output has four, but half are newly created nodes. + expected: ` +apiVersion: example.com/v1 +kind: One +metadata: + name: r1 +--- +apiVersion: example.com/v1 +kind: One +metadata: + name: r1Clone +--- +apiVersion: example.com/v1 +kind: Three +metadata: + name: r3 +--- +apiVersion: example.com/v1 +kind: Three +metadata: + name: r3Clone +`, + }, + } + for name := range tests { + tc := tests[name] + t.Run(name, func(t *testing.T) { + m, err := rmF.NewResMapFromBytes([]byte(tc.input)) + assert.NoError(t, err) + assert.NoError(t, m.ApplyFilter(tc.f)) + kusttest_test.AssertActualEqualsExpectedWithTweak( + t, m, nil, tc.expected) + }) + } +} + +func TestApplySmPatch_Deletion(t *testing.T) { + target := ` +apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + replica: 2 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - name: nginx + image: nginx +` + tests := map[string]struct { + patch string + expected string + finalMapSize int + }{ + "delete1": { + patch: `apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + replica: 2 + template: + $patch: delete + metadata: + labels: + old-label: old-value + spec: + containers: + - name: nginx + image: nginx +`, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +spec: + replica: 2 +`, + finalMapSize: 1, + }, + "delete2": { + patch: `apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +spec: + $patch: delete + replica: 2 + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - name: nginx + image: nginx +`, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: myDeploy +`, + finalMapSize: 1, + }, + "delete3": { + patch: `apiVersion: apps/v1 +metadata: + name: myDeploy +kind: Deployment +$patch: delete +`, + expected: "", + finalMapSize: 0, + }, + } + for name := range tests { + tc := tests[name] + t.Run(name, func(t *testing.T) { + m, err := rmF.NewResMapFromBytes([]byte(target)) + assert.NoError(t, err, name) + idSet := resource.MakeIdSet(m.Resources()) + assert.Equal(t, 1, idSet.Size(), name) + p, err := rf.FromBytes([]byte(tc.patch)) + assert.NoError(t, err, name) + assert.NoError(t, m.ApplySmPatch(idSet, p), name) + assert.Equal(t, tc.finalMapSize, m.Size(), name) + m.RemoveBuildAnnotations() + yml, err := m.AsYaml() + assert.NoError(t, err, name) + assert.Equal(t, tc.expected, string(yml), name) + }) + } +} + +func TestOriginAnnotations(t *testing.T) { + w := New() + for i := 0; i < 3; i++ { + assert.NoError(t, w.Append(makeCm(i))) + } + // this should add an origin annotation to every resource + assert.NoError(t, w.AddOriginAnnotation(origin1)) + resources := w.Resources() + for _, res := range resources { + or, err := res.GetOrigin() + assert.NoError(t, err) + assert.Equal(t, origin1, or) + } + // this should not overwrite the existing origin annotations + assert.NoError(t, w.AddOriginAnnotation(origin2)) + for _, res := range resources { + or, err := res.GetOrigin() + assert.NoError(t, err) + assert.Equal(t, origin1, or) + } + // this should remove origin annotations from all resources + assert.NoError(t, w.RemoveOriginAnnotations()) + for _, res := range resources { + or, err := res.GetOrigin() + assert.NoError(t, err) + assert.Nil(t, or) + } +} + +func TestTransformerAnnotations(t *testing.T) { + w := New() + for i := 0; i < 3; i++ { + assert.NoError(t, w.Append(makeCm(i))) + } + // this should add an origin annotation to every resource + assert.NoError(t, w.AddTransformerAnnotation(origin1)) + resources := w.Resources() + for _, res := range resources { + or, err := res.GetOrigin() + assert.NoError(t, err) + assert.Equal(t, origin1, or) + } + // this should add a transformer annotation to every resource + assert.NoError(t, w.AddTransformerAnnotation(origin2)) + for _, res := range resources { + or, err := res.GetOrigin() + assert.NoError(t, err) + assert.Equal(t, origin1, or) + tr, err := res.GetTransformations() + assert.NoError(t, err) + assert.Equal(t, resource.Transformations{origin2}, tr) + } + // remove transformer annotations from all resources + assert.NoError(t, w.RemoveTransformerAnnotations()) + for _, res := range resources { + tr, err := res.GetTransformations() + assert.NoError(t, err) + assert.Nil(t, tr) + } +} + +// baseResource produces a base object which used to test +// patch transformation +// Also the structure is matching the Deployment syntax +// the kind can be replaced to allow testing using CRD +// without access to the schema +func baseResource(kind string) string { + return fmt.Sprintf(`apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - image: nginx + name: nginx +`, kind) +} + +// addContainerAndEnvPatch produces a patch object which adds +// an entry in the env slice of the first/nginx container +// as well as adding a label in the metadata +// Note that for SMP/WithSchema merge, the name:nginx entry +// is mandatory +func addLabelAndEnvPatch(kind string) string { + return fmt.Sprintf(`apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + some-label: some-value + spec: + containers: + - name: nginx + env: + - name: SOMEENV + value: SOMEVALUE`, kind) +} + +// changeImagePatch produces a patch object which replaces +// the value of the image field in the first/nginx container +// Note that for SMP/WithSchema merge, the name:nginx entry +// is mandatory +func changeImagePatch(kind string) string { + return fmt.Sprintf(`apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - name: nginx + image: "nginx:1.7.9"`, kind) +} + +// utility method building the expected output of a JMP. +// imagename parameter allows to build a result consistent +// with the JMP behavior which basically overrides the +// entire "containers" list. +func expectedResultJMP(imagename string) string { + if imagename == "" { + return `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + name: nginx +` + } + return fmt.Sprintf(`apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - image: %s + name: nginx +`, imagename) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/selector_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/selector_test.go new file mode 100644 index 000000000..922fb1c63 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resmap/selector_test.go @@ -0,0 +1,189 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2. + +package resmap_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func setupRMForPatchTargets(t *testing.T) ResMap { + result, err := rmF.NewResMapFromBytes([]byte(` +apiVersion: group1/v1 +kind: Kind1 +metadata: + name: name1 + namespace: ns1 + labels: + app: name1 + annotations: + foo: bar +--- +apiVersion: group1/v1 +kind: Kind1 +metadata: + name: name2 + namespace: default + labels: + app: name2 + annotations: + foo: bar +--- +apiVersion: group1/v1 +kind: Kind2 +metadata: + name: name3 + labels: + app: name3 + annotations: + bar: baz +--- +apiVersion: group1/v1 +kind: Kind2 +metadata: + name: x-name1 + namespace: x-default +`)) + assert.NoError(t, err) + return result +} + +func TestFindPatchTargets(t *testing.T) { + rm := setupRMForPatchTargets(t) + testcases := map[string]struct { + target types.Selector + count int + }{ + "select_01": { + target: types.Selector{ + ResId: resid.ResId{Name: "name.*"}, + }, + count: 3, + }, + "select_02": { + target: types.Selector{ + ResId: resid.ResId{Name: "name.*"}, + AnnotationSelector: "foo=bar", + }, + count: 2, + }, + "select_03": { + target: types.Selector{ + LabelSelector: "app=name1", + }, + count: 1, + }, + "select_04": { + target: types.Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Kind: "Kind1", + }, + Name: "name.*", + }, + }, + count: 2, + }, + "select_05": { + target: types.Selector{ + ResId: resid.ResId{Name: "NotMatched"}, + }, + count: 0, + }, + "select_06": { + target: types.Selector{ + ResId: resid.ResId{Name: ""}, + }, + count: 4, + }, + "select_07": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "default"}, + }, + count: 2, + }, + "select_08": { + target: types.Selector{ + ResId: resid.ResId{Namespace: ""}, + }, + count: 4, + }, + "select_09": { + target: types.Selector{ + ResId: resid.ResId{ + Namespace: "default", + Name: "name.*", + Gvk: resid.Gvk{ + Kind: "Kind1", + }, + }, + }, + count: 1, + }, + "select_10": { + target: types.Selector{ + ResId: resid.ResId{Name: "^name.*"}, + }, + count: 3, + }, + "select_11": { + target: types.Selector{ + ResId: resid.ResId{Name: "name.*$"}, + }, + count: 3, + }, + "select_12": { + target: types.Selector{ + ResId: resid.ResId{Name: "^name.*$"}, + }, + count: 3, + }, + "select_13": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "^def.*"}, + }, + count: 2, + }, + "select_14": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "def.*$"}, + }, + count: 2, + }, + "select_15": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "^def.*$"}, + }, + count: 2, + }, + "select_16": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "default"}, + }, + count: 2, + }, + "select_17": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "NotMatched"}, + }, + count: 0, + }, + "select_18": { + target: types.Selector{ + ResId: resid.ResId{Namespace: "ns1"}, + }, + count: 1, + }, + } + for n, testcase := range testcases { + actual, err := rm.Select(testcase.target) + assert.NoError(t, err) + assert.Equalf( + t, testcase.count, len(actual), "test=%s target=%v", n, testcase.target) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/doc.go new file mode 100644 index 000000000..32d34b162 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/doc.go @@ -0,0 +1,5 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package resource implements representations of k8s API resources. +package resource diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/factory.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/factory.go new file mode 100644 index 000000000..cbda87237 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/factory.go @@ -0,0 +1,293 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource + +import ( + "encoding/json" + "fmt" + "log" + "strings" + + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/generators" + "sigs.k8s.io/kustomize/api/internal/kusterr" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/resid" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Factory makes instances of Resource. +type Factory struct { + hasher ifc.KustHasher + + // When set to true, IncludeLocalConfigs indicates + // that Factory should include resources with the + // annotation 'config.kubernetes.io/local-config'. + // By default these resources are ignored. + IncludeLocalConfigs bool +} + +// NewFactory makes an instance of Factory. +func NewFactory(h ifc.KustHasher) *Factory { + return &Factory{hasher: h} +} + +// Hasher returns an ifc.KustHasher +func (rf *Factory) Hasher() ifc.KustHasher { + return rf.hasher +} + +// FromMap returns a new instance of Resource. +func (rf *Factory) FromMap(m map[string]interface{}) *Resource { + return rf.FromMapAndOption(m, nil) +} + +// FromMapWithName returns a new instance with the given "original" name. +func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource { + return rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m) +} + +// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace. +func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource { + r := rf.FromMapAndOption(m, nil) + return r.setPreviousId(ns, n, r.GetKind()) +} + +// FromMapAndOption returns a new instance of Resource with given options. +func (rf *Factory) FromMapAndOption( + m map[string]interface{}, args *types.GeneratorArgs) *Resource { + n, err := yaml.FromMap(m) + if err != nil { + // TODO: return err instead of log. + log.Fatal(err) + } + return rf.makeOne(n, args) +} + +// makeOne returns a new instance of Resource. +func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GeneratorArgs) *Resource { + if rn == nil { + log.Fatal("RNode must not be null") + } + resource := &Resource{RNode: *rn} + if o != nil { + if o.Options == nil || !o.Options.DisableNameSuffixHash { + resource.EnableHashSuffix() + } + resource.SetBehavior(types.NewGenerationBehavior(o.Behavior)) + } + + return resource +} + +// SliceFromPatches returns a slice of resources given a patch path +// slice from a kustomization file. +func (rf *Factory) SliceFromPatches( + ldr ifc.Loader, paths []types.PatchStrategicMerge) ([]*Resource, error) { + var result []*Resource + for _, path := range paths { + content, err := ldr.Load(string(path)) + if err != nil { + return nil, err + } + res, err := rf.SliceFromBytes(content) + if err != nil { + return nil, kusterr.Handler(err, string(path)) + } + result = append(result, res...) + } + return result, nil +} + +// FromBytes unmarshalls bytes into one Resource. +func (rf *Factory) FromBytes(in []byte) (*Resource, error) { + result, err := rf.SliceFromBytes(in) + if err != nil { + return nil, err + } + if len(result) != 1 { + return nil, fmt.Errorf( + "expected 1 resource, found %d in %v", len(result), in) + } + return result[0], nil +} + +// SliceFromBytes unmarshals bytes into a Resource slice. +func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) { + nodes, err := rf.RNodesFromBytes(in) + if err != nil { + return nil, err + } + return rf.resourcesFromRNodes(nodes), nil +} + +// DropLocalNodes removes the local nodes by default. Local nodes are detected via the annotation `config.kubernetes.io/local-config: "true"` +func (rf *Factory) DropLocalNodes(nodes []*yaml.RNode) ([]*Resource, error) { + var result []*yaml.RNode + for _, node := range nodes { + if node.IsNilOrEmpty() { + continue + } + md, err := node.GetValidatedMetadata() + if err != nil { + return nil, err + } + + if rf.IncludeLocalConfigs { + result = append(result, node) + continue + } + localConfig, exist := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation] + if !exist || localConfig == "false" { + result = append(result, node) + } + } + return rf.resourcesFromRNodes(result), nil +} + +// ResourcesFromRNodes converts RNodes to Resources. +func (rf *Factory) ResourcesFromRNodes( + nodes []*yaml.RNode) (result []*Resource, err error) { + return rf.DropLocalNodes(nodes) +} + +// resourcesFromRNode assumes all nodes are good. +func (rf *Factory) resourcesFromRNodes( + nodes []*yaml.RNode) (result []*Resource) { + for _, n := range nodes { + result = append(result, rf.makeOne(n, nil)) + } + return +} + +func (rf *Factory) RNodesFromBytes(b []byte) ([]*yaml.RNode, error) { + nodes, err := kio.FromBytes(b) + if err != nil { + return nil, err + } + nodes, err = rf.dropBadNodes(nodes) + if err != nil { + return nil, err + } + return rf.inlineAnyEmbeddedLists(nodes) +} + +// inlineAnyEmbeddedLists scans the RNode slice for nodes named FooList. +// Such nodes are expected to be lists of resources, each of type Foo. +// These lists are replaced in the result by their inlined resources. +func (rf *Factory) inlineAnyEmbeddedLists( + nodes []*yaml.RNode) (result []*yaml.RNode, err error) { + var n0 *yaml.RNode + for len(nodes) > 0 { + n0, nodes = nodes[0], nodes[1:] + kind := n0.GetKind() + if !strings.HasSuffix(kind, "List") { + result = append(result, n0) + continue + } + // Convert a FooList into a slice of Foo. + var m map[string]interface{} + m, err = n0.Map() + if err != nil { + return nil, fmt.Errorf("trouble expanding list of %s; %w", kind, err) + } + items, ok := m["items"] + if !ok { + // treat as an empty list + continue + } + slice, ok := items.([]interface{}) + if !ok { + if items == nil { + // an empty list + continue + } + return nil, fmt.Errorf( + "expected array in %s/items, but found %T", kind, items) + } + innerNodes, err := rf.convertObjectSliceToNodeSlice(slice) + if err != nil { + return nil, err + } + nodes = append(nodes, innerNodes...) + } + return result, nil +} + +// convertObjectSlice converts a list of objects to a list of RNode. +func (rf *Factory) convertObjectSliceToNodeSlice( + objects []interface{}) (result []*yaml.RNode, err error) { + var bytes []byte + var nodes []*yaml.RNode + for _, obj := range objects { + bytes, err = json.Marshal(obj) + if err != nil { + return + } + nodes, err = kio.FromBytes(bytes) + if err != nil { + return + } + nodes, err = rf.dropBadNodes(nodes) + if err != nil { + return + } + result = append(result, nodes...) + } + return +} + +// dropBadNodes may drop some nodes from its input argument. +func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + var result []*yaml.RNode + for _, n := range nodes { + if n.IsNilOrEmpty() { + continue + } + if _, err := n.GetValidatedMetadata(); err != nil { + return nil, err + } + if foundNil, path := n.HasNilEntryInList(); foundNil { + return nil, fmt.Errorf("empty item at %v in object %v", path, n) + } + result = append(result, n) + } + return result, nil +} + +// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original +// name. +func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) { + result, err := rf.SliceFromBytes(in) + if err != nil { + return nil, err + } + if len(names) != len(result) { + return nil, fmt.Errorf("number of names doesn't match number of resources") + } + for i, res := range result { + res.setPreviousId(resid.DefaultNamespace, names[i], res.GetKind()) + } + return result, nil +} + +// MakeConfigMap makes an instance of Resource for ConfigMap +func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) { + rn, err := generators.MakeConfigMap(kvLdr, args) + if err != nil { + return nil, err + } + return rf.makeOne(rn, &args.GeneratorArgs), nil +} + +// MakeSecret makes an instance of Resource for Secret +func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) { + rn, err := generators.MakeSecret(kvLdr, args) + if err != nil { + return nil, err + } + return rf.makeOne(rn, &args.GeneratorArgs), nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/factory_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/factory_test.go new file mode 100644 index 000000000..ed186a8b2 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/factory_test.go @@ -0,0 +1,739 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/loader" + . "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +func TestRNodesFromBytes(t *testing.T) { + type testCase struct { + input string + expected []string + } + testCases := map[string]testCase{ + "empty1": { + input: "", + expected: []string{}, + }, + "empty2": { + input: ` +--- +--- +`, + expected: []string{}, + }, + "deployment1": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pooh +--- +`, + expected: []string{ + `apiVersion: apps/v1 +kind: Deployment +metadata: + name: pooh +`, + }, + }, + "deployment2": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: baseprefix-mungebot +spec: + replicas: 1 + selector: + matchLabels: + foo: bar + template: + metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + spec: + containers: + - env: + - name: foo + value: bar + image: nginx + name: nginx + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: baseprefix-mungebot-service +spec: + ports: + - port: 7002 + selector: + app: mungebot + foo: bar +`, + expected: []string{ + `apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: baseprefix-mungebot +spec: + replicas: 1 + selector: + matchLabels: + foo: bar + template: + metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + spec: + containers: + - env: + - name: foo + value: bar + image: nginx + name: nginx + ports: + - containerPort: 80 +`, + `apiVersion: v1 +kind: Service +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: baseprefix-mungebot-service +spec: + ports: + - port: 7002 + selector: + app: mungebot + foo: bar +`, + }, + }, + } + + for name := range testCases { + tc := testCases[name] + t.Run(name, func(t *testing.T) { + result, err := factory.RNodesFromBytes([]byte(tc.input)) + if err != nil { + t.Fatalf("%v: fails with err: %v", name, err) + } + if len(result) != len(tc.expected) { + for i := range result { + str, err := result[i].String() + if err != nil { + t.Fatalf("%v: result to YAML fails with err: %v", name, err) + } + t.Logf("--- %d:\n%s", i, str) + } + t.Fatalf( + "%v: actual len %d != expected len %d", + name, len(result), len(tc.expected)) + } + for i := range tc.expected { + str, err := result[i].String() + if err != nil { + t.Fatalf("%v: result to YAML fails with err: %v", name, err) + } + if str != tc.expected[i] { + t.Fatalf( + "%v: string mismatch in item %d\n"+ + "actual:\n-----\n%s\n-----\n"+ + "expected:\n-----\n%s\n-----\n", + name, i, str, tc.expected[i]) + } + } + }) + } +} + +func TestSliceFromPatches(t *testing.T) { + patchGood1 := types.PatchStrategicMerge("patch1.yaml") + patch1 := ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pooh +` + patchGood2 := types.PatchStrategicMerge("patch2.yaml") + patch2 := ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie + namespace: hundred-acre-wood +--- +# some comment +--- +--- +` + patchBad := types.PatchStrategicMerge("patch3.yaml") + patch3 := ` +WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOT: woot +` + patchList := types.PatchStrategicMerge("patch4.yaml") + patch4 := ` +apiVersion: v1 +kind: List +items: +- apiVersion: apps/v1 + kind: Deployment + metadata: + name: pooh +- apiVersion: v1 + kind: ConfigMap + metadata: + name: winnie + namespace: hundred-acre-wood +` + patchList2 := types.PatchStrategicMerge("patch5.yaml") + patch5 := ` +apiVersion: v1 +kind: DeploymentList +items: +- apiVersion: apps/v1 + kind: Deployment + metadata: + name: deployment-a + spec: &hostAliases + template: + spec: + hostAliases: + - hostnames: + - a.example.com + ip: 8.8.8.8 +- apiVersion: apps/v1 + kind: Deployment + metadata: + name: deployment-b + spec: + <<: *hostAliases +` + patchList3 := types.PatchStrategicMerge("patch6.yaml") + patch6 := ` +apiVersion: v1 +kind: List +items: +` + patchList4 := types.PatchStrategicMerge("patch7.yaml") + patch7 := ` +apiVersion: v1 +kind: List +` + testDeploymentSpec := map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "hostAliases": []interface{}{ + map[string]interface{}{ + "hostnames": []interface{}{ + "a.example.com", + }, + "ip": "8.8.8.8", + }, + }, + }, + }, + } + testDeploymentA := factory.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deployment-a", + }, + "spec": testDeploymentSpec, + }) + testDeploymentB := factory.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deployment-b", + }, + "spec": testDeploymentSpec, + }) + + fSys := filesys.MakeFsInMemory() + fSys.WriteFile(string(patchGood1), []byte(patch1)) + fSys.WriteFile(string(patchGood2), []byte(patch2)) + fSys.WriteFile(string(patchBad), []byte(patch3)) + fSys.WriteFile(string(patchList), []byte(patch4)) + fSys.WriteFile(string(patchList2), []byte(patch5)) + fSys.WriteFile(string(patchList3), []byte(patch6)) + fSys.WriteFile(string(patchList4), []byte(patch7)) + + ldr, err := loader.NewLoader( + loader.RestrictionRootOnly, filesys.Separator, fSys) + if err != nil { + t.Fatal(err) + } + + tests := map[string]struct { + input []types.PatchStrategicMerge + expectedOut []*Resource + expectedErr bool + }{ + "happy": { + input: []types.PatchStrategicMerge{patchGood1, patchGood2}, + expectedOut: []*Resource{testDeployment, testConfigMap}, + expectedErr: false, + }, + "badFileName": { + input: []types.PatchStrategicMerge{patchGood1, "doesNotExist"}, + expectedOut: []*Resource{}, + expectedErr: true, + }, + "badData": { + input: []types.PatchStrategicMerge{patchGood1, patchBad}, + expectedOut: []*Resource{}, + expectedErr: true, + }, + "listOfPatches": { + input: []types.PatchStrategicMerge{patchList}, + expectedOut: []*Resource{testDeployment, testConfigMap}, + expectedErr: false, + }, + "listWithAnchorReference": { + input: []types.PatchStrategicMerge{patchList2}, + expectedOut: []*Resource{testDeploymentA, testDeploymentB}, + // The error using kyaml is: + // json: unsupported type: map[interface {}]interface {} + // maybe arising from too many conversions between + // yaml, json, Resource, RNode, etc. + // These conversions go away after closing #3506 + // TODO(#3271) This shouldn't have an error, but does when kyaml is used. + expectedErr: true, + }, + "listWithNoEntries": { + input: []types.PatchStrategicMerge{patchList3}, + expectedOut: []*Resource{}, + expectedErr: false, + }, + "listWithNoItems": { + input: []types.PatchStrategicMerge{patchList4}, + expectedOut: []*Resource{}, + expectedErr: false, + }, + } + for n, test := range tests { + t.Run(n, func(t *testing.T) { + rs, err := factory.SliceFromPatches(ldr, test.input) + if err != nil { + assert.True(t, test.expectedErr, + fmt.Sprintf("in test %s, got unexpected error: %v", n, err)) + return + } + assert.False(t, test.expectedErr, "expected no error in "+n) + assert.Equal(t, len(test.expectedOut), len(rs)) + for i := range rs { + expYaml, err := test.expectedOut[i].AsYAML() + assert.NoError(t, err) + actYaml, err := rs[i].AsYAML() + assert.NoError(t, err) + assert.Equal(t, expYaml, actYaml) + } + }) + } +} + +func TestHash(t *testing.T) { + input := ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + one: "" +binaryData: + two: "" +` + expect := "698h7c7t9m" + k, err := factory.SliceFromBytes([]byte(input)) + if err != nil { + t.Fatal(err) + } + result, err := k[0].Hash(factory.Hasher()) + if err != nil { + t.Fatal(err) + } + if result != expect { + t.Fatalf("expect %s but got %s", expect, result) + } +} + +func TestMoreRNodesFromBytes(t *testing.T) { + type expected struct { + out []string + isErr bool + } + testCases := map[string]struct { + input []byte + exp expected + }{ + "garbage": { + input: []byte("garbageIn: garbageOut"), + exp: expected{ + isErr: true, + }, + }, + "noBytes": { + input: []byte{}, + exp: expected{}, + }, + "goodJson": { + input: []byte(` +{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}} +`), + exp: expected{ + out: []string{ + `{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`, + }, + }, + }, + "goodYaml1": { + input: []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`), + exp: expected{ + out: []string{` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`}, + }, + }, + "goodYaml2": { + input: []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`), + exp: expected{ + out: []string{` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`}, + }, + }, + "garbageInOneOfTwoObjects": { + input: []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +--- +WOOOOOOOOOOOOOOOOOOOOOOOOT: woot +`), + exp: expected{ + isErr: true, + }, + }, + "emptyObjects": { + input: []byte(` +--- +#a comment + +--- + +`), + exp: expected{ + out: []string{}, + }, + }, + "Missing .metadata.name in object": { + input: []byte(` +apiVersion: v1 +kind: Namespace +metadata: + annotations: + foo: bar +`), + exp: expected{ + isErr: true, + }, + }, + "nil value in list": { + input: []byte(` +apiVersion: builtin +kind: ConfigMapGenerator +metadata: + name: kube100-site + labels: + app: web +testList: +- testA +- +`), + exp: expected{ + isErr: true, + }, + }, + "List": { + input: []byte(` +apiVersion: v1 +kind: List +items: +- apiVersion: v1 + kind: ConfigMap + metadata: + name: winnie +- apiVersion: v1 + kind: ConfigMap + metadata: + name: winnie +`), + exp: expected{ + out: []string{` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`, + }, + }, + }, + "ConfigMapList": { + input: []byte(` +apiVersion: v1 +kind: ConfigMapList +items: +- apiVersion: v1 + kind: ConfigMap + metadata: + name: winnie +- apiVersion: v1 + kind: ConfigMap + metadata: + name: winnie +`), + exp: expected{ + out: []string{ + `{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`, + `{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`, + }, + }, + }, + "listWithAnchors": { + input: []byte(` +apiVersion: v1 +kind: DeploymentList +items: +- apiVersion: apps/v1 + kind: Deployment + metadata: + name: deployment-a + spec: &foo + template: + spec: + hostAliases: + - hostnames: + - a.example.com + ip: 8.8.8.8 +- apiVersion: apps/v1 + kind: Deployment + metadata: + name: deployment-b + spec: + *foo +`), + exp: expected{ + out: []string{ + `{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "deployment-a"}, ` + + `"spec": {"template": {"spec": {"hostAliases": [{"hostnames": ["a.example.com"], "ip": "8.8.8.8"}]}}}}`, + `{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "deployment-b"}, ` + + `"spec": {"template": {"spec": {"hostAliases": [{"hostnames": ["a.example.com"], "ip": "8.8.8.8"}]}}}}`}, + }, + }, + "simpleAnchor": { + input: []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: wildcard +data: + color: &color-used blue + feeling: *color-used +`), + exp: expected{ + out: []string{` +apiVersion: v1 +kind: ConfigMap +metadata: + name: wildcard +data: + color: blue + feeling: blue +`}, + }, + }, + } + for n := range testCases { + tc := testCases[n] + t.Run(n, func(t *testing.T) { + rs, err := factory.RNodesFromBytes(tc.input) + if err != nil { + assert.True(t, tc.exp.isErr) + return + } + assert.False(t, tc.exp.isErr) + assert.Equal(t, len(tc.exp.out), len(rs)) + for i := range rs { + actual, err := rs[i].String() + assert.NoError(t, err) + assert.Equal( + t, strings.TrimSpace(tc.exp.out[i]), strings.TrimSpace(actual)) + } + }) + } +} + +func TestDropLocalNodes(t *testing.T) { + testCases := map[string]struct { + input []byte + expected []byte + }{ + "localConfigUnset": { + input: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`), + expected: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`), + }, + "localConfigSet": { + input: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie-skip + annotations: + # this annotation causes the Resource to be ignored by kustomize + config.kubernetes.io/local-config: "" +`), + expected: nil, + }, + "localConfigSetToTrue": { + input: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie-skip + annotations: + config.kubernetes.io/local-config: "true" + `), + expected: nil, + }, + "localConfigSetToFalse": { + input: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie + annotations: + config.kubernetes.io/local-config: "false" +`), + expected: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + annotations: + config.kubernetes.io/local-config: "false" + name: winnie +`), + }, + "localConfigMultiInput": { + input: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie-skip + annotations: + config.kubernetes.io/local-config: "true" +`), + expected: []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: winnie +`), + }, + } + for n := range testCases { + tc := testCases[n] + t.Run(n, func(t *testing.T) { + nin, _ := kio.FromBytes(tc.input) + res, err := factory.DropLocalNodes(nin) + assert.NoError(t, err) + if tc.expected == nil { + assert.Equal(t, 0, len(res)) + } else { + actual, _ := res[0].AsYAML() + assert.Equal(t, tc.expected, actual) + } + }) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/idset.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/idset.go new file mode 100644 index 000000000..5d6bd63ed --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/idset.go @@ -0,0 +1,30 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource + +import "sigs.k8s.io/kustomize/kyaml/resid" + +type IdSet struct { + ids map[resid.ResId]bool +} + +func MakeIdSet(slice []*Resource) *IdSet { + set := make(map[resid.ResId]bool) + for _, r := range slice { + id := r.CurId() + if _, ok := set[id]; !ok { + set[id] = true + } + } + return &IdSet{ids: set} +} + +func (s IdSet) Contains(id resid.ResId) bool { + _, ok := s.ids[id] + return ok +} + +func (s IdSet) Size() int { + return len(s.ids) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/idset_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/idset_test.go new file mode 100644 index 000000000..79cef5a0a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/idset_test.go @@ -0,0 +1,32 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/resource" +) + +func TestIdSet_Empty(t *testing.T) { + s := MakeIdSet([]*Resource{}) + assert.Equal(t, 0, s.Size()) + assert.False(t, s.Contains(testDeployment.CurId())) + assert.False(t, s.Contains(testConfigMap.CurId())) +} + +func TestIdSet_One(t *testing.T) { + s := MakeIdSet([]*Resource{testDeployment}) + assert.Equal(t, 1, s.Size()) + assert.True(t, s.Contains(testDeployment.CurId())) + assert.False(t, s.Contains(testConfigMap.CurId())) +} + +func TestIdSet_Two(t *testing.T) { + s := MakeIdSet([]*Resource{testDeployment, testConfigMap}) + assert.Equal(t, 2, s.Size()) + assert.True(t, s.Contains(testDeployment.CurId())) + assert.True(t, s.Contains(testConfigMap.CurId())) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/origin.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/origin.go new file mode 100644 index 000000000..d5ac3a1a4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/origin.go @@ -0,0 +1,106 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource + +import ( + "path/filepath" + "strings" + + "sigs.k8s.io/kustomize/api/internal/git" + kyaml "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Origin retains information about the origin of resources and transformer configs +// that contributed to the output of `kustomize build` +type Origin struct { + // Path is the path to the resource. If a local resource, this path is + // rooted from the directory upon which `kustomize build` was invoked. If a + // remote resource, this path is rooted from the root of the remote repo. + Path string `json:"path,omitempty" yaml:"path,omitempty"` + + // Repo is the remote repository that the resource or transformer originated from if it is + // not from a local file + Repo string `json:"repo,omitempty" yaml:"repo,omitempty"` + + // Ref is the ref of the remote repository that the resource or transformer originated from + // if it is not from a local file + Ref string `json:"ref,omitempty" yaml:"ref,omitempty"` + + // The following fields only apply to resources that have been + // generated by fields other than the `resources` field, or to transformer + // configs. + + // ConfiguredIn is the file path to the generator or transformer config that created the + // resource + ConfiguredIn string `json:"configuredIn,omitempty" yaml:"configuredIn,omitempty"` + + // ConfiguredBy is the ObjectReference of the generator or transformer config + ConfiguredBy kyaml.ResourceIdentifier `json:"configuredBy,omitempty" yaml:"configuredBy,omitempty"` +} + +// Copy returns a copy of origin +func (origin *Origin) Copy() Origin { + if origin == nil { + return Origin{} + } + return *origin +} + +// Append returns a copy of origin with a path appended to it +func (origin *Origin) Append(path string) *Origin { + originCopy := origin.Copy() + repoSpec, err := git.NewRepoSpecFromUrl(path) + if err == nil { + originCopy.Repo = repoSpec.Host + repoSpec.OrgRepo + absPath := repoSpec.AbsPath() + path = absPath[strings.Index(absPath[1:], "/")+1:][1:] + originCopy.Path = "" + originCopy.Ref = repoSpec.Ref + } + originCopy.Path = filepath.Join(originCopy.Path, path) + return &originCopy +} + +// String returns a string version of origin +func (origin *Origin) String() (string, error) { + anno, err := kyaml.Marshal(origin) + return string(anno), err +} + +// Transformations is a list of Origin +type Transformations []*Origin + +// String returns a string version of Transformations +func (transformations *Transformations) String() (string, error) { + anno, err := kyaml.Marshal(transformations) + return string(anno), err +} + +// OriginFromCustomPlugin takes a custom plugin defined as a resource +// and returns an origin object to describe it +func OriginFromCustomPlugin(res *Resource) (*Origin, error) { + origin, err := res.GetOrigin() + if err != nil { + return nil, err + } + var result *Origin + if origin != nil { + result = &Origin{ + Repo: origin.Repo, + Ref: origin.Ref, + ConfiguredIn: origin.Path, + ConfiguredBy: kyaml.ResourceIdentifier{ + TypeMeta: kyaml.TypeMeta{ + APIVersion: res.GetApiVersion(), + Kind: res.GetKind(), + }, + NameMeta: kyaml.NameMeta{ + Name: res.GetName(), + Namespace: res.GetNamespace(), + }, + }, + } + } + return result, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/origin_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/origin_test.go new file mode 100644 index 000000000..46ae2b8d0 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/origin_test.go @@ -0,0 +1,160 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + . "sigs.k8s.io/kustomize/api/resource" + kyaml "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestOriginAppend(t *testing.T) { + tests := []struct { + in *Origin + path string + expected string + }{ + { + in: &Origin{ + Path: "prod", + }, + path: "service.yaml", + expected: `path: prod/service.yaml +`, + }, + { + in: &Origin{ + Path: "overlay/prod", + }, + path: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/", + expected: `path: examples/multibases/dev +repo: https://github.com/kubernetes-sigs/kustomize +`, + }, + } + for _, test := range tests { + actual, err := test.in.Append(test.path).String() + assert.NoError(t, err) + assert.Equal(t, actual, test.expected) + } +} + +func TestOriginString(t *testing.T) { + tests := []struct { + in *Origin + expected string + }{ + { + in: &Origin{ + Path: "prod/service.yaml", + Repo: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/", + Ref: "v1.0.6", + }, + expected: `path: prod/service.yaml +repo: github.com/kubernetes-sigs/kustomize/examples/multibases/dev/ +ref: v1.0.6 +`, + }, + { + in: &Origin{ + Path: "prod/service.yaml", + Repo: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/", + }, + expected: `path: prod/service.yaml +repo: github.com/kubernetes-sigs/kustomize/examples/multibases/dev/ +`, + }, + { + in: &Origin{ + Path: "prod/service.yaml", + }, + expected: `path: prod/service.yaml +`, + }, + } + + for _, test := range tests { + actual, err := test.in.String() + assert.NoError(t, err) + assert.Equal(t, test.expected, actual) + } +} + +func TestTransformationsString(t *testing.T) { + origin1 := &Origin{ + Repo: "github.com/myrepo", + Ref: "master", + ConfiguredIn: "config.yaml", + ConfiguredBy: kyaml.ResourceIdentifier{ + TypeMeta: kyaml.TypeMeta{ + APIVersion: "builtin", + Kind: "Generator", + }, + NameMeta: kyaml.NameMeta{ + Name: "my-name", + Namespace: "my-namespace", + }, + }, + } + origin2 := &Origin{ + ConfiguredIn: "../base/config.yaml", + ConfiguredBy: kyaml.ResourceIdentifier{ + TypeMeta: kyaml.TypeMeta{ + APIVersion: "builtin", + Kind: "Generator", + }, + NameMeta: kyaml.NameMeta{ + Name: "my-name", + Namespace: "my-namespace", + }, + }, + } + tests := []struct { + in Transformations + expected string + }{ + { + in: Transformations{origin1}, + expected: `- repo: github.com/myrepo + ref: master + configuredIn: config.yaml + configuredBy: + apiVersion: builtin + kind: Generator + name: my-name + namespace: my-namespace +`, + }, + { + in: Transformations{origin1, origin2}, + expected: `- repo: github.com/myrepo + ref: master + configuredIn: config.yaml + configuredBy: + apiVersion: builtin + kind: Generator + name: my-name + namespace: my-namespace +- configuredIn: ../base/config.yaml + configuredBy: + apiVersion: builtin + kind: Generator + name: my-name + namespace: my-namespace +`, + }, + { + in: Transformations{}, + expected: `[] +`, + }, + } + for _, test := range tests { + actual, err := test.in.String() + assert.NoError(t, err) + assert.Equal(t, test.expected, actual) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/resource.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/resource.go new file mode 100644 index 000000000..faf882045 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/resource.go @@ -0,0 +1,532 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource + +import ( + "fmt" + "log" + "strings" + + "sigs.k8s.io/kustomize/api/filters/patchstrategicmerge" + "sigs.k8s.io/kustomize/api/ifc" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/kio/kioutil" + "sigs.k8s.io/kustomize/kyaml/resid" + kyaml "sigs.k8s.io/kustomize/kyaml/yaml" + "sigs.k8s.io/yaml" +) + +// Resource is an RNode, representing a Kubernetes Resource Model object, +// paired with metadata used by kustomize. +type Resource struct { + kyaml.RNode + refVarNames []string +} + +// nolint +var BuildAnnotations = []string{ + utils.BuildAnnotationPreviousKinds, + utils.BuildAnnotationPreviousNames, + utils.BuildAnnotationPrefixes, + utils.BuildAnnotationSuffixes, + utils.BuildAnnotationPreviousNamespaces, + utils.BuildAnnotationAllowNameChange, + utils.BuildAnnotationAllowKindChange, + utils.BuildAnnotationsRefBy, + utils.BuildAnnotationsGenBehavior, + utils.BuildAnnotationsGenAddHashSuffix, + + kioutil.PathAnnotation, + kioutil.IndexAnnotation, + kioutil.SeqIndentAnnotation, + kioutil.IdAnnotation, + kioutil.InternalAnnotationsMigrationResourceIDAnnotation, + + kioutil.LegacyPathAnnotation, + kioutil.LegacyIndexAnnotation, + kioutil.LegacyIdAnnotation, +} + +func (r *Resource) ResetRNode(incoming *Resource) { + r.RNode = *incoming.Copy() +} + +func (r *Resource) GetGvk() resid.Gvk { + return resid.GvkFromNode(&r.RNode) +} + +func (r *Resource) Hash(h ifc.KustHasher) (string, error) { + return h.Hash(&r.RNode) +} + +func (r *Resource) SetGvk(gvk resid.Gvk) { + r.SetKind(gvk.Kind) + r.SetApiVersion(gvk.ApiVersion()) +} + +func (r *Resource) GetOrigin() (*Origin, error) { + annotations := r.GetAnnotations() + originAnnotations, ok := annotations[utils.OriginAnnotationKey] + if !ok { + return nil, nil + } + var origin Origin + if err := yaml.Unmarshal([]byte(originAnnotations), &origin); err != nil { + return nil, err + } + return &origin, nil +} + +func (r *Resource) SetOrigin(origin *Origin) error { + annotations := r.GetAnnotations() + if origin == nil { + delete(annotations, utils.OriginAnnotationKey) + } else { + originStr, err := origin.String() + if err != nil { + return err + } + annotations[utils.OriginAnnotationKey] = originStr + } + return r.SetAnnotations(annotations) +} + +func (r *Resource) GetTransformations() (Transformations, error) { + annotations := r.GetAnnotations() + transformerAnnotations, ok := annotations[utils.TransformerAnnotationKey] + if !ok { + return nil, nil + } + var transformations Transformations + if err := yaml.Unmarshal([]byte(transformerAnnotations), &transformations); err != nil { + return nil, err + } + return transformations, nil +} + +func (r *Resource) AddTransformation(origin *Origin) error { + annotations := r.GetAnnotations() + transformations, err := r.GetTransformations() + if err != nil { + return err + } + if transformations == nil { + transformations = Transformations{} + } + transformations = append(transformations, origin) + transformationStr, err := transformations.String() + if err != nil { + return err + } + annotations[utils.TransformerAnnotationKey] = transformationStr + return r.SetAnnotations(annotations) +} + +func (r *Resource) ClearTransformations() error { + annotations := r.GetAnnotations() + delete(annotations, utils.TransformerAnnotationKey) + return r.SetAnnotations(annotations) +} + +// ResCtx is an interface describing the contextual added +// kept kustomize in the context of each Resource object. +// Currently mainly the name prefix and name suffix are added. +type ResCtx interface { + AddNamePrefix(p string) + AddNameSuffix(s string) + GetNamePrefixes() []string + GetNameSuffixes() []string +} + +// ResCtxMatcher returns true if two Resources are being +// modified in the same kustomize context. +type ResCtxMatcher func(ResCtx) bool + +// DeepCopy returns a new copy of resource +func (r *Resource) DeepCopy() *Resource { + rc := &Resource{ + RNode: *r.Copy(), + } + rc.copyKustomizeSpecificFields(r) + return rc +} + +// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in +// the resource. +// TODO: move to RNode, use GetMeta to improve performance. +// TODO: make a version of mergeStringMaps that is build-annotation aware +// to avoid repeatedly setting refby and genargs annotations +// Must remove the kustomize bit at the end. +func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error { + if err := r.SetLabels( + mergeStringMaps(other.GetLabels(), r.GetLabels())); err != nil { + return fmt.Errorf("copyMerge cannot set labels - %w", err) + } + if err := r.SetAnnotations( + mergeStringMapsWithBuildAnnotations(other.GetAnnotations(), r.GetAnnotations())); err != nil { + return fmt.Errorf("copyMerge cannot set annotations - %w", err) + } + if err := r.SetName(other.GetName()); err != nil { + return fmt.Errorf("copyMerge cannot set name - %w", err) + } + if err := r.SetNamespace(other.GetNamespace()); err != nil { + return fmt.Errorf("copyMerge cannot set namespace - %w", err) + } + r.copyKustomizeSpecificFields(other) + return nil +} + +func (r *Resource) copyKustomizeSpecificFields(other *Resource) { + r.refVarNames = copyStringSlice(other.refVarNames) +} + +func (r *Resource) MergeDataMapFrom(o *Resource) { + r.SetDataMap(mergeStringMaps(o.GetDataMap(), r.GetDataMap())) +} + +func (r *Resource) MergeBinaryDataMapFrom(o *Resource) { + r.SetBinaryDataMap(mergeStringMaps(o.GetBinaryDataMap(), r.GetBinaryDataMap())) +} + +func (r *Resource) ErrIfNotEquals(o *Resource) error { + meYaml, err := r.AsYAML() + if err != nil { + return err + } + otherYaml, err := o.AsYAML() + if err != nil { + return err + } + if !r.ReferencesEqual(o) { + return fmt.Errorf( + `unequal references - self: +%sreferenced by: %s +--- other: +%sreferenced by: %s +`, meYaml, r.GetRefBy(), otherYaml, o.GetRefBy()) + } + if string(meYaml) != string(otherYaml) { + return fmt.Errorf(`--- self: +%s +--- other: +%s +`, meYaml, otherYaml) + } + return nil +} + +func (r *Resource) ReferencesEqual(other *Resource) bool { + setSelf := make(map[resid.ResId]bool) + setOther := make(map[resid.ResId]bool) + for _, ref := range other.GetRefBy() { + setOther[ref] = true + } + for _, ref := range r.GetRefBy() { + if _, ok := setOther[ref]; !ok { + return false + } + setSelf[ref] = true + } + return len(setSelf) == len(setOther) +} + +func copyStringSlice(s []string) []string { + if s == nil { + return nil + } + c := make([]string, len(s)) + copy(c, s) + return c +} + +// Implements ResCtx AddNamePrefix +func (r *Resource) AddNamePrefix(p string) { + r.appendCsvAnnotation(utils.BuildAnnotationPrefixes, p) +} + +// Implements ResCtx AddNameSuffix +func (r *Resource) AddNameSuffix(s string) { + r.appendCsvAnnotation(utils.BuildAnnotationSuffixes, s) +} + +func (r *Resource) appendCsvAnnotation(name, value string) { + if value == "" { + return + } + annotations := r.GetAnnotations() + if existing, ok := annotations[name]; ok { + annotations[name] = existing + "," + value + } else { + annotations[name] = value + } + if err := r.SetAnnotations(annotations); err != nil { + panic(err) + } +} + +// Implements ResCtx GetNamePrefixes +func (r *Resource) GetNamePrefixes() []string { + return r.getCsvAnnotation(utils.BuildAnnotationPrefixes) +} + +// Implements ResCtx GetNameSuffixes +func (r *Resource) GetNameSuffixes() []string { + return r.getCsvAnnotation(utils.BuildAnnotationSuffixes) +} + +func (r *Resource) getCsvAnnotation(name string) []string { + annotations := r.GetAnnotations() + if _, ok := annotations[name]; !ok { + return nil + } + return strings.Split(annotations[name], ",") +} + +// PrefixesSuffixesEquals is conceptually doing the same task +// as OutermostPrefixSuffix but performs a deeper comparison +// of the suffix and prefix slices. +func (r *Resource) PrefixesSuffixesEquals(o ResCtx) bool { + return utils.SameEndingSubSlice(r.GetNamePrefixes(), o.GetNamePrefixes()) && + utils.SameEndingSubSlice(r.GetNameSuffixes(), o.GetNameSuffixes()) +} + +// RemoveBuildAnnotations removes annotations created by the build process. +// These are internal-only to kustomize, added to the data pipeline to +// track name changes so name references can be fixed. +func (r *Resource) RemoveBuildAnnotations() { + annotations := r.GetAnnotations() + if len(annotations) == 0 { + return + } + for _, a := range BuildAnnotations { + delete(annotations, a) + } + if err := r.SetAnnotations(annotations); err != nil { + panic(err) + } +} + +func (r *Resource) setPreviousId(ns string, n string, k string) *Resource { + r.appendCsvAnnotation(utils.BuildAnnotationPreviousNames, n) + r.appendCsvAnnotation(utils.BuildAnnotationPreviousNamespaces, ns) + r.appendCsvAnnotation(utils.BuildAnnotationPreviousKinds, k) + return r +} + +// AllowNameChange allows name changes to the resource. +func (r *Resource) AllowNameChange() { + r.enable(utils.BuildAnnotationAllowNameChange) +} + +// NameChangeAllowed checks if a patch resource is allowed to change another resource's name. +func (r *Resource) NameChangeAllowed() bool { + return r.isEnabled(utils.BuildAnnotationAllowNameChange) +} + +// AllowKindChange allows kind changes to the resource. +func (r *Resource) AllowKindChange() { + r.enable(utils.BuildAnnotationAllowKindChange) +} + +// KindChangeAllowed checks if a patch resource is allowed to change another resource's kind. +func (r *Resource) KindChangeAllowed() bool { + return r.isEnabled(utils.BuildAnnotationAllowKindChange) +} + +func (r *Resource) isEnabled(annoKey string) bool { + annotations := r.GetAnnotations() + v, ok := annotations[annoKey] + return ok && v == utils.Enabled +} + +func (r *Resource) enable(annoKey string) { + annotations := r.GetAnnotations() + annotations[annoKey] = utils.Enabled + if err := r.SetAnnotations(annotations); err != nil { + panic(err) + } +} + +// String returns resource as JSON. +func (r *Resource) String() string { + bs, err := r.MarshalJSON() + if err != nil { + return "<" + err.Error() + ">" + } + return strings.TrimSpace(string(bs)) +} + +// AsYAML returns the resource in Yaml form. +// Easier to read than JSON. +func (r *Resource) AsYAML() ([]byte, error) { + json, err := r.MarshalJSON() + if err != nil { + return nil, err + } + return yaml.JSONToYAML(json) +} + +// MustYaml returns YAML or panics. +func (r *Resource) MustYaml() string { + yml, err := r.AsYAML() + if err != nil { + log.Fatal(err) + } + return string(yml) +} + +// Behavior returns the behavior for the resource. +func (r *Resource) Behavior() types.GenerationBehavior { + annotations := r.GetAnnotations() + if v, ok := annotations[utils.BuildAnnotationsGenBehavior]; ok { + return types.NewGenerationBehavior(v) + } + return types.NewGenerationBehavior("") +} + +// SetBehavior sets the behavior for the resource. +func (r *Resource) SetBehavior(behavior types.GenerationBehavior) { + annotations := r.GetAnnotations() + annotations[utils.BuildAnnotationsGenBehavior] = behavior.String() + if err := r.SetAnnotations(annotations); err != nil { + panic(err) + } +} + +// NeedHashSuffix returns true if a resource content +// hash should be appended to the name of the resource. +func (r *Resource) NeedHashSuffix() bool { + return r.isEnabled(utils.BuildAnnotationsGenAddHashSuffix) +} + +// EnableHashSuffix marks the resource as needing a content +// hash to be appended to the name of the resource. +func (r *Resource) EnableHashSuffix() { + r.enable(utils.BuildAnnotationsGenAddHashSuffix) +} + +// OrgId returns the original, immutable ResId for the resource. +// This doesn't have to be unique in a ResMap. +func (r *Resource) OrgId() resid.ResId { + ids := r.PrevIds() + if len(ids) > 0 { + return ids[0] + } + return r.CurId() +} + +// PrevIds returns a list of ResIds that includes every +// previous ResId the resource has had through all of its +// GVKN transformations, in the order that it had that ID. +// I.e. the oldest ID is first. +// The returned array does not include the resource's current +// ID. If there are no previous IDs, this will return nil. +func (r *Resource) PrevIds() []resid.ResId { + prevIds, err := utils.PrevIds(&r.RNode) + if err != nil { + // this should never happen + panic(err) + } + return prevIds +} + +// StorePreviousId stores the resource's current ID via build annotations. +func (r *Resource) StorePreviousId() { + id := r.CurId() + r.setPreviousId(id.EffectiveNamespace(), id.Name, id.Kind) +} + +// CurId returns a ResId for the resource using the +// mutable parts of the resource. +// This should be unique in any ResMap. +func (r *Resource) CurId() resid.ResId { + return resid.NewResIdWithNamespace( + r.GetGvk(), r.GetName(), r.GetNamespace()) +} + +// GetRefBy returns the ResIds that referred to current resource +func (r *Resource) GetRefBy() []resid.ResId { + var resIds []resid.ResId + asStrings := r.getCsvAnnotation(utils.BuildAnnotationsRefBy) + for _, s := range asStrings { + resIds = append(resIds, resid.FromString(s)) + } + return resIds +} + +// AppendRefBy appends a ResId into the refBy list +// Using any type except fmt.Stringer here results in a compilation error +func (r *Resource) AppendRefBy(id fmt.Stringer) { + r.appendCsvAnnotation(utils.BuildAnnotationsRefBy, id.String()) +} + +// GetRefVarNames returns vars that refer to current resource +func (r *Resource) GetRefVarNames() []string { + return r.refVarNames +} + +// AppendRefVarName appends a name of a var into the refVar list +func (r *Resource) AppendRefVarName(variable types.Var) { + r.refVarNames = append(r.refVarNames, variable.Name) +} + +// ApplySmPatch applies the provided strategic merge patch. +func (r *Resource) ApplySmPatch(patch *Resource) error { + n, ns, k := r.GetName(), r.GetNamespace(), r.GetKind() + if patch.NameChangeAllowed() || patch.KindChangeAllowed() { + r.StorePreviousId() + } + if err := r.ApplyFilter(patchstrategicmerge.Filter{ + Patch: &patch.RNode, + }); err != nil { + return err + } + if r.IsNilOrEmpty() { + return nil + } + if !patch.KindChangeAllowed() { + r.SetKind(k) + } + if !patch.NameChangeAllowed() { + r.SetName(n) + } + r.SetNamespace(ns) + return nil +} + +func (r *Resource) ApplyFilter(f kio.Filter) error { + l, err := f.Filter([]*kyaml.RNode{&r.RNode}) + if len(l) == 0 { + // The node was deleted, which means the entire resource + // must be deleted. Signal that via the following: + r.SetYNode(nil) + } + return err +} + +func mergeStringMaps(maps ...map[string]string) map[string]string { + result := map[string]string{} + for _, m := range maps { + for key, value := range m { + result[key] = value + } + } + return result +} + +func mergeStringMapsWithBuildAnnotations(maps ...map[string]string) map[string]string { + result := mergeStringMaps(maps...) + for i := range BuildAnnotations { + if len(maps) > 0 { + if v, ok := maps[0][BuildAnnotations[i]]; ok { + result[BuildAnnotations[i]] = v + continue + } + } + delete(result, BuildAnnotations[i]) + } + return result +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/resource_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/resource_test.go new file mode 100644 index 000000000..106c827ae --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/resource/resource_test.go @@ -0,0 +1,1556 @@ +// Copyright 2020 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resource_test + +import ( + "fmt" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/api/internal/utils" + "sigs.k8s.io/kustomize/api/provider" + . "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" + kyaml "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var factory = provider.NewDefaultDepProvider().GetResourceFactory() + +var testConfigMap = factory.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "winnie", + "namespace": "hundred-acre-wood", + }, + }) + +//nolint:gosec +const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}` + +var testDeployment = factory.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "pooh", + }, + }) + +const deploymentAsString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}` + +func TestAsYAML(t *testing.T) { + expected := `apiVersion: apps/v1 +kind: Deployment +metadata: + name: pooh +` + yaml, err := testDeployment.AsYAML() + if err != nil { + t.Fatal(err) + } + if string(yaml) != expected { + t.Fatalf("--- expected\n%s\n--- got\n%s\n", expected, string(yaml)) + } +} + +func TestResourceString(t *testing.T) { + tests := []struct { + in *Resource + s string + }{ + { + in: testConfigMap, + s: configMapAsString, + }, + { + in: testDeployment, + s: deploymentAsString, + }, + } + for _, test := range tests { + assert.Equal(t, test.in.String(), test.s) + } +} + +func TestResourceId(t *testing.T) { + tests := []struct { + in *Resource + id resid.ResId + }{ + { + in: testConfigMap, + id: resid.NewResIdWithNamespace( + resid.NewGvk("", "v1", "ConfigMap"), + "winnie", "hundred-acre-wood"), + }, + { + in: testDeployment, + id: resid.NewResId( + resid.NewGvk("apps", "v1", "Deployment"), "pooh"), + }, + } + for _, test := range tests { + if test.in.OrgId() != test.id { + t.Fatalf("Expected %v, but got %v\n", test.id, test.in.OrgId()) + } + } +} + +func TestDeepCopy(t *testing.T) { + r := factory.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "pooh", + }, + }) + r.AppendRefBy(resid.NewResId(resid.Gvk{Group: "somegroup", Kind: "MyKind"}, "random")) + + var1 := types.Var{ + Name: "SERVICE_ONE", + ObjRef: types.Target{ + Gvk: resid.Gvk{Version: "v1", Kind: "Service"}, + Name: "backendOne"}, + } + r.AppendRefVarName(var1) + + cr := r.DeepCopy() + if !reflect.DeepEqual(r, cr) { + t.Errorf("expected %v\nbut got%v", r, cr) + } +} + +func TestApplySmPatch_1(t *testing.T) { + resource, err := factory.FromBytes([]byte(` +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: bingo +spec: + replicas: 1 + selector: + matchLabels: + foo: bar + template: + metadata: + labels: + app: mungebot + spec: + containers: + - env: + - name: foo + value: bar + image: nginx + name: nginx + ports: + - containerPort: 80 +`)) + assert.NoError(t, err) + patch, err := factory.FromBytes([]byte(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: baseprefix-mungebot +spec: + template: + spec: + containers: + - image: nginx + name: nginx + ports: + - containerPort: 777 +`)) + assert.NoError(t, err) + + assert.NoError(t, resource.ApplySmPatch(patch)) + bytes, err := resource.AsYAML() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + baseAnno: This is a base annotation + labels: + app: mungebot + foo: bar + name: bingo +spec: + replicas: 1 + selector: + matchLabels: + foo: bar + template: + metadata: + labels: + app: mungebot + spec: + containers: + - env: + - name: foo + value: bar + image: nginx + name: nginx + ports: + - containerPort: 777 + - containerPort: 80 +`, string(bytes)) +} + +func TestApplySmPatch_2(t *testing.T) { + resource, err := factory.FromBytes([]byte(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + A: X + B: Y +`)) + assert.NoError(t, err) + patch, err := factory.FromBytes([]byte(` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + B: + C: Z + D: W + baz: + hello: world +`)) + assert.NoError(t, err) + assert.NoError(t, resource.ApplySmPatch(patch)) + bytes, err := resource.AsYAML() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + A: X + C: Z + D: W + baz: + hello: world +`, string(bytes)) +} + +func TestApplySmPatch_3(t *testing.T) { + resource, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`)) + assert.NoError(t, err) + patch, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`)) + assert.NoError(t, err) + assert.NoError(t, resource.ApplySmPatch(patch)) + bytes, err := resource.AsYAML() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 999 +`, string(bytes)) +} + +func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) { + cases := []struct { + name string + skip bool + patch string + expectedOutput string + }{ + { + name: "Order should not change when patch has foo only", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: foo +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: foo + - name: bar +`, + }, + { + name: "Order changes when patch has bar only", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: bar +`, + // This test records current behavior, but this behavior might be undesirable. + // If so, feel free to change the test to pass with some improved algorithm. + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: bar + - name: foo +`, + }, + { + name: "Order should not change and should include a new item at the beginning when patch has a new list item", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: baz +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: baz + - name: foo + - name: bar +`, + }, + { + name: "Order should not change when patch has foo and bar in same order", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: foo + - name: bar +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: foo + - name: bar +`, + }, + { + name: "Order should change when patch has foo and bar in different order", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: bar + - name: foo +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: bar + - name: foo +`, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.skip { + t.Skip() + } + + resource, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Pod +metadata: + name: test +spec: + initContainers: + - name: foo + - name: bar +`)) + assert.NoError(t, err) + + patch, err := factory.FromBytes([]byte(tc.patch)) + assert.NoError(t, err) + assert.NoError(t, resource.ApplySmPatch(patch)) + bytes, err := resource.AsYAML() + assert.NoError(t, err) + assert.Equal(t, tc.expectedOutput, string(bytes)) + }) + } +} + +func TestApplySmPatchShouldOutputPrimitiveListItemsInCorrectOrder(t *testing.T) { + cases := []struct { + name string + skip bool + patch string + expectedOutput string + }{ + { + name: "Order should not change when patch has foo only", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test + finalizers: ["foo"] +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + finalizers: + - foo + - bar + name: test +`, + }, + { + name: "Order should not change when patch has bar only", + skip: true, // TODO: This test should pass but fails currently. Fix the problem and unskip this test + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test + finalizers: ["bar"] +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + finalizers: + - foo + - bar + name: test +`, + }, + { + name: "Order should not change and should include a new item at the beginning when patch has a new list item", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test + finalizers: ["baz"] +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + finalizers: + - baz + - foo + - bar + name: test +`, + }, + { + name: "Order should not change when patch has foo and bar in same order", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test + finalizers: ["foo", "bar"] +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + finalizers: + - foo + - bar + name: test +`, + }, + { + name: "Order should change when patch has foo and bar in different order", + patch: `apiVersion: v1 +kind: Pod +metadata: + name: test + finalizers: ["bar", "foo"] +`, + expectedOutput: `apiVersion: v1 +kind: Pod +metadata: + finalizers: + - bar + - foo + name: test +`, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.skip { + t.Skip() + } + + resource, err := factory.FromBytes([]byte(` +kind: Pod +metadata: + name: test + finalizers: ["foo", "bar"] +`)) + assert.NoError(t, err) + + patch, err := factory.FromBytes([]byte(tc.patch)) + assert.NoError(t, err) + assert.NoError(t, resource.ApplySmPatch(patch)) + bytes, err := resource.AsYAML() + assert.NoError(t, err) + assert.Equal(t, tc.expectedOutput, string(bytes)) + }) + } +} + +func TestMergeDataMapFrom(t *testing.T) { + resource, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: BlahBlah +metadata: + name: clown +data: + fruit: pear +`)) + if !assert.NoError(t, err) { + t.FailNow() + } + patch, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Whatever +metadata: + name: spaceship +data: + spaceship: enterprise +`)) + if !assert.NoError(t, err) { + t.FailNow() + } + resource.MergeDataMapFrom(patch) + bytes, err := resource.AsYAML() + assert.NoError(t, err) + assert.Equal(t, `apiVersion: v1 +data: + fruit: pear + spaceship: enterprise +kind: BlahBlah +metadata: + name: clown +`, string(bytes)) +} + +func TestApplySmPatch_SwapOrder(t *testing.T) { + s1 := ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + B: + C: Z +` + s2 := ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +` + expected := `apiVersion: example.com/v1 +kind: Foo +metadata: + name: my-foo +spec: + bar: + C: Z + D: W + baz: + hello: world +` + r1, err := factory.FromBytes([]byte(s1)) + assert.NoError(t, err) + r2, err := factory.FromBytes([]byte(s2)) + assert.NoError(t, err) + assert.NoError(t, r1.ApplySmPatch(r2)) + bytes, err := r1.AsYAML() + assert.NoError(t, err) + assert.Equal(t, expected, string(bytes)) + + r1, _ = factory.FromBytes([]byte(s1)) + r2, _ = factory.FromBytes([]byte(s2)) + assert.NoError(t, r2.ApplySmPatch(r1)) + bytes, err = r2.AsYAML() + assert.NoError(t, err) + assert.Equal(t, expected, string(bytes)) +} + +func TestApplySmPatch(t *testing.T) { + const ( + myDeployment = "Deployment" + myCRD = "myCRD" + ) + + tests := map[string]struct { + base string + patch []string + expected string + errorExpected bool + errorMsg string + }{ + "withschema-label-image-container": { + base: baseResource(myDeployment), + patch: []string{ + addLabelAndEnvPatch(myDeployment), + changeImagePatch(myDeployment, "nginx:latest"), + addContainerAndEnvPatch(myDeployment), + }, + errorExpected: false, + expected: expectedResultMultiPatch(myDeployment, false), + }, + "withschema-image-container-label": { + base: baseResource(myDeployment), + patch: []string{ + changeImagePatch(myDeployment, "nginx:latest"), + addContainerAndEnvPatch(myDeployment), + addLabelAndEnvPatch(myDeployment), + }, + errorExpected: false, + expected: expectedResultMultiPatch(myDeployment, true), + }, + "withschema-container-label-image": { + base: baseResource(myDeployment), + patch: []string{ + addContainerAndEnvPatch(myDeployment), + addLabelAndEnvPatch(myDeployment), + changeImagePatch(myDeployment, "nginx:latest"), + }, + errorExpected: false, + expected: expectedResultMultiPatch(myDeployment, true), + }, + "noschema-label-image-container": { + base: baseResource(myCRD), + patch: []string{ + addLabelAndEnvPatch(myCRD), + changeImagePatch(myCRD, "nginx:latest"), + addContainerAndEnvPatch(myCRD), + }, + // Might be better if this complained about patch conflict. + // See plugin/builtin/patchstrategicmergetransformer/psmt_test.go + expected: `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: ANOTHERENV + value: ANOTHERVALUE + name: nginx + - image: anotherimage + name: anothercontainer +`, + }, + "noschema-image-container-label": { + base: baseResource(myCRD), + patch: []string{ + changeImagePatch(myCRD, "nginx:latest"), + addContainerAndEnvPatch(myCRD), + addLabelAndEnvPatch(myCRD), + }, + // Might be better if this complained about patch conflict. + expected: `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + name: nginx +`, + }, + "noschema-container-label-image": { + base: baseResource(myCRD), + patch: []string{ + addContainerAndEnvPatch(myCRD), + addLabelAndEnvPatch(myCRD), + changeImagePatch(myCRD, "nginx:latest"), + }, + // Might be better if this complained about patch conflict. + expected: `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - image: nginx:latest + name: nginx +`, + }, + + "withschema-label-latest-someV-01": { + base: baseResource(myDeployment), + patch: []string{ + addLabelAndEnvPatch(myDeployment), + changeImagePatch(myDeployment, "nginx:latest"), + changeImagePatch(myDeployment, "nginx:1.7.9"), + }, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + image: nginx:1.7.9 + name: nginx +`, + }, + "withschema-latest-label-someV-02": { + base: baseResource(myDeployment), + patch: []string{ + changeImagePatch(myDeployment, "nginx:latest"), + addLabelAndEnvPatch(myDeployment), + changeImagePatch(myDeployment, "nginx:1.7.9"), + }, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + image: nginx:1.7.9 + name: nginx +`, + }, + "withschema-latest-label-someV-03": { + base: baseResource(myDeployment), + patch: []string{ + changeImagePatch(myDeployment, "nginx:1.7.9"), + addLabelAndEnvPatch(myDeployment), + changeImagePatch(myDeployment, "nginx:latest"), + }, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + image: nginx:latest + name: nginx +`, + }, + "withschema-latest-label-someV-04": { + base: baseResource(myDeployment), + patch: []string{ + changeImagePatch(myDeployment, "nginx:1.7.9"), + changeImagePatch(myDeployment, "nginx:latest"), + addLabelAndEnvPatch(myDeployment), + changeImagePatch(myDeployment, "nginx:nginx"), + }, + expected: `apiVersion: apps/v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + image: nginx:nginx + name: nginx +`, + }, + "noschema-latest-label-someV-01": { + base: baseResource(myCRD), + patch: []string{ + addLabelAndEnvPatch(myCRD), + changeImagePatch(myCRD, "nginx:latest"), + changeImagePatch(myCRD, "nginx:1.7.9"), + }, + expected: `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - image: nginx:1.7.9 + name: nginx +`, + }, + "noschema-latest-label-someV-02": { + base: baseResource(myCRD), + patch: []string{ + changeImagePatch(myCRD, "nginx:latest"), + addLabelAndEnvPatch(myCRD), + changeImagePatch(myCRD, "nginx:1.7.9"), + }, + expected: expectedResultJMP("nginx:1.7.9"), + }, + "noschema-latest-label-someV-03": { + base: baseResource(myCRD), + patch: []string{ + changeImagePatch(myCRD, "nginx:1.7.9"), + addLabelAndEnvPatch(myCRD), + changeImagePatch(myCRD, "nginx:latest"), + }, + expected: `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - image: nginx:latest + name: nginx +`, + }, + "noschema-latest-label-someV-04": { + base: baseResource(myCRD), + patch: []string{ + changeImagePatch(myCRD, "nginx:1.7.9"), + changeImagePatch(myCRD, "nginx:latest"), + addLabelAndEnvPatch(myCRD), + changeImagePatch(myCRD, "nginx:nginx"), + }, + expected: `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - image: nginx:nginx + name: nginx +`, + }, + } + + for name, test := range tests { + resource, err := factory.FromBytes([]byte(test.base)) + assert.NoError(t, err) + for _, p := range test.patch { + patch, err := factory.FromBytes([]byte(p)) + assert.NoError(t, err, name) + assert.NoError(t, resource.ApplySmPatch(patch), name) + } + bytes, err := resource.AsYAML() + if test.errorExpected { + assert.Error(t, err, name) + } else { + assert.NoError(t, err, name) + assert.Equal(t, test.expected, string(bytes), name) + } + } +} + +func TestResourceStorePreviousId(t *testing.T) { + tests := map[string]struct { + input string + newName string + newNs string + expected string + }{ + "default namespace, first previous name": { + input: `apiVersion: apps/v1 +kind: Secret +metadata: + name: oldName +`, + newName: "newName", + newNs: "", + expected: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret + internal.config.kubernetes.io/previousNames: oldName + internal.config.kubernetes.io/previousNamespaces: default + name: newName +`, + }, + + "default namespace, second previous name": { + input: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret + internal.config.kubernetes.io/previousNames: oldName + internal.config.kubernetes.io/previousNamespaces: default + name: oldName2 +`, + newName: "newName", + newNs: "", + expected: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret,Secret + internal.config.kubernetes.io/previousNames: oldName,oldName2 + internal.config.kubernetes.io/previousNamespaces: default,default + name: newName +`, + }, + + "non-default namespace": { + input: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret + internal.config.kubernetes.io/previousNames: oldName + internal.config.kubernetes.io/previousNamespaces: default + name: oldName2 + namespace: oldNamespace +`, + newName: "newName", + newNs: "newNamespace", + expected: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret,Secret + internal.config.kubernetes.io/previousNames: oldName,oldName2 + internal.config.kubernetes.io/previousNamespaces: default,oldNamespace + name: newName + namespace: newNamespace +`, + }, + } + factory := provider.NewDefaultDepProvider().GetResourceFactory() + for i := range tests { + test := tests[i] + t.Run(i, func(t *testing.T) { + resources, err := factory.SliceFromBytes([]byte(test.input)) + if !assert.NoError(t, err) || len(resources) == 0 { + t.FailNow() + } + r := resources[0] + r.StorePreviousId() + r.SetName(test.newName) + if test.newNs != "" { + r.SetNamespace(test.newNs) + } + bytes, err := r.AsYAML() + if !assert.NoError(t, err) { + t.FailNow() + } + assert.Equal(t, test.expected, string(bytes)) + }) + } +} + +func TestResource_PrevIds(t *testing.T) { + tests := map[string]struct { + input string + expected []resid.ResId + }{ + "no previous IDs": { + input: `apiVersion: apps/v1 +kind: Secret +metadata: + name: name +`, + expected: nil, + }, + + "one previous ID": { + input: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret + internal.config.kubernetes.io/previousNames: oldName + internal.config.kubernetes.io/previousNamespaces: default + name: newName +`, + expected: []resid.ResId{ + { + Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"}, + Name: "oldName", + Namespace: resid.DefaultNamespace, + }, + }, + }, + + "two ids": { + input: `apiVersion: apps/v1 +kind: Secret +metadata: + annotations: + internal.config.kubernetes.io/previousKinds: Secret,Secret + internal.config.kubernetes.io/previousNames: oldName,oldName2 + internal.config.kubernetes.io/previousNamespaces: default,oldNamespace + name: newName + namespace: newNamespace +`, + expected: []resid.ResId{ + { + Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"}, + Name: "oldName", + Namespace: resid.DefaultNamespace, + }, + { + Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"}, + Name: "oldName2", + Namespace: "oldNamespace", + }, + }, + }, + } + factory := provider.NewDefaultDepProvider().GetResourceFactory() + for i := range tests { + test := tests[i] + t.Run(i, func(t *testing.T) { + resources, err := factory.SliceFromBytes([]byte(test.input)) + if !assert.NoError(t, err) || len(resources) == 0 { + t.FailNow() + } + r := resources[0] + assert.Equal(t, test.expected, r.PrevIds()) + }) + } +} + +// baseResource produces a base object which used to test +// patch transformation +// Also the structure is matching the Deployment syntax +// the kind can be replaced to allow testing using CRD +// without access to the schema +func baseResource(kind string) string { + res := ` +apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + spec: + containers: + - name: nginx + image: nginx` + return fmt.Sprintf(res, kind) +} + +// addContainerAndEnvPatch produces a patch object which adds +// an entry in the env slice of the first/nginx container +// as well as adding a label in the metadata +// Note that for SMP/WithSchema merge, the name:nginx entry +// is mandatory +func addLabelAndEnvPatch(kind string) string { + return fmt.Sprintf(` +apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + some-label: some-value + spec: + containers: + - name: nginx + env: + - name: SOMEENV + value: SOMEVALUE`, kind) +} + +// addContainerAndEnvPatch produces a patch object which adds +// an entry in the env slice of the first/nginx container +// as well as adding a second container in the container list +// Note that for SMP/WithSchema merge, the name:nginx entry +// is mandatory +func addContainerAndEnvPatch(kind string) string { + return fmt.Sprintf(` +apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - name: nginx + env: + - name: ANOTHERENV + value: ANOTHERVALUE + - name: anothercontainer + image: anotherimage`, kind) +} + +// addContainerAndEnvPatch produces a patch object which replaces +// the value of the image field in the first/nginx container +// Note that for SMP/WithSchema merge, the name:nginx entry +// is mandatory +func changeImagePatch(kind string, newImage string) string { + return fmt.Sprintf(` +apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - name: nginx + image: %s`, kind, newImage) +} + +// utility method to build the expected result of a multipatch +// the order of the patches still have influence especially +// in the insertion location within arrays. +func expectedResultMultiPatch(kind string, reversed bool) string { + pattern := `apiVersion: apps/v1 +kind: %s +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + %s + image: nginx:latest + name: nginx + - image: anotherimage + name: anothercontainer +` + if reversed { + return fmt.Sprintf(pattern, kind, `- name: SOMEENV + value: SOMEVALUE + - name: ANOTHERENV + value: ANOTHERVALUE`) + } + return fmt.Sprintf(pattern, kind, `- name: ANOTHERENV + value: ANOTHERVALUE + - name: SOMEENV + value: SOMEVALUE`) +} + +// utility method building the expected output of a JMP. +// imagename parameter allows to build a result consistent +// with the JMP behavior which basically overrides the +// entire "containers" list. +func expectedResultJMP(imagename string) string { + if imagename == "" { + return `apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - env: + - name: SOMEENV + value: SOMEVALUE + name: nginx +` + } + return fmt.Sprintf(`apiVersion: apps/v1 +kind: myCRD +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-label: old-value + some-label: some-value + spec: + containers: + - image: %s + name: nginx +`, imagename) +} + +func TestSameEndingSubarray(t *testing.T) { + testCases := map[string]struct { + a []string + b []string + expected bool + }{ + "both nil": { + expected: true, + }, + "one nil": { + b: []string{}, + expected: true, + }, + "both empty": { + a: []string{}, + b: []string{}, + expected: true, + }, + "no1": { + a: []string{"a"}, + b: []string{}, + expected: false, + }, + "no2": { + a: []string{"b", "a"}, + b: []string{"b"}, + expected: false, + }, + "yes1": { + a: []string{"a", "b"}, + b: []string{"b"}, + expected: true, + }, + "yes2": { + a: []string{"a", "b", "c"}, + b: []string{"b", "c"}, + expected: true, + }, + "yes3": { + a: []string{"a", "b", "c", "d", "e", "f"}, + b: []string{"f"}, + expected: true, + }, + } + for n := range testCases { + tc := testCases[n] + t.Run(n, func(t *testing.T) { + assert.Equal(t, tc.expected, utils.SameEndingSubSlice(tc.a, tc.b)) + }) + } +} + +func TestGetGvk(t *testing.T) { + r, err := factory.FromBytes([]byte(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`)) + assert.NoError(t, err) + + gvk := r.GetGvk() + expected := "apps" + actual := gvk.Group + if expected != actual { + t.Fatalf("expected '%s', got '%s'", expected, actual) + } + expected = "v1" + actual = gvk.Version + if expected != actual { + t.Fatalf("expected '%s', got '%s'", expected, actual) + } + expected = "Deployment" + actual = gvk.Kind + if expected != actual { + t.Fatalf("expected '%s', got '%s'", expected, actual) + } +} +func TestSetGvk(t *testing.T) { + r, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`)) + assert.NoError(t, err) + r.SetGvk(resid.GvkFromString("knd.ver.grp")) + gvk := r.GetGvk() + if expected, actual := "grp", gvk.Group; expected != actual { + t.Fatalf("expected '%s', got '%s'", expected, actual) + } + if expected, actual := "ver", gvk.Version; expected != actual { + t.Fatalf("expected '%s', got '%s'", expected, actual) + } + if expected, actual := "knd", gvk.Kind; expected != actual { + t.Fatalf("expected '%s', got '%s'", expected, actual) + } +} + +func TestRefBy(t *testing.T) { + r, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`)) + assert.NoError(t, err) + r.AppendRefBy(resid.FromString("knd1.ver1.gr1/name1.ns1")) + assert.Equal(t, r.RNode.MustString(), `apiVersion: v1 +kind: Deployment +metadata: + name: clown + annotations: + internal.config.kubernetes.io/refBy: knd1.ver1.gr1/name1.ns1 +spec: + numReplicas: 1 +`) + assert.Equal(t, r.GetRefBy(), []resid.ResId{resid.FromString("knd1.ver1.gr1/name1.ns1")}) + + r.AppendRefBy(resid.FromString("knd2.ver2.gr2/name2.ns2")) + assert.Equal(t, r.RNode.MustString(), `apiVersion: v1 +kind: Deployment +metadata: + name: clown + annotations: + internal.config.kubernetes.io/refBy: knd1.ver1.gr1/name1.ns1,knd2.ver2.gr2/name2.ns2 +spec: + numReplicas: 1 +`) + assert.Equal(t, r.GetRefBy(), []resid.ResId{ + resid.FromString("knd1.ver1.gr1/name1.ns1"), + resid.FromString("knd2.ver2.gr2/name2.ns2"), + }) +} + +func TestOrigin(t *testing.T) { + r, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`)) + assert.NoError(t, err) + origin := &Origin{ + Path: "deployment.yaml", + Repo: "github.com/myrepo", + Ref: "master", + } + assert.NoError(t, r.SetOrigin(origin)) + assert.Equal(t, `apiVersion: v1 +kind: Deployment +metadata: + name: clown + annotations: + config.kubernetes.io/origin: | + path: deployment.yaml + repo: github.com/myrepo + ref: master +spec: + numReplicas: 1 +`, r.MustString()) + or, err := r.GetOrigin() + assert.NoError(t, err) + assert.Equal(t, origin, or) +} + +func TestTransformations(t *testing.T) { + r, err := factory.FromBytes([]byte(` +apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`)) + assert.NoError(t, err) + origin1 := &Origin{ + Repo: "github.com/myrepo", + Ref: "master", + ConfiguredIn: "config.yaml", + ConfiguredBy: kyaml.ResourceIdentifier{ + TypeMeta: kyaml.TypeMeta{ + APIVersion: "builtin", + Kind: "Generator", + }, + NameMeta: kyaml.NameMeta{ + Name: "my-name", + Namespace: "my-namespace", + }, + }, + } + origin2 := &Origin{ + ConfiguredIn: "../base/config.yaml", + ConfiguredBy: kyaml.ResourceIdentifier{ + TypeMeta: kyaml.TypeMeta{ + APIVersion: "builtin", + Kind: "Generator", + }, + NameMeta: kyaml.NameMeta{ + Name: "my-name", + Namespace: "my-namespace", + }, + }, + } + assert.NoError(t, r.AddTransformation(origin1)) + assert.Equal(t, `apiVersion: v1 +kind: Deployment +metadata: + name: clown + annotations: + alpha.config.kubernetes.io/transformations: | + - repo: github.com/myrepo + ref: master + configuredIn: config.yaml + configuredBy: + apiVersion: builtin + kind: Generator + name: my-name + namespace: my-namespace +spec: + numReplicas: 1 +`, r.MustString()) + assert.NoError(t, r.AddTransformation(origin2)) + assert.Equal(t, `apiVersion: v1 +kind: Deployment +metadata: + name: clown + annotations: + alpha.config.kubernetes.io/transformations: | + - repo: github.com/myrepo + ref: master + configuredIn: config.yaml + configuredBy: + apiVersion: builtin + kind: Generator + name: my-name + namespace: my-namespace + - configuredIn: ../base/config.yaml + configuredBy: + apiVersion: builtin + kind: Generator + name: my-name + namespace: my-namespace +spec: + numReplicas: 1 +`, r.MustString()) + transformations, err := r.GetTransformations() + assert.NoError(t, err) + assert.Equal(t, Transformations{origin1, origin2}, transformations) + assert.NoError(t, r.ClearTransformations()) + assert.Equal(t, `apiVersion: v1 +kind: Deployment +metadata: + name: clown +spec: + numReplicas: 1 +`, r.MustString()) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/filtertest/runfilter.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/filtertest/runfilter.go new file mode 100644 index 000000000..95f8d26c3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/filtertest/runfilter.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filtertest_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func run(input string, f kio.Filter) (string, error) { + var out bytes.Buffer + rw := kio.ByteReadWriter{ + Reader: bytes.NewBufferString(input), + Writer: &out, + } + + err := kio.Pipeline{ + Inputs: []kio.Reader{&rw}, + Filters: []kio.Filter{f}, + Outputs: []kio.Writer{&rw}, + }.Execute() + if err != nil { + return "", err + } + return out.String(), nil +} + +// RunFilter runs filter and panic if there is error +func RunFilter(t *testing.T, input string, f kio.Filter) string { + output, err := run(input, f) + if !assert.NoError(t, err) { + t.FailNow() + } + return output +} + +// RunFilterE runs filter and return error if there is +func RunFilterE(t *testing.T, input string, f kio.Filter) (string, error) { + output, err := run(input, f) + if err != nil { + return "", err + } + return output, nil +} + +type SetValueArg struct { + Key string + Value string + Tag string + NodePath []string +} + +// MutationTrackerStub to help stub a mutation tracker for kio.TrackableFilter +type MutationTrackerStub struct { + setValueArgs []SetValueArg +} + +func (mts *MutationTrackerStub) MutationTracker(key, value, tag string, node *yaml.RNode) { + mts.setValueArgs = append(mts.setValueArgs, SetValueArg{ + Key: key, + Value: value, + Tag: tag, + NodePath: node.FieldPath(), + }) +} + +func (mts *MutationTrackerStub) SetValueArgs() []SetValueArg { + return mts.setValueArgs +} + +func (mts *MutationTrackerStub) Reset() { + mts.setValueArgs = nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/harness.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/harness.go new file mode 100644 index 000000000..412502a50 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/harness.go @@ -0,0 +1,137 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kusttest_test + +import ( + "path/filepath" + "testing" + + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// Harness manages a test environment. +type Harness struct { + t *testing.T + fSys filesys.FileSystem +} + +func MakeHarness(t *testing.T) Harness { + return MakeHarnessWithFs(t, filesys.MakeFsInMemory()) +} + +func MakeHarnessWithFs( + t *testing.T, fSys filesys.FileSystem) Harness { + return Harness{ + t: t, + fSys: fSys, + } +} + +func (th Harness) GetT() *testing.T { + return th.t +} + +func (th Harness) GetFSys() filesys.FileSystem { + return th.fSys +} + +func (th Harness) WriteK(path string, content string) { + err := th.fSys.WriteFile( + filepath.Join( + path, + konfig.DefaultKustomizationFileName()), []byte(` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +`+content)) + if err != nil { + th.t.Fatalf("unexpected error while writing Kustomization to %s: %v", path, err) + } +} + +func (th Harness) WriteC(path string, content string) { + err := th.fSys.WriteFile( + filepath.Join( + path, + konfig.DefaultKustomizationFileName()), []byte(` +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component +`+content)) + if err != nil { + th.t.Fatalf("unexpected error while writing Component to %s: %v", path, err) + } +} + +func (th Harness) WriteF(path string, content string) { + err := th.fSys.WriteFile(path, []byte(content)) + if err != nil { + th.t.Fatalf("unexpected error while writing file to %s: %v", path, err) + } +} + +func (th Harness) MakeDefaultOptions() krusty.Options { + return th.MakeOptionsPluginsDisabled() +} + +// This has no impact on Builtin plugins, as they are always enabled. +func (th Harness) MakeOptionsPluginsDisabled() krusty.Options { + return *krusty.MakeDefaultOptions() +} + +// Enables use of non-builtin plugins. +func (th Harness) MakeOptionsPluginsEnabled() krusty.Options { + pc := types.EnabledPluginConfig(types.BploLoadFromFileSys) + o := *krusty.MakeDefaultOptions() + o.PluginConfig = pc + return o +} + +// Run, failing on error. +func (th Harness) Run(path string, o krusty.Options) resmap.ResMap { + m, err := krusty.MakeKustomizer(&o).Run(th.fSys, path) + if err != nil { + th.t.Fatal(err) + } + return m +} + +// Run, failing if there is no error. +func (th Harness) RunWithErr(path string, o krusty.Options) error { + _, err := krusty.MakeKustomizer(&o).Run(th.fSys, path) + if err == nil { + th.t.Fatalf("expected error") + } + return err +} + +func (th Harness) WriteLegacyConfigs(fName string) { + m := builtinpluginconsts.GetDefaultFieldSpecsAsMap() + var content []byte + for _, tCfg := range m { + content = append(content, []byte(tCfg)...) + } + err := th.fSys.WriteFile(fName, content) + if err != nil { + th.t.Fatalf("unable to add file %s", fName) + } +} + +func (th Harness) AssertActualEqualsExpected( + m resmap.ResMap, expected string) { + th.AssertActualEqualsExpectedWithTweak(m, nil, expected) +} + +func (th Harness) AssertActualEqualsExpectedNoIdAnnotations(m resmap.ResMap, expected string) { + m.RemoveBuildAnnotations() + th.AssertActualEqualsExpectedWithTweak(m, nil, expected) +} + +func (th Harness) AssertActualEqualsExpectedWithTweak( + m resmap.ResMap, tweaker func([]byte) []byte, expected string) { + assertActualEqualsExpectedWithTweak(th, m, tweaker, expected) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/harnessenhanced.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/harnessenhanced.go new file mode 100644 index 000000000..fa33f5db8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/harnessenhanced.go @@ -0,0 +1,227 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kusttest_test + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/ifc" + pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader" + "sigs.k8s.io/kustomize/api/konfig" + fLdr "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// HarnessEnhanced manages a full plugin environment for tests. +type HarnessEnhanced struct { + // An instance of *testing.T, and a filesystem (likely in-memory) + // for loading test data - plugin config, resources to transform, etc. + Harness + + // plugintestEnv holds the plugin compiler and data needed to + // create compilation sub-processes. + pte *pluginTestEnv + + // rf creates Resources from byte streams. + rf *resmap.Factory + + // A file loader using the Harness.fSys to read test data. + ldr ifc.Loader + + // If true, wipe the ifc.loader root (not the plugin loader root) + // as part of cleanup. + shouldWipeLdrRoot bool + + // A plugin loader that loads plugins from a (real) file system. + pl *pLdr.Loader +} + +func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced { + r := makeBaseEnhancedHarness(t) + r.Harness = MakeHarnessWithFs(t, filesys.MakeFsInMemory()) + // Point the Harness's file loader to the root ('/') + // of the in-memory file system. + r.ResetLoaderRoot(filesys.Separator) + return r +} + +func MakeEnhancedHarnessWithTmpRoot(t *testing.T) *HarnessEnhanced { + r := makeBaseEnhancedHarness(t) + fSys := filesys.MakeFsOnDisk() + r.Harness = MakeHarnessWithFs(t, fSys) + tmpDir, err := ioutil.TempDir("", "kust-testing-") + if err != nil { + panic("test harness cannot make tmp dir: " + err.Error()) + } + r.ldr, err = fLdr.NewLoader(fLdr.RestrictionRootOnly, tmpDir, fSys) + if err != nil { + panic("test harness cannot make ldr at tmp dir: " + err.Error()) + } + r.shouldWipeLdrRoot = true + return r +} + +func makeBaseEnhancedHarness(t *testing.T) *HarnessEnhanced { + rf := resmap.NewFactory( + provider.NewDefaultDepProvider().GetResourceFactory()) + return &HarnessEnhanced{ + pte: newPluginTestEnv(t).set(), + rf: rf, + pl: pLdr.NewLoader( + types.EnabledPluginConfig(types.BploLoadFromFileSys), + rf, + // Plugin configs are always located on disk, + // regardless of the test harness's FS + filesys.MakeFsOnDisk())} +} + +func (th *HarnessEnhanced) ErrIfNoHelm() error { + _, err := exec.LookPath(th.GetPluginConfig().HelmConfig.Command) + return err +} + +func (th *HarnessEnhanced) GetRoot() string { + return th.ldr.Root() +} + +func (th *HarnessEnhanced) MkDir(path string) string { + dir := filepath.Join(th.ldr.Root(), path) + th.GetFSys().Mkdir(dir) + return dir +} + +func (th *HarnessEnhanced) Reset() { + if th.shouldWipeLdrRoot { + root, _ := filepath.EvalSymlinks(th.ldr.Root()) + tmpdir, _ := filepath.EvalSymlinks(os.TempDir()) + + if !strings.HasPrefix(root, tmpdir) { + // sanity check. + panic("something strange about th.ldr.Root() = " + th.ldr.Root()) + } + os.RemoveAll(th.ldr.Root()) + } + th.pte.reset() +} + +func (th *HarnessEnhanced) GetPluginConfig() *types.PluginConfig { + return th.pl.Config() +} + +func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced { + return th.BuildGoPlugin(konfig.BuiltinPluginPackage, "", k) +} + +func (th *HarnessEnhanced) BuildGoPlugin(g, v, k string) *HarnessEnhanced { + th.pte.prepareGoPlugin(g, v, k) + return th +} + +func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced { + th.pte.prepareExecPlugin(g, v, k) + return th +} + +// ResetLoaderRoot interprets its argument as an absolute directory path. +// It creates the directory, and creates the harness's file loader +// rooted in that directory. +func (th *HarnessEnhanced) ResetLoaderRoot(root string) { + if err := th.fSys.Mkdir(root); err != nil { + th.t.Fatal(err) + } + ldr, err := fLdr.NewLoader( + fLdr.RestrictionRootOnly, root, th.fSys) + if err != nil { + th.t.Fatalf("Unable to make loader: %v", err) + } + th.ldr = ldr +} + +func (th *HarnessEnhanced) LoadAndRunGenerator( + config string) resmap.ResMap { + rm := th.LoadAndRunGeneratorWithBuildAnnotations(config) + rm.RemoveBuildAnnotations() + return rm +} + +func (th *HarnessEnhanced) LoadAndRunGeneratorWithBuildAnnotations( + config string) resmap.ResMap { + res, err := th.rf.RF().FromBytes([]byte(config)) + if err != nil { + th.t.Fatalf("Err: %v", err) + } + g, err := th.pl.LoadGenerator( + th.ldr, valtest_test.MakeFakeValidator(), res) + if err != nil { + th.t.Fatalf("Err: %v", err) + } + rm, err := g.Generate() + if err != nil { + th.t.Fatalf("generate err: %v", err) + } + return rm +} + +func (th *HarnessEnhanced) LoadAndRunTransformer( + config, input string) resmap.ResMap { + resMap, err := th.RunTransformer(config, input) + if err != nil { + th.t.Fatalf("Err: %v", err) + } + return resMap +} + +func (th *HarnessEnhanced) RunTransformerAndCheckResult( + config, input, expected string) { + resMap := th.LoadAndRunTransformer(config, input) + th.AssertActualEqualsExpectedNoIdAnnotations(resMap, expected) +} + +func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer( + config, input string) error { + _, err := th.RunTransformer(config, input) + return err +} + +type AssertFunc func(t *testing.T, err error) + +func (th *HarnessEnhanced) RunTransformerAndCheckError( + config, input string, assertFn AssertFunc) { + _, err := th.RunTransformer(config, input) + assertFn(th.t, err) +} + +func (th *HarnessEnhanced) RunTransformer( + config, input string) (resmap.ResMap, error) { + resMap, err := th.rf.NewResMapFromBytes([]byte(input)) + if err != nil { + th.t.Fatalf("Err: %v", err) + } + return th.RunTransformerFromResMap(config, resMap) +} + +func (th *HarnessEnhanced) RunTransformerFromResMap( + config string, resMap resmap.ResMap) (resmap.ResMap, error) { + transConfig, err := th.rf.RF().FromBytes([]byte(config)) + if err != nil { + th.t.Logf("config: '%s'", config) + th.t.Fatalf("Err: %v", err) + } + g, err := th.pl.LoadTransformer( + th.ldr, valtest_test.MakeFakeValidator(), transConfig) + if err != nil { + return nil, err + } + err = g.Transform(resMap) + return resMap, err +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/hasgett.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/hasgett.go new file mode 100644 index 000000000..33496b9d2 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/hasgett.go @@ -0,0 +1,111 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kusttest_test + +import ( + "fmt" + "strings" + "testing" + + "sigs.k8s.io/kustomize/api/resmap" +) + +type hasGetT interface { + GetT() *testing.T +} + +func assertActualEqualsExpectedWithTweak( + ht hasGetT, + m resmap.ResMap, + tweaker func([]byte) []byte, expected string) { + AssertActualEqualsExpectedWithTweak(ht.GetT(), m, tweaker, expected) +} + +func AssertActualEqualsExpectedWithTweak( + t *testing.T, + m resmap.ResMap, + tweaker func([]byte) []byte, expected string) { + if m == nil { + t.Fatalf("Map should not be nil.") + } + // Ignore leading linefeed in expected value + // to ease readability of tests. + if len(expected) > 0 && expected[0] == 10 { + expected = expected[1:] + } + actual, err := m.AsYaml() + if err != nil { + t.Fatalf("Unexpected err: %v", err) + } + if tweaker != nil { + actual = tweaker(actual) + } + if string(actual) != expected { + reportDiffAndFail(t, actual, expected) + } +} + +// Pretty printing of file differences. +func reportDiffAndFail( + t *testing.T, actual []byte, expected string) { + sE, maxLen := convertToArray(expected) + sA, _ := convertToArray(string(actual)) + fmt.Println("===== ACTUAL BEGIN ========================================") + fmt.Print(string(actual)) + fmt.Println("===== ACTUAL END ==========================================") + format := fmt.Sprintf("%%s %%-%ds %%s\n", maxLen+4) + limit := 0 + if len(sE) < len(sA) { + limit = len(sE) + } else { + limit = len(sA) + } + fmt.Printf(format, " ", "EXPECTED", "ACTUAL") + fmt.Printf(format, " ", "--------", "------") + for i := 0; i < limit; i++ { + fmt.Printf(format, hint(sE[i], sA[i]), sE[i], sA[i]) + } + if len(sE) < len(sA) { + for i := len(sE); i < len(sA); i++ { + fmt.Printf(format, "X", "", sA[i]) + } + } else { + for i := len(sA); i < len(sE); i++ { + fmt.Printf(format, "X", sE[i], "") + } + } + t.Fatalf("Expected not equal to actual") +} + +func hint(a, b string) string { + if a == b { + return " " + } + return "X" +} + +func convertToArray(x string) ([]string, int) { + a := strings.Split(strings.TrimSuffix(x, "\n"), "\n") + maxLen := 0 + for i, v := range a { + z := tabToSpace(v) + if len(z) > maxLen { + maxLen = len(z) + } + a[i] = z + } + return a, maxLen +} + +func tabToSpace(input string) string { + var result []string + for _, i := range input { + if i == 9 { + result = append(result, " ") + } else { + result = append(result, string(i)) + } + } + return strings.Join(result, "") +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/plugintestenv.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/plugintestenv.go new file mode 100644 index 000000000..0bc5bd183 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/kusttest/plugintestenv.go @@ -0,0 +1,82 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kusttest_test + +import ( + "os" + "testing" + + "sigs.k8s.io/kustomize/api/internal/plugins/compiler" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// pluginTestEnv manages compiling plugins for tests. +// It manages a Go plugin compiler, and sets/resets shell env vars as needed. +type pluginTestEnv struct { + t *testing.T + compiler *compiler.Compiler + pluginRoot string + oldXdg string + wasSet bool +} + +// newPluginTestEnv returns a new instance of pluginTestEnv. +func newPluginTestEnv(t *testing.T) *pluginTestEnv { + return &pluginTestEnv{t: t} +} + +// set creates a test environment. +// Uses a filesystem on disk for compilation (or copying) of +// plugin code - this FileSystem has nothing to do with +// the FileSystem used for loading config yaml in the tests. +func (x *pluginTestEnv) set() *pluginTestEnv { + var err error + x.pluginRoot, err = utils.DeterminePluginSrcRoot(filesys.MakeFsOnDisk()) + if err != nil { + x.t.Error(err) + } + x.compiler = compiler.NewCompiler(x.pluginRoot) + x.setEnv() + return x +} + +// reset restores the environment to pre-test state. +func (x *pluginTestEnv) reset() { + // Calling Cleanup forces recompilation in a test file with multiple + // calls to MakeEnhancedHarness - so leaving it out. Your .gitignore + // should ignore .so files anyway. + // x.compiler.Cleanup() + x.resetEnv() +} + +// prepareGoPlugin compiles a Go plugin, leaving the newly +// created object code alongside the src code. +func (x *pluginTestEnv) prepareGoPlugin(g, v, k string) { + x.compiler.SetGVK(g, v, k) + err := x.compiler.Compile() + if err != nil { + x.t.Errorf("compile failed: %v", err) + } +} + +func (x *pluginTestEnv) prepareExecPlugin(_, _, _ string) { + // Do nothing. At one point this method + // copied the exec plugin directory to a temp dir + // and ran it from there. Left as a hook. +} + +func (x *pluginTestEnv) setEnv() { + x.oldXdg, x.wasSet = os.LookupEnv(konfig.KustomizePluginHomeEnv) + os.Setenv(konfig.KustomizePluginHomeEnv, x.pluginRoot) +} + +func (x *pluginTestEnv) resetEnv() { + if x.wasSet { + os.Setenv(konfig.KustomizePluginHomeEnv, x.oldXdg) + } else { + os.Unsetenv(konfig.KustomizePluginHomeEnv) + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/resmaptest/rmbuilder.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/resmaptest/rmbuilder.go new file mode 100644 index 000000000..5b725c3c4 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/resmaptest/rmbuilder.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package resmaptest_test + +import ( + "testing" + + "sigs.k8s.io/kustomize/api/provider" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// Builds ResMaps for tests, with test-aware error handling. +type rmBuilder struct { + t *testing.T + m resmap.ResMap + rf *resource.Factory +} + +func NewSeededRmBuilder(t *testing.T, rf *resource.Factory, m resmap.ResMap) *rmBuilder { + return &rmBuilder{t: t, rf: rf, m: m} +} + +func NewRmBuilder(t *testing.T, rf *resource.Factory) *rmBuilder { + return NewSeededRmBuilder(t, rf, resmap.New()) +} + +func NewRmBuilderDefault(t *testing.T) *rmBuilder { + return NewSeededRmBuilderDefault(t, resmap.New()) +} + +func NewSeededRmBuilderDefault(t *testing.T, m resmap.ResMap) *rmBuilder { + return NewSeededRmBuilder( + t, provider.NewDefaultDepProvider().GetResourceFactory(), m) +} + +func (rm *rmBuilder) Add(m map[string]interface{}) *rmBuilder { + return rm.AddR(rm.rf.FromMap(m)) +} + +func (rm *rmBuilder) AddR(r *resource.Resource) *rmBuilder { + err := rm.m.Append(r) + if err != nil { + rm.t.Fatalf("test setup failure: %v", err) + } + return rm +} + +func (rm *rmBuilder) AddWithName(n string, m map[string]interface{}) *rmBuilder { + err := rm.m.Append(rm.rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m)) + if err != nil { + rm.t.Fatalf("test setup failure: %v", err) + } + return rm +} + +func (rm *rmBuilder) AddWithNsAndName(ns string, n string, m map[string]interface{}) *rmBuilder { + err := rm.m.Append(rm.rf.FromMapWithNamespaceAndName(ns, n, m)) + if err != nil { + rm.t.Fatalf("test setup failure: %v", err) + } + return rm +} + +func (rm *rmBuilder) ReplaceResource(m map[string]interface{}) *rmBuilder { + r := rm.rf.FromMap(m) + _, err := rm.m.Replace(r) + if err != nil { + rm.t.Fatalf("test setup failure: %v", err) + } + return rm +} + +func (rm *rmBuilder) ResMap() resmap.ResMap { + return rm.m +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/valtest/fakevalidator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/valtest/fakevalidator.go new file mode 100644 index 000000000..7e7daadfa --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/testutils/valtest/fakevalidator.go @@ -0,0 +1,108 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package valtest_test defines a fakeValidator that can be used in tests +package valtest_test + +import ( + "errors" + "regexp" + "testing" +) + +// fakeValidator can be used in tests. +type fakeValidator struct { + happy bool + called bool + t *testing.T +} + +// SAD is an error string. +const SAD = "i'm not happy Bob, NOT HAPPY" + +// MakeHappyMapValidator makes a fakeValidator that always passes. +func MakeHappyMapValidator(t *testing.T) *fakeValidator { + return &fakeValidator{happy: true, t: t} +} + +// MakeSadMapValidator makes a fakeValidator that always fails. +func MakeSadMapValidator(t *testing.T) *fakeValidator { + return &fakeValidator{happy: false, t: t} +} + +// MakeFakeValidator makes an empty Fake Validator. +func MakeFakeValidator() *fakeValidator { + return &fakeValidator{} +} + +// ErrIfInvalidKey returns nil +func (v *fakeValidator) ErrIfInvalidKey(_ string) error { + return nil +} + +// IsEnvVarName returns nil +func (v *fakeValidator) IsEnvVarName(_ string) error { + return nil +} + +// MakeAnnotationValidator returns a nil function +func (v *fakeValidator) MakeAnnotationValidator() func(map[string]string) error { + return nil +} + +// MakeAnnotationNameValidator returns a nil function +func (v *fakeValidator) MakeAnnotationNameValidator() func([]string) error { + return nil +} + +// MakeLabelValidator returns a nil function +func (v *fakeValidator) MakeLabelValidator() func(map[string]string) error { + return nil +} + +// MakeLabelNameValidator returns a nil function +func (v *fakeValidator) MakeLabelNameValidator() func([]string) error { + return nil +} + +// ValidateNamespace validates namespace by regexp +func (v *fakeValidator) ValidateNamespace(s string) []string { + pattern := regexp.MustCompile(`^[a-zA-Z].*`) + if pattern.MatchString(s) { + return nil + } + return []string{"doesn't match"} +} + +// Validator replaces apimachinery validation in tests. +// Can be set to fail or succeed to test error handling. +// Can confirm if run or not run by surrounding code. +func (v *fakeValidator) Validator(_ map[string]string) error { + v.called = true + if v.happy { + return nil + } + return errors.New(SAD) +} + +func (v *fakeValidator) ValidatorArray(_ []string) error { + v.called = true + if v.happy { + return nil + } + return errors.New(SAD) +} + +// VerifyCall returns true if Validator was used. +func (v *fakeValidator) VerifyCall() { + if !v.called { + v.t.Errorf("should have called Validator") + } +} + +// VerifyNoCall returns true if Validator was not used. +func (v *fakeValidator) VerifyNoCall() { + if v.called { + v.t.Errorf("should not have called Validator") + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/builtinpluginloadingoptions_string.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/builtinpluginloadingoptions_string.go new file mode 100644 index 000000000..033a45123 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/builtinpluginloadingoptions_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type=BuiltinPluginLoadingOptions"; DO NOT EDIT. + +package types + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BploUndefined-0] + _ = x[BploUseStaticallyLinked-1] + _ = x[BploLoadFromFileSys-2] +} + +const _BuiltinPluginLoadingOptions_name = "BploUndefinedBploUseStaticallyLinkedBploLoadFromFileSys" + +var _BuiltinPluginLoadingOptions_index = [...]uint8{0, 13, 36, 55} + +func (i BuiltinPluginLoadingOptions) String() string { + if i < 0 || i >= BuiltinPluginLoadingOptions(len(_BuiltinPluginLoadingOptions_index)-1) { + return "BuiltinPluginLoadingOptions(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BuiltinPluginLoadingOptions_name[_BuiltinPluginLoadingOptions_index[i]:_BuiltinPluginLoadingOptions_index[i+1]] +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/configmapargs.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/configmapargs.go new file mode 100644 index 000000000..69877769f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/configmapargs.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// ConfigMapArgs contains the metadata of how to generate a configmap. +type ConfigMapArgs struct { + // GeneratorArgs for the configmap. + GeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/doc.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/doc.go new file mode 100644 index 000000000..22c38a651 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/doc.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package types holds the definition of the kustomization struct and +// supporting structs. It's the k8s API conformant object that describes +// a set of generation and transformation operations to create and/or +// modify k8s resources. +// A kustomization file is a serialization of this struct. +package types diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/erronlybuiltinpluginsallowed.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/erronlybuiltinpluginsallowed.go new file mode 100644 index 000000000..73ee95fd7 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/erronlybuiltinpluginsallowed.go @@ -0,0 +1,33 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + + "github.com/pkg/errors" +) + +type errOnlyBuiltinPluginsAllowed struct { + name string +} + +func (e *errOnlyBuiltinPluginsAllowed) Error() string { + return fmt.Sprintf( + "external plugins disabled; unable to load external plugin '%s'", + e.name) +} + +func NewErrOnlyBuiltinPluginsAllowed(n string) *errOnlyBuiltinPluginsAllowed { + return &errOnlyBuiltinPluginsAllowed{name: n} +} + +func IsErrOnlyBuiltinPluginsAllowed(err error) bool { + _, ok := err.(*errOnlyBuiltinPluginsAllowed) + if ok { + return true + } + _, ok = errors.Cause(err).(*errOnlyBuiltinPluginsAllowed) + return ok +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/errunabletofind.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/errunabletofind.go new file mode 100644 index 000000000..f95b8edd5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/errunabletofind.go @@ -0,0 +1,40 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" +) + +type errUnableToFind struct { + // What are we unable to find? + what string + // What things did we try? + attempts []Pair +} + +func (e *errUnableToFind) Error() string { + var m []string + for _, p := range e.attempts { + m = append(m, "('"+p.Value+"'; "+p.Key+")") + } + return fmt.Sprintf( + "unable to find %s - tried: %s", e.what, strings.Join(m, ", ")) +} + +func NewErrUnableToFind(w string, a []Pair) *errUnableToFind { + return &errUnableToFind{what: w, attempts: a} +} + +func IsErrUnableToFind(err error) bool { + _, ok := err.(*errUnableToFind) + if ok { + return true + } + _, ok = errors.Cause(err).(*errUnableToFind) + return ok +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fieldspec.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fieldspec.go new file mode 100644 index 000000000..8c248e0dc --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fieldspec.go @@ -0,0 +1,98 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// FieldSpec completely specifies a kustomizable field in a k8s API object. +// It helps define the operands of transformations. +// +// For example, a directive to add a common label to objects +// will need to know that a 'Deployment' object (in API group +// 'apps', any version) can have labels at field path +// 'spec/template/metadata/labels', and further that it is OK +// (or not OK) to add that field path to the object if the +// field path doesn't exist already. +// +// This would look like +// { +// group: apps +// kind: Deployment +// path: spec/template/metadata/labels +// create: true +// } +type FieldSpec struct { + resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` + RegexPattern string `json:"regexPattern,omitempty" yaml:"regexPattern,omitempty"` + CreateIfNotPresent bool `json:"create,omitempty" yaml:"create,omitempty"` +} + +func (fs FieldSpec) String() string { + if fs.RegexPattern == "" { + return fmt.Sprintf( + "%s:%v:%s", fs.Gvk.String(), fs.CreateIfNotPresent, fs.Path) + } else { + + return fmt.Sprintf( + "%s:%v:%s:%s", fs.Gvk.String(), fs.CreateIfNotPresent, fs.Path, fs.RegexPattern) + } +} + +// If true, the primary key is the same, but other fields might not be. +func (fs FieldSpec) effectivelyEquals(other FieldSpec) bool { + return fs.IsSelected(&other.Gvk) && fs.Path == other.Path +} + +type FsSlice []FieldSpec + +func (s FsSlice) Len() int { return len(s) } +func (s FsSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s FsSlice) Less(i, j int) bool { + return s[i].Gvk.IsLessThan(s[j].Gvk) +} + +// MergeAll merges the argument into this, returning the result. +// Items already present are ignored. +// Items that conflict (primary key matches, but remain data differs) +// result in an error. +func (s FsSlice) MergeAll(incoming FsSlice) (result FsSlice, err error) { + result = s + for _, x := range incoming { + result, err = result.MergeOne(x) + if err != nil { + return nil, err + } + } + return result, nil +} + +// MergeOne merges the argument into this, returning the result. +// If the item's primary key is already present, and there are no +// conflicts, it is ignored (we don't want duplicates). +// If there is a conflict, the merge fails. +func (s FsSlice) MergeOne(x FieldSpec) (FsSlice, error) { + i := s.index(x) + if i > -1 { + // It's already there. + if s[i].CreateIfNotPresent != x.CreateIfNotPresent { + return nil, fmt.Errorf("conflicting fieldspecs") + } + return s, nil + } + return append(s, x), nil +} + +func (s FsSlice) index(fs FieldSpec) int { + for i, x := range s { + if x.effectivelyEquals(fs) { + return i + } + } + return -1 +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fieldspec_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fieldspec_test.go new file mode 100644 index 000000000..c0518b94a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fieldspec_test.go @@ -0,0 +1,144 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types_test + +import ( + "fmt" + "reflect" + "strings" + "testing" + + . "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +var mergeTests = []struct { + name string + original FsSlice + incoming FsSlice + err error + result FsSlice +}{ + { + "normal", + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: false, + }, + { + Path: "whatever", + Gvk: resid.Gvk{Group: "pear"}, + CreateIfNotPresent: false, + }, + }, + FsSlice{ + { + Path: "home", + Gvk: resid.Gvk{Group: "beans"}, + CreateIfNotPresent: false, + }, + }, + nil, + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: false, + }, + { + Path: "whatever", + Gvk: resid.Gvk{Group: "pear"}, + CreateIfNotPresent: false, + }, + { + Path: "home", + Gvk: resid.Gvk{Group: "beans"}, + CreateIfNotPresent: false, + }, + }, + }, + { + "ignore copy", + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: false, + }, + { + Path: "whatever", + Gvk: resid.Gvk{Group: "pear"}, + CreateIfNotPresent: false, + }, + }, + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: false, + }, + }, + nil, + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: false, + }, + { + Path: "whatever", + Gvk: resid.Gvk{Group: "pear"}, + CreateIfNotPresent: false, + }, + }, + }, + { + "error on conflict", + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: false, + }, + { + Path: "whatever", + Gvk: resid.Gvk{Group: "pear"}, + CreateIfNotPresent: false, + }, + }, + FsSlice{ + { + Path: "whatever", + Gvk: resid.Gvk{Group: "apple"}, + CreateIfNotPresent: true, + }, + }, + fmt.Errorf("hey"), + FsSlice{}, + }, +} + +func TestFsSlice_MergeAll(t *testing.T) { + for _, item := range mergeTests { + result, err := item.original.MergeAll(item.incoming) + if item.err == nil { + if err != nil { + t.Fatalf("test %s: unexpected err %v", item.name, err) + } + if !reflect.DeepEqual(item.result, result) { + t.Fatalf("test %s: expected: %v\n but got: %v\n", + item.name, item.result, result) + } + } else { + if err == nil { + t.Fatalf("test %s: expected err: %v", item.name, err) + } + if !strings.Contains(err.Error(), "conflicting fieldspecs") { + t.Fatalf("test %s: unexpected err: %v", item.name, err) + } + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fix.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fix.go new file mode 100644 index 000000000..de70467ed --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/fix.go @@ -0,0 +1,54 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "regexp" + + "sigs.k8s.io/yaml" +) + +// FixKustomizationPreUnmarshalling modifies the raw data +// before marshalling - e.g. changes old field names to +// new field names. +func FixKustomizationPreUnmarshalling(data []byte) ([]byte, error) { + deprecatedFieldsMap := map[string]string{ + "imageTags:": "images:", + } + for oldname, newname := range deprecatedFieldsMap { + pattern := regexp.MustCompile(oldname) + data = pattern.ReplaceAll(data, []byte(newname)) + } + doLegacy, err := useLegacyPatch(data) + if err != nil { + return nil, err + } + if doLegacy { + pattern := regexp.MustCompile("patches:") + data = pattern.ReplaceAll(data, []byte("patchesStrategicMerge:")) + } + return data, nil +} + +func useLegacyPatch(data []byte) (bool, error) { + found := false + var object map[string]interface{} + err := yaml.Unmarshal(data, &object) + if err != nil { + return false, err + } + if rawPatches, ok := object["patches"]; ok { + patches, ok := rawPatches.([]interface{}) + if !ok { + return false, err + } + for _, p := range patches { + _, ok := p.(string) + if ok { + found = true + } + } + } + return found, nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generationbehavior.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generationbehavior.go new file mode 100644 index 000000000..f8f362780 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generationbehavior.go @@ -0,0 +1,46 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// GenerationBehavior specifies generation behavior of configmaps, secrets and maybe other resources. +type GenerationBehavior int + +const ( + // BehaviorUnspecified is an Unspecified behavior; typically treated as a Create. + BehaviorUnspecified GenerationBehavior = iota + // BehaviorCreate makes a new resource. + BehaviorCreate + // BehaviorReplace replaces a resource. + BehaviorReplace + // BehaviorMerge attempts to merge a new resource with an existing resource. + BehaviorMerge +) + +// String converts a GenerationBehavior to a string. +func (b GenerationBehavior) String() string { + switch b { + case BehaviorReplace: + return "replace" + case BehaviorMerge: + return "merge" + case BehaviorCreate: + return "create" + default: + return "unspecified" + } +} + +// NewGenerationBehavior converts a string to a GenerationBehavior. +func NewGenerationBehavior(s string) GenerationBehavior { + switch s { + case "replace": + return BehaviorReplace + case "merge": + return BehaviorMerge + case "create": + return BehaviorCreate + default: + return BehaviorUnspecified + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatorargs.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatorargs.go new file mode 100644 index 000000000..a4145db3d --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatorargs.go @@ -0,0 +1,27 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// GeneratorArgs contains arguments common to ConfigMap and Secret generators. +type GeneratorArgs struct { + // Namespace for the configmap, optional + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + + // Name - actually the partial name - of the generated resource. + // The full name ends up being something like + // NamePrefix + this.Name + hash(content of generated resource). + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // Behavior of generated resource, must be one of: + // 'create': create a new one + // 'replace': replace the existing one + // 'merge': merge with the existing one + Behavior string `json:"behavior,omitempty" yaml:"behavior,omitempty"` + + // KvPairSources for the generator. + KvPairSources `json:",inline,omitempty" yaml:",inline,omitempty"` + + // Local overrides to global generatorOptions field. + Options *GeneratorOptions `json:"options,omitempty" yaml:"options,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatoroptions.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatoroptions.go new file mode 100644 index 000000000..683d89bfd --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatoroptions.go @@ -0,0 +1,76 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// GeneratorOptions modify behavior of all ConfigMap and Secret generators. +type GeneratorOptions struct { + // Labels to add to all generated resources. + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` + + // Annotations to add to all generated resources. + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` + + // DisableNameSuffixHash if true disables the default behavior of adding a + // suffix to the names of generated resources that is a hash of the + // resource contents. + DisableNameSuffixHash bool `json:"disableNameSuffixHash,omitempty" yaml:"disableNameSuffixHash,omitempty"` + + // Immutable if true add to all generated resources. + Immutable bool `json:"immutable,omitempty" yaml:"immutable,omitempty"` +} + +// MergeGlobalOptionsIntoLocal merges two instances of GeneratorOptions. +// Values in the first 'local' argument cannot be overridden by the second +// 'global' argument, except in the case of booleans. +// +// With booleans, there's no way to distinguish an 'intentional' +// false from 'default' false. So the rule is, if the global value +// of the value of a boolean is true, i.e. disable, it trumps the +// local value. If the global value is false, then the local value is +// respected. Bottom line: a local false cannot override a global true. +// +// boolean fields are always a bad idea; should always use enums instead. +func MergeGlobalOptionsIntoLocal( + localOpts *GeneratorOptions, + globalOpts *GeneratorOptions) *GeneratorOptions { + if globalOpts == nil { + return localOpts + } + if localOpts == nil { + localOpts = &GeneratorOptions{} + } + overrideMap(&localOpts.Labels, globalOpts.Labels) + overrideMap(&localOpts.Annotations, globalOpts.Annotations) + if globalOpts.DisableNameSuffixHash { + localOpts.DisableNameSuffixHash = true + } + if globalOpts.Immutable { + localOpts.Immutable = true + } + return localOpts +} + +func overrideMap(localMap *map[string]string, globalMap map[string]string) { + if *localMap == nil { + if globalMap != nil { + *localMap = CopyMap(globalMap) + } + return + } + for k, v := range globalMap { + _, ok := (*localMap)[k] + if !ok { + (*localMap)[k] = v + } + } +} + +// CopyMap copies a map. +func CopyMap(in map[string]string) map[string]string { + out := make(map[string]string) + for k, v := range in { + out[k] = v + } + return out +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatoroptions_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatoroptions_test.go new file mode 100644 index 000000000..a8bf634c3 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/generatoroptions_test.go @@ -0,0 +1,137 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types_test + +import ( + "reflect" + "testing" + + . "sigs.k8s.io/kustomize/api/types" +) + +func TestMergeGlobalOptionsIntoLocal(t *testing.T) { + tests := []struct { + name string + local *GeneratorOptions + global *GeneratorOptions + expected *GeneratorOptions + }{ + { + name: "everything nil", + local: nil, + global: nil, + expected: nil, + }, + { + name: "nil global", + local: &GeneratorOptions{ + Labels: map[string]string{"pet": "dog"}, + Annotations: map[string]string{"fruit": "apple"}, + }, + global: nil, + expected: &GeneratorOptions{ + Labels: map[string]string{"pet": "dog"}, + Annotations: map[string]string{"fruit": "apple"}, + DisableNameSuffixHash: false, + Immutable: false, + }, + }, + { + name: "nil local", + local: nil, + global: &GeneratorOptions{ + Labels: map[string]string{"pet": "dog"}, + Annotations: map[string]string{"fruit": "apple"}, + }, + expected: &GeneratorOptions{ + Labels: map[string]string{"pet": "dog"}, + Annotations: map[string]string{"fruit": "apple"}, + DisableNameSuffixHash: false, + Immutable: false, + }, + }, + { + name: "global doesn't damage local", + local: &GeneratorOptions{ + Labels: map[string]string{"pet": "dog"}, + Annotations: map[string]string{ + "fruit": "apple"}, + }, + global: &GeneratorOptions{ + Labels: map[string]string{ + "pet": "cat", + "simpson": "homer", + }, + Annotations: map[string]string{ + "fruit": "peach", + "tesla": "Y", + }, + }, + expected: &GeneratorOptions{ + Labels: map[string]string{ + "pet": "dog", + "simpson": "homer", + }, + Annotations: map[string]string{ + "fruit": "apple", + "tesla": "Y", + }, + DisableNameSuffixHash: false, + Immutable: false, + }, + }, + { + name: "global disable trumps local", + local: &GeneratorOptions{ + DisableNameSuffixHash: false, + Immutable: false, + }, + global: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + expected: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + }, + { + name: "local disable works", + local: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + global: &GeneratorOptions{ + DisableNameSuffixHash: false, + Immutable: false, + }, + expected: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + }, + { + name: "everyone wants disable", + local: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + global: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + expected: &GeneratorOptions{ + DisableNameSuffixHash: true, + Immutable: true, + }, + }, + } + for _, tc := range tests { + actual := MergeGlobalOptionsIntoLocal(tc.local, tc.global) + if !reflect.DeepEqual(tc.expected, actual) { + t.Fatalf("%s annotations: Expected '%v', got '%v'", + tc.name, tc.expected, *actual) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/helmchartargs.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/helmchartargs.go new file mode 100644 index 000000000..c0fff2457 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/helmchartargs.go @@ -0,0 +1,122 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +type HelmGlobals struct { + // ChartHome is a file path, relative to the kustomization root, + // to a directory containing a subdirectory for each chart to be + // included in the kustomization. + // The default value of this field is "charts". + // So, for example, kustomize looks for the minecraft chart + // at {kustomizationRoot}/{ChartHome}/minecraft. + // If the chart is there at build time, kustomize will use it as found, + // and not check version numbers or dates. + // If the chart is not there, kustomize will attempt to pull it + // using the version number specified in the kustomization file, + // and put it there. To suppress the pull attempt, simply assure + // that the chart is already there. + ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"` + + // ConfigHome defines a value that kustomize should pass to helm via + // the HELM_CONFIG_HOME environment variable. kustomize doesn't attempt + // to read or write this directory. + // If omitted, {tmpDir}/helm is used, where {tmpDir} is some temporary + // directory created by kustomize for the benefit of helm. + // Likewise, kustomize sets + // HELM_CACHE_HOME={ConfigHome}/.cache + // HELM_DATA_HOME={ConfigHome}/.data + // for the helm subprocess. + ConfigHome string `json:"configHome,omitempty" yaml:"configHome,omitempty"` +} + +type HelmChart struct { + // Name is the name of the chart, e.g. 'minecraft'. + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // Version is the version of the chart, e.g. '3.1.3' + Version string `json:"version,omitempty" yaml:"version,omitempty"` + + // Repo is a URL locating the chart on the internet. + // This is the argument to helm's `--repo` flag, e.g. + // `https://itzg.github.io/minecraft-server-charts`. + Repo string `json:"repo,omitempty" yaml:"repo,omitempty"` + + // ReleaseName replaces RELEASE-NAME in chart template output, + // making a particular inflation of a chart unique with respect to + // other inflations of the same chart in a cluster. It's the first + // argument to the helm `install` and `template` commands, i.e. + // helm install {RELEASE-NAME} {chartName} + // helm template {RELEASE-NAME} {chartName} + // If omitted, the flag --generate-name is passed to 'helm template'. + ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"` + + // Namespace set the target namespace for a release. It is .Release.Namespace + // in the helm template + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + + // ValuesFile is local file path to a values file to use _instead of_ + // the default values that accompanied the chart. + // The default values are in '{ChartHome}/{Name}/values.yaml'. + ValuesFile string `json:"valuesFile,omitempty" yaml:"valuesFile,omitempty"` + + // ValuesInline holds value mappings specified directly, + // rather than in a separate file. + ValuesInline map[string]interface{} `json:"valuesInline,omitempty" yaml:"valuesInline,omitempty"` + + // ValuesMerge specifies how to treat ValuesInline with respect to Values. + // Legal values: 'merge', 'override', 'replace'. + // Defaults to 'override'. + ValuesMerge string `json:"valuesMerge,omitempty" yaml:"valuesMerge,omitempty"` + + // IncludeCRDs specifies if Helm should also generate CustomResourceDefinitions. + // Defaults to 'false'. + IncludeCRDs bool `json:"includeCRDs,omitempty" yaml:"includeCRDs,omitempty"` +} + +// HelmChartArgs contains arguments to helm. +// Deprecated. Use HelmGlobals and HelmChart instead. +type HelmChartArgs struct { + ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"` + ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"` + ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"` + ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"` + ChartRepoName string `json:"chartRepoName,omitempty" yaml:"chartRepoName,omitempty"` + HelmBin string `json:"helmBin,omitempty" yaml:"helmBin,omitempty"` + HelmHome string `json:"helmHome,omitempty" yaml:"helmHome,omitempty"` + Values string `json:"values,omitempty" yaml:"values,omitempty"` + ValuesLocal map[string]interface{} `json:"valuesLocal,omitempty" yaml:"valuesLocal,omitempty"` + ValuesMerge string `json:"valuesMerge,omitempty" yaml:"valuesMerge,omitempty"` + ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"` + ReleaseNamespace string `json:"releaseNamespace,omitempty" yaml:"releaseNamespace,omitempty"` + ExtraArgs []string `json:"extraArgs,omitempty" yaml:"extraArgs,omitempty"` +} + +// SplitHelmParameters splits helm parameters into +// per-chart params and global chart-independent parameters. +func SplitHelmParameters( + oldArgs []HelmChartArgs) (charts []HelmChart, globals HelmGlobals) { + for _, old := range oldArgs { + charts = append(charts, makeHelmChartFromHca(&old)) + if old.HelmHome != "" { + // last non-empty wins + globals.ConfigHome = old.HelmHome + } + if old.ChartHome != "" { + // last non-empty wins + globals.ChartHome = old.ChartHome + } + } + return charts, globals +} + +func makeHelmChartFromHca(old *HelmChartArgs) (c HelmChart) { + c.Name = old.ChartName + c.Version = old.ChartVersion + c.Repo = old.ChartRepoURL + c.ValuesFile = old.Values + c.ValuesInline = old.ValuesLocal + c.ValuesMerge = old.ValuesMerge + c.ReleaseName = old.ReleaseName + return +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/iampolicygenerator.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/iampolicygenerator.go new file mode 100644 index 000000000..f1d27ba7b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/iampolicygenerator.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +type Cloud string + +const GKE Cloud = "gke" + +// IAMPolicyGeneratorArgs contains arguments to generate a GKE service account resource. +type IAMPolicyGeneratorArgs struct { + // which cloud provider to generate for (e.g. "gke") + Cloud `json:"cloud" yaml:"cloud"` + + // information about the kubernetes cluster for this object + KubernetesService `json:"kubernetesService" yaml:"kubernetesService"` + + // information about the service account and project + ServiceAccount `json:"serviceAccount" yaml:"serviceAccount"` +} + +type KubernetesService struct { + // the name used for the Kubernetes service account + Name string `json:"name" yaml:"name"` + + // the name of the Kubernetes namespace for this object + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} + +type ServiceAccount struct { + // the name of the new cloud provider service account + Name string `json:"name" yaml:"name"` + + // The ID of the project + ProjectId string `json:"projectId" yaml:"projectId"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/image.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/image.go new file mode 100644 index 000000000..c7982338f --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/image.go @@ -0,0 +1,21 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// Image contains an image name, a new name, a new tag or digest, +// which will replace the original name and tag. +type Image struct { + // Name is a tag-less image name. + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // NewName is the value used to replace the original name. + NewName string `json:"newName,omitempty" yaml:"newName,omitempty"` + + // NewTag is the value used to replace the original tag. + NewTag string `json:"newTag,omitempty" yaml:"newTag,omitempty"` + + // Digest is the value used to replace the original image tag. + // If digest is present NewTag value is ignored. + Digest string `json:"digest,omitempty" yaml:"digest,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/inventory.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/inventory.go new file mode 100644 index 000000000..544deb5e5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/inventory.go @@ -0,0 +1,16 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// Inventory records all objects touched in a build operation. +type Inventory struct { + Type string `json:"type,omitempty" yaml:"type,omitempty"` + ConfigMap NameArgs `json:"configMap,omitempty" yaml:"configMap,omitempty"` +} + +// NameArgs holds both namespace and name. +type NameArgs struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kustomization.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kustomization.go new file mode 100644 index 000000000..a128c7749 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kustomization.go @@ -0,0 +1,274 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "bytes" + "encoding/json" + "fmt" + + "sigs.k8s.io/yaml" +) + +const ( + KustomizationVersion = "kustomize.config.k8s.io/v1beta1" + KustomizationKind = "Kustomization" + ComponentVersion = "kustomize.config.k8s.io/v1alpha1" + ComponentKind = "Component" + MetadataNamespacePath = "metadata/namespace" + + OriginAnnotations = "originAnnotations" + TransformerAnnotations = "transformerAnnotations" + ManagedByLabelOption = "managedByLabel" +) + +var BuildMetadataOptions = []string{OriginAnnotations, TransformerAnnotations, ManagedByLabelOption} + +// Kustomization holds the information needed to generate customized k8s api resources. +type Kustomization struct { + TypeMeta `json:",inline" yaml:",inline"` + + // MetaData is a pointer to avoid marshalling empty struct + MetaData *ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + + // OpenAPI contains information about what kubernetes schema to use. + OpenAPI map[string]string `json:"openapi,omitempty" yaml:"openapi,omitempty"` + + // + // Operators - what kustomize can do. + // + + // NamePrefix will prefix the names of all resources mentioned in the kustomization + // file including generated configmaps and secrets. + NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"` + + // NameSuffix will suffix the names of all resources mentioned in the kustomization + // file including generated configmaps and secrets. + NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"` + + // Namespace to add to all objects. + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + + // CommonLabels to add to all objects and selectors. + CommonLabels map[string]string `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"` + + // Labels to add to all objects but not selectors. + Labels []Label `json:"labels,omitempty" yaml:"labels,omitempty"` + + // CommonAnnotations to add to all objects. + CommonAnnotations map[string]string `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"` + + // PatchesStrategicMerge specifies the relative path to a file + // containing a strategic merge patch. Format documented at + // https://github.com/kubernetes/community/blob/master/contributors/devel/strategic-merge-patch.md + // URLs and globs are not supported. + PatchesStrategicMerge []PatchStrategicMerge `json:"patchesStrategicMerge,omitempty" yaml:"patchesStrategicMerge,omitempty"` + + // JSONPatches is a list of JSONPatch for applying JSON patch. + // Format documented at https://tools.ietf.org/html/rfc6902 + // and http://jsonpatch.com + PatchesJson6902 []Patch `json:"patchesJson6902,omitempty" yaml:"patchesJson6902,omitempty"` + + // Patches is a list of patches, where each one can be either a + // Strategic Merge Patch or a JSON patch. + // Each patch can be applied to multiple target objects. + Patches []Patch `json:"patches,omitempty" yaml:"patches,omitempty"` + + // Images is a list of (image name, new name, new tag or digest) + // for changing image names, tags or digests. This can also be achieved with a + // patch, but this operator is simpler to specify. + Images []Image `json:"images,omitempty" yaml:"images,omitempty"` + + // Replacements is a list of replacements, which will copy nodes from a + // specified source to N specified targets. + Replacements []ReplacementField `json:"replacements,omitempty" yaml:"replacements,omitempty"` + + // Replicas is a list of {resourcename, count} that allows for simpler replica + // specification. This can also be done with a patch. + Replicas []Replica `json:"replicas,omitempty" yaml:"replicas,omitempty"` + + // Vars allow things modified by kustomize to be injected into a + // kubernetes object specification. A var is a name (e.g. FOO) associated + // with a field in a specific resource instance. The field must + // contain a value of type string/bool/int/float, and defaults to the name field + // of the instance. Any appearance of "$(FOO)" in the object + // spec will be replaced at kustomize build time, after the final + // value of the specified field has been determined. + Vars []Var `json:"vars,omitempty" yaml:"vars,omitempty"` + + // + // Operands - what kustomize operates on. + // + + // Resources specifies relative paths to files holding YAML representations + // of kubernetes API objects, or specifications of other kustomizations + // via relative paths, absolute paths, or URLs. + Resources []string `json:"resources,omitempty" yaml:"resources,omitempty"` + + // Components specifies relative paths to specifications of other Components + // via relative paths, absolute paths, or URLs. + Components []string `json:"components,omitempty" yaml:"components,omitempty"` + + // Crds specifies relative paths to Custom Resource Definition files. + // This allows custom resources to be recognized as operands, making + // it possible to add them to the Resources list. + // CRDs themselves are not modified. + Crds []string `json:"crds,omitempty" yaml:"crds,omitempty"` + + // Deprecated. + // Anything that would have been specified here should + // be specified in the Resources field instead. + Bases []string `json:"bases,omitempty" yaml:"bases,omitempty"` + + // + // Generators (operators that create operands) + // + + // ConfigMapGenerator is a list of configmaps to generate from + // local data (one configMap per list item). + // The resulting resource is a normal operand, subject to + // name prefixing, patching, etc. By default, the name of + // the map will have a suffix hash generated from its contents. + ConfigMapGenerator []ConfigMapArgs `json:"configMapGenerator,omitempty" yaml:"configMapGenerator,omitempty"` + + // SecretGenerator is a list of secrets to generate from + // local data (one secret per list item). + // The resulting resource is a normal operand, subject to + // name prefixing, patching, etc. By default, the name of + // the map will have a suffix hash generated from its contents. + SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"` + + // HelmGlobals contains helm configuration that isn't chart specific. + HelmGlobals *HelmGlobals `json:"helmGlobals,omitempty" yaml:"helmGlobals,omitempty"` + + // HelmCharts is a list of helm chart configuration instances. + HelmCharts []HelmChart `json:"helmCharts,omitempty" yaml:"helmCharts,omitempty"` + + // HelmChartInflationGenerator is a list of helm chart configurations. + // Deprecated. Auto-converted to HelmGlobals and HelmCharts. + HelmChartInflationGenerator []HelmChartArgs `json:"helmChartInflationGenerator,omitempty" yaml:"helmChartInflationGenerator,omitempty"` + + // GeneratorOptions modify behavior of all ConfigMap and Secret generators. + GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"` + + // Configurations is a list of transformer configuration files + Configurations []string `json:"configurations,omitempty" yaml:"configurations,omitempty"` + + // Generators is a list of files containing custom generators + Generators []string `json:"generators,omitempty" yaml:"generators,omitempty"` + + // Transformers is a list of files containing transformers + Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"` + + // Validators is a list of files containing validators + Validators []string `json:"validators,omitempty" yaml:"validators,omitempty"` + + // Inventory appends an object that contains the record + // of all other objects, which can be used in apply, prune and delete + Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"` + + // BuildMetadata is a list of strings used to toggle different build options + BuildMetadata []string `json:"buildMetadata,omitempty" yaml:"buildMetadata,omitempty"` +} + +// FixKustomizationPostUnmarshalling fixes things +// like empty fields that should not be empty, or +// moving content of deprecated fields to newer +// fields. +func (k *Kustomization) FixKustomizationPostUnmarshalling() { + if k.Kind == "" { + k.Kind = KustomizationKind + } + if k.APIVersion == "" { + if k.Kind == ComponentKind { + k.APIVersion = ComponentVersion + } else { + k.APIVersion = KustomizationVersion + } + } + k.Resources = append(k.Resources, k.Bases...) + k.Bases = nil + for i, g := range k.ConfigMapGenerator { + if g.EnvSource != "" { + k.ConfigMapGenerator[i].EnvSources = + append(g.EnvSources, g.EnvSource) + k.ConfigMapGenerator[i].EnvSource = "" + } + } + for i, g := range k.SecretGenerator { + if g.EnvSource != "" { + k.SecretGenerator[i].EnvSources = + append(g.EnvSources, g.EnvSource) + k.SecretGenerator[i].EnvSource = "" + } + } + charts, globals := SplitHelmParameters(k.HelmChartInflationGenerator) + if k.HelmGlobals == nil { + if globals.ChartHome != "" || globals.ConfigHome != "" { + k.HelmGlobals = &globals + } + } + k.HelmCharts = append(k.HelmCharts, charts...) + // Wipe it for the fix command. + k.HelmChartInflationGenerator = nil +} + +// FixKustomizationPreMarshalling fixes things +// that should occur after the kustomization file +// has been processed. +func (k *Kustomization) FixKustomizationPreMarshalling() error { + // PatchesJson6902 should be under the Patches field. + k.Patches = append(k.Patches, k.PatchesJson6902...) + k.PatchesJson6902 = nil + + // this fix is not in FixKustomizationPostUnmarshalling because + // it will break some commands like `create` and `add`. those + // commands depend on 'commonLabels' field + if cl := labelFromCommonLabels(k.CommonLabels); cl != nil { + // check conflicts between commonLabels and labels + for _, l := range k.Labels { + for k := range l.Pairs { + if _, exist := cl.Pairs[k]; exist { + return fmt.Errorf("label name '%s' exists in both commonLabels and labels", k) + } + } + } + k.Labels = append(k.Labels, *cl) + k.CommonLabels = nil + } + + return nil +} + +func (k *Kustomization) EnforceFields() []string { + var errs []string + if k.Kind != "" && k.Kind != KustomizationKind && k.Kind != ComponentKind { + errs = append(errs, "kind should be "+KustomizationKind+" or "+ComponentKind) + } + requiredVersion := KustomizationVersion + if k.Kind == ComponentKind { + requiredVersion = ComponentVersion + } + if k.APIVersion != "" && k.APIVersion != requiredVersion { + errs = append(errs, "apiVersion for "+k.Kind+" should be "+requiredVersion) + } + return errs +} + +// Unmarshal replace k with the content in YAML input y +func (k *Kustomization) Unmarshal(y []byte) error { + j, err := yaml.YAMLToJSON(y) + if err != nil { + return err + } + dec := json.NewDecoder(bytes.NewReader(j)) + dec.DisallowUnknownFields() + var nk Kustomization + err = dec.Decode(&nk) + if err != nil { + return err + } + *k = nk + return nil +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kustomization_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kustomization_test.go new file mode 100644 index 000000000..74101b1ab --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kustomization_test.go @@ -0,0 +1,225 @@ +package types + +import ( + "reflect" + "testing" +) + +func fixKustomizationPostUnmarshallingCheck(k, e *Kustomization) bool { + return k.Kind == e.Kind && + k.APIVersion == e.APIVersion && + len(k.Resources) == len(e.Resources) && + k.Resources[0] == e.Resources[0] && + k.Bases == nil +} + +func TestFixKustomizationPostUnmarshalling(t *testing.T) { + var k Kustomization + k.Bases = append(k.Bases, "foo") + k.ConfigMapGenerator = []ConfigMapArgs{{GeneratorArgs{ + KvPairSources: KvPairSources{ + EnvSources: []string{"a", "b"}, + EnvSource: "c", + }, + }}} + k.CommonLabels = map[string]string{ + "foo": "bar", + } + k.FixKustomizationPostUnmarshalling() + + expected := Kustomization{ + TypeMeta: TypeMeta{ + Kind: KustomizationKind, + APIVersion: KustomizationVersion, + }, + Resources: []string{"foo"}, + ConfigMapGenerator: []ConfigMapArgs{{GeneratorArgs{ + KvPairSources: KvPairSources{ + EnvSources: []string{"a", "b", "c"}, + }, + }}}, + CommonLabels: map[string]string{ + "foo": "bar", + }, + } + if !reflect.DeepEqual(k, expected) { + t.Fatalf("unexpected output: %v", k) + } + if !fixKustomizationPostUnmarshallingCheck(&k, &expected) { + t.Fatalf("unexpected output: %v", k) + } +} + +func TestFixKustomizationPostUnmarshalling_2(t *testing.T) { + k := Kustomization{ + TypeMeta: TypeMeta{ + Kind: ComponentKind, + }, + } + k.Bases = append(k.Bases, "foo") + k.FixKustomizationPostUnmarshalling() + + expected := Kustomization{ + TypeMeta: TypeMeta{ + Kind: ComponentKind, + APIVersion: ComponentVersion, + }, + Resources: []string{"foo"}, + } + + if !fixKustomizationPostUnmarshallingCheck(&k, &expected) { + t.Fatalf("unexpected output: %v", k) + } +} + +func TestEnforceFields_InvalidKindAndVersion(t *testing.T) { + k := Kustomization{ + TypeMeta: TypeMeta{ + Kind: "foo", + APIVersion: "bar", + }, + } + + errs := k.EnforceFields() + if len(errs) != 2 { + t.Fatalf("number of errors should be 2 but got: %v", errs) + } +} + +func TestEnforceFields_InvalidKind(t *testing.T) { + k := Kustomization{ + TypeMeta: TypeMeta{ + Kind: "foo", + APIVersion: KustomizationVersion, + }, + } + + errs := k.EnforceFields() + if len(errs) != 1 { + t.Fatalf("number of errors should be 1 but got: %v", errs) + } + + expected := "kind should be " + KustomizationKind + " or " + ComponentKind + if errs[0] != expected { + t.Fatalf("error should be %v but got: %v", expected, errs[0]) + } +} + +func TestEnforceFields_InvalidVersion(t *testing.T) { + k := Kustomization{ + TypeMeta: TypeMeta{ + Kind: KustomizationKind, + APIVersion: "bar", + }, + } + + errs := k.EnforceFields() + if len(errs) != 1 { + t.Fatalf("number of errors should be 1 but got: %v", errs) + } + + expected := "apiVersion for " + k.Kind + " should be " + KustomizationVersion + if errs[0] != expected { + t.Fatalf("error should be %v but got: %v", expected, errs[0]) + } +} + +func TestEnforceFields_ComponentKind(t *testing.T) { + k := Kustomization{ + TypeMeta: TypeMeta{ + Kind: ComponentKind, + APIVersion: "bar", + }, + } + + errs := k.EnforceFields() + if len(errs) != 1 { + t.Fatalf("number of errors should be 1 but got: %v", errs) + } + + expected := "apiVersion for " + k.Kind + " should be " + ComponentVersion + if errs[0] != expected { + t.Fatalf("error should be %v but got: %v", expected, errs[0]) + } +} + +func TestEnforceFields(t *testing.T) { + k := Kustomization{ + TypeMeta: TypeMeta{ + Kind: KustomizationKind, + APIVersion: KustomizationVersion, + }, + } + + errs := k.EnforceFields() + if len(errs) != 0 { + t.Fatalf("number of errors should be 0 but got: %v", errs) + } +} + +func TestUnmarshal(t *testing.T) { + y := []byte(` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +metadata: + name: kust + namespace: default + labels: + foo: bar + annotations: + foo: bar +resources: +- foo +- bar +nameSuffix: dog +namePrefix: cat`) + var k Kustomization + err := k.Unmarshal(y) + if err != nil { + t.Fatal(err) + } + meta := ObjectMeta{ + Name: "kust", + Namespace: "default", + Labels: map[string]string{ + "foo": "bar", + }, + Annotations: map[string]string{ + "foo": "bar", + }, + } + if k.Kind != KustomizationKind || k.APIVersion != KustomizationVersion || + len(k.Resources) != 2 || k.NamePrefix != "cat" || k.NameSuffix != "dog" || + k.MetaData.Name != meta.Name || k.MetaData.Namespace != meta.Namespace || + k.MetaData.Labels["foo"] != meta.Labels["foo"] || k.MetaData.Annotations["foo"] != meta.Annotations["foo"] { + t.Fatalf("wrong unmarshal result: %v", k) + } +} + +func TestUnmarshal_UnkownField(t *testing.T) { + y := []byte(` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +unknown: foo`) + var k Kustomization + err := k.Unmarshal(y) + if err == nil { + t.Fatalf("expect an error") + } + expect := "json: unknown field \"unknown\"" + if err.Error() != expect { + t.Fatalf("expect %v but got: %v", expect, err.Error()) + } +} + +func TestUnmarshal_InvalidYaml(t *testing.T) { + y := []byte(` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +unknown`) + var k Kustomization + err := k.Unmarshal(y) + if err == nil { + t.Fatalf("expect an error") + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kvpairsources.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kvpairsources.go new file mode 100644 index 000000000..9898defad --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/kvpairsources.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// KvPairSources defines places to obtain key value pairs. +type KvPairSources struct { + // LiteralSources is a list of literal + // pair sources. Each literal source should + // be a key and literal value, e.g. `key=value` + LiteralSources []string `json:"literals,omitempty" yaml:"literals,omitempty"` + + // FileSources is a list of file "sources" to + // use in creating a list of key, value pairs. + // A source takes the form: [{key}=]{path} + // If the "key=" part is missing, the key is the + // path's basename. If they "key=" part is present, + // it becomes the key (replacing the basename). + // In either case, the value is the file contents. + // Specifying a directory will iterate each named + // file in the directory whose basename is a + // valid configmap key. + FileSources []string `json:"files,omitempty" yaml:"files,omitempty"` + + // EnvSources is a list of file paths. + // The contents of each file should be one + // key=value pair per line, e.g. a Docker + // or npm ".env" file or a ".ini" file + // (wikipedia.org/wiki/INI_file) + EnvSources []string `json:"envs,omitempty" yaml:"envs,omitempty"` + + // Older, singular form of EnvSources. + // On edits (e.g. `kustomize fix`) this is merged into the plural form + // for consistency with LiteralSources and FileSources. + EnvSource string `json:"env,omitempty" yaml:"env,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/labels.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/labels.go new file mode 100644 index 000000000..e2a2aee78 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/labels.go @@ -0,0 +1,25 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +type Label struct { + // Pairs contains the key-value pairs for labels to add + Pairs map[string]string `json:"pairs,omitempty" yaml:"pairs,omitempty"` + // IncludeSelectors inidicates should transformer include the + // fieldSpecs for selectors. Custom fieldSpecs specified by + // FieldSpecs will be merged with builtin fieldSpecs if this + // is true. + IncludeSelectors bool `json:"includeSelectors,omitempty" yaml:"includeSelectors,omitempty"` + FieldSpecs []FieldSpec `json:"fields,omitempty" yaml:"fields,omitempty"` +} + +func labelFromCommonLabels(commonLabels map[string]string) *Label { + if len(commonLabels) == 0 { + return nil + } + return &Label{ + Pairs: commonLabels, + IncludeSelectors: true, + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/loadrestrictions.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/loadrestrictions.go new file mode 100644 index 000000000..6617abdac --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/loadrestrictions.go @@ -0,0 +1,24 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// Restrictions on what things can be referred to +// in a kustomization file. +// +//go:generate stringer -type=LoadRestrictions +type LoadRestrictions int + +const ( + LoadRestrictionsUnknown LoadRestrictions = iota + + // Files referenced by a kustomization file must be in + // or under the directory holding the kustomization + // file itself. + LoadRestrictionsRootOnly + + // The kustomization file may specify absolute or + // relative paths to patch or resources files outside + // its own tree. + LoadRestrictionsNone +) diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/loadrestrictions_string.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/loadrestrictions_string.go new file mode 100644 index 000000000..d2355950b --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/loadrestrictions_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type=LoadRestrictions"; DO NOT EDIT. + +package types + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[LoadRestrictionsUnknown-0] + _ = x[LoadRestrictionsRootOnly-1] + _ = x[LoadRestrictionsNone-2] +} + +const _LoadRestrictions_name = "LoadRestrictionsUnknownLoadRestrictionsRootOnlyLoadRestrictionsNone" + +var _LoadRestrictions_index = [...]uint8{0, 23, 47, 67} + +func (i LoadRestrictions) String() string { + if i < 0 || i >= LoadRestrictions(len(_LoadRestrictions_index)-1) { + return "LoadRestrictions(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _LoadRestrictions_name[_LoadRestrictions_index[i]:_LoadRestrictions_index[i+1]] +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/objectmeta.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/objectmeta.go new file mode 100644 index 000000000..4f5d41f4a --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/objectmeta.go @@ -0,0 +1,13 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// ObjectMeta partially copies apimachinery/pkg/apis/meta/v1.ObjectMeta +// No need for a direct dependence; the fields are stable. +type ObjectMeta struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pair.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pair.go new file mode 100644 index 000000000..63cfb776e --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pair.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// Pair is a key value pair. +type Pair struct { + Key string + Value string +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patch.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patch.go new file mode 100644 index 000000000..5310a6e66 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patch.go @@ -0,0 +1,34 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import "reflect" + +// Patch represent either a Strategic Merge Patch or a JSON patch +// and its targets. +// The content of the patch can either be from a file +// or from an inline string. +type Patch struct { + // Path is a relative file path to the patch file. + Path string `json:"path,omitempty" yaml:"path,omitempty"` + + // Patch is the content of a patch. + Patch string `json:"patch,omitempty" yaml:"patch,omitempty"` + + // Target points to the resources that the patch is applied to + Target *Selector `json:"target,omitempty" yaml:"target,omitempty"` + + // Options is a list of options for the patch + Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"` +} + +// Equals return true if p equals o. +func (p *Patch) Equals(o Patch) bool { + targetEqual := (p.Target == o.Target) || + (p.Target != nil && o.Target != nil && *p.Target == *o.Target) + return p.Path == o.Path && + p.Patch == o.Patch && + targetEqual && + reflect.DeepEqual(p.Options, o.Options) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patch_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patch_test.go new file mode 100644 index 000000000..633ef559c --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patch_test.go @@ -0,0 +1,131 @@ +package types_test + +import ( + "testing" + + . "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestPatchEquals(t *testing.T) { + selector := Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + Name: "name", + Namespace: "namespace", + }, + LabelSelector: "selector", + AnnotationSelector: "selector", + } + type testcase struct { + patch1 Patch + patch2 Patch + expect bool + name string + } + testcases := []testcase{ + { + name: "empty patches", + patch1: Patch{}, + patch2: Patch{}, + expect: true, + }, + { + name: "full patches", + patch1: Patch{ + Path: "foo", + Patch: "bar", + Target: &Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + Name: "name", + Namespace: "namespace", + }, + LabelSelector: "selector", + AnnotationSelector: "selector", + }, + }, + patch2: Patch{ + Path: "foo", + Patch: "bar", + Target: &Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + Name: "name", + Namespace: "namespace", + }, + LabelSelector: "selector", + AnnotationSelector: "selector", + }, + }, + expect: true, + }, + { + name: "same target", + patch1: Patch{ + Path: "foo", + Patch: "bar", + Target: &selector, + }, + patch2: Patch{ + Path: "foo", + Patch: "bar", + Target: &selector, + }, + expect: true, + }, + { + name: "omit target", + patch1: Patch{ + Path: "foo", + Patch: "bar", + }, + patch2: Patch{ + Path: "foo", + Patch: "bar", + }, + expect: true, + }, + { + name: "one nil target", + patch1: Patch{ + Path: "foo", + Patch: "bar", + Target: &selector, + }, + patch2: Patch{ + Path: "foo", + Patch: "bar", + }, + expect: false, + }, + { + name: "different path", + patch1: Patch{ + Path: "foo", + }, + patch2: Patch{ + Path: "bar", + }, + expect: false, + }, + } + + for _, tc := range testcases { + if tc.expect != tc.patch1.Equals(tc.patch2) { + t.Fatalf("%s: unexpected result %v", tc.name, !tc.expect) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patchstrategicmerge.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patchstrategicmerge.go new file mode 100644 index 000000000..81a5ba456 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/patchstrategicmerge.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// PatchStrategicMerge represents a relative path to a +// stategic merge patch with the format +// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md +type PatchStrategicMerge string diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginconfig.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginconfig.go new file mode 100644 index 000000000..741e5debc --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginconfig.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +type HelmConfig struct { + Enabled bool + Command string +} + +// PluginConfig holds plugin configuration. +type PluginConfig struct { + // PluginRestrictions distinguishes plugin restrictions. + PluginRestrictions PluginRestrictions + + // BpLoadingOptions distinguishes builtin plugin behaviors. + BpLoadingOptions BuiltinPluginLoadingOptions + + // FnpLoadingOptions sets the way function-based plugin behaviors. + FnpLoadingOptions FnPluginLoadingOptions + + // HelmConfig contains metadata needed for allowing and running helm. + HelmConfig HelmConfig +} + +func EnabledPluginConfig(b BuiltinPluginLoadingOptions) (pc *PluginConfig) { + pc = MakePluginConfig(PluginRestrictionsNone, b) + pc.FnpLoadingOptions.EnableStar = true + pc.HelmConfig.Enabled = true + // If this command is not on PATH, tests needing it should skip. + pc.HelmConfig.Command = "helmV3" + return +} + +func DisabledPluginConfig() *PluginConfig { + return MakePluginConfig( + PluginRestrictionsBuiltinsOnly, + BploUseStaticallyLinked) +} + +func MakePluginConfig(pr PluginRestrictions, + b BuiltinPluginLoadingOptions) *PluginConfig { + return &PluginConfig{ + PluginRestrictions: pr, + BpLoadingOptions: b, + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginrestrictions.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginrestrictions.go new file mode 100644 index 000000000..88b03b3f5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginrestrictions.go @@ -0,0 +1,62 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// Some plugin classes +// - builtin: plugins defined in the kustomize repo. +// May be freely used and re-configured. +// - local: plugins that aren't builtin but are +// locally defined (presumably by the user), meaning +// the kustomization refers to them via a relative +// file path, not a URL. +// - remote: require a build-time download to obtain. +// Unadvised, unless one controls the +// serving site. +// +//go:generate stringer -type=PluginRestrictions +type PluginRestrictions int + +const ( + PluginRestrictionsUnknown PluginRestrictions = iota + + // Non-builtin plugins completely disabled. + PluginRestrictionsBuiltinsOnly + + // No restrictions, do whatever you want. + PluginRestrictionsNone +) + +// BuiltinPluginLoadingOptions distinguish ways in which builtin plugins are used. +//go:generate stringer -type=BuiltinPluginLoadingOptions +type BuiltinPluginLoadingOptions int + +const ( + BploUndefined BuiltinPluginLoadingOptions = iota + + // Desired in production use for performance. + BploUseStaticallyLinked + + // Desired in testing and development cycles where it's undesirable + // to generate static code. + BploLoadFromFileSys +) + +// FnPluginLoadingOptions set way functions-based plugins are restricted +type FnPluginLoadingOptions struct { + // Allow to run executables + EnableExec bool + // Allow to run starlark + EnableStar bool + // Allow container access to network + Network bool + NetworkName string + // list of mounts + Mounts []string + // list of env variables to pass to fn + Env []string + // Run as uid and gid of the command executor + AsCurrentUser bool + // Run in this working directory + WorkingDir string +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginrestrictions_string.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginrestrictions_string.go new file mode 100644 index 000000000..b9dba7dfc --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/pluginrestrictions_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type=PluginRestrictions"; DO NOT EDIT. + +package types + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[PluginRestrictionsUnknown-0] + _ = x[PluginRestrictionsBuiltinsOnly-1] + _ = x[PluginRestrictionsNone-2] +} + +const _PluginRestrictions_name = "PluginRestrictionsUnknownPluginRestrictionsBuiltinsOnlyPluginRestrictionsNone" + +var _PluginRestrictions_index = [...]uint8{0, 25, 55, 77} + +func (i PluginRestrictions) String() string { + if i < 0 || i >= PluginRestrictions(len(_PluginRestrictions_index)-1) { + return "PluginRestrictions(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _PluginRestrictions_name[_PluginRestrictions_index[i]:_PluginRestrictions_index[i+1]] +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replacement.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replacement.go new file mode 100644 index 000000000..cb4163429 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replacement.go @@ -0,0 +1,87 @@ +// Copyright 2021 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "strings" + + "sigs.k8s.io/kustomize/kyaml/resid" +) + +const DefaultReplacementFieldPath = "metadata.name" + +// Replacement defines how to perform a substitution +// where it is from and where it is to. +type Replacement struct { + // The source of the value. + Source *SourceSelector `json:"source,omitempty" yaml:"source,omitempty"` + + // The N fields to write the value to. + Targets []*TargetSelector `json:"targets,omitempty" yaml:"targets,omitempty"` +} + +// SourceSelector is the source of the replacement transformer. +type SourceSelector struct { + // A specific object to read it from. + resid.ResId `json:",inline,omitempty" yaml:",inline,omitempty"` + + // Structured field path expected in the allowed object. + FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"` + + // Used to refine the interpretation of the field. + Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"` +} + +func (s *SourceSelector) String() string { + if s == nil { + return "" + } + result := []string{s.ResId.String()} + if s.FieldPath != "" { + result = append(result, s.FieldPath) + } + if opts := s.Options.String(); opts != "" { + result = append(result, opts) + } + return strings.Join(result, ":") +} + +// TargetSelector specifies fields in one or more objects. +type TargetSelector struct { + // Include objects that match this. + Select *Selector `json:"select" yaml:"select"` + + // From the allowed set, remove objects that match this. + Reject []*Selector `json:"reject,omitempty" yaml:"reject,omitempty"` + + // Structured field paths expected in each allowed object. + FieldPaths []string `json:"fieldPaths,omitempty" yaml:"fieldPaths,omitempty"` + + // Used to refine the interpretation of the field. + Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"` +} + +// FieldOptions refine the interpretation of FieldPaths. +type FieldOptions struct { + // Used to split/join the field. + Delimiter string `json:"delimiter,omitempty" yaml:"delimiter,omitempty"` + + // Which position in the split to consider. + Index int `json:"index,omitempty" yaml:"index,omitempty"` + + // TODO (#3492): Implement use of this option + // None, Base64, URL, Hex, etc + Encoding string `json:"encoding,omitempty" yaml:"encoding,omitempty"` + + // If field missing, add it. + Create bool `json:"create,omitempty" yaml:"create,omitempty"` +} + +func (fo *FieldOptions) String() string { + if fo == nil || (fo.Delimiter == "" && !fo.Create) { + return "" + } + return fmt.Sprintf("%s(%d), create=%t", fo.Delimiter, fo.Index, fo.Create) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replacementfield.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replacementfield.go new file mode 100644 index 000000000..a5684f848 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replacementfield.go @@ -0,0 +1,6 @@ +package types + +type ReplacementField struct { + Replacement `json:",inline,omitempty" yaml:",inline,omitempty"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replica.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replica.go new file mode 100644 index 000000000..8267366b5 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/replica.go @@ -0,0 +1,16 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// Replica specifies a modification to a replica config. +// The number of replicas of a resource whose name matches will be set to count. +// This struct is used by the ReplicaCountTransform, and is meant to supplement +// the existing patch functionality with a simpler syntax for replica configuration. +type Replica struct { + // The name of the resource to change the replica count + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // The number of replicas required. + Count int64 `json:"count" yaml:"count"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/secretargs.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/secretargs.go new file mode 100644 index 000000000..62dbe26a7 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/secretargs.go @@ -0,0 +1,19 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// SecretArgs contains the metadata of how to generate a secret. +type SecretArgs struct { + // GeneratorArgs for the secret. + GeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"` + + // Type of the secret. + // + // This is the same field as the secret type field in v1/Secret: + // It can be "Opaque" (default), or "kubernetes.io/tls". + // + // If type is "kubernetes.io/tls", then "literals" or "files" must have exactly two + // keys: "tls.key" and "tls.crt" + Type string `json:"type,omitempty" yaml:"type,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/selector.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/selector.go new file mode 100644 index 000000000..2c07f0b01 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/selector.go @@ -0,0 +1,124 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "regexp" + + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// Selector specifies a set of resources. +// Any resource that matches intersection of all conditions +// is included in this set. +type Selector struct { + // ResId refers to a GVKN/Ns of a resource. + resid.ResId `json:",inline,omitempty" yaml:",inline,omitempty"` + + // AnnotationSelector is a string that follows the label selection expression + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + // It matches with the resource annotations. + AnnotationSelector string `json:"annotationSelector,omitempty" yaml:"annotationSelector,omitempty"` + + // LabelSelector is a string that follows the label selection expression + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + // It matches with the resource labels. + LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"` +} + +func (s *Selector) Copy() Selector { + return *s +} + +func (s *Selector) String() string { + return fmt.Sprintf( + "%s:a=%s:l=%s", s.ResId, s.AnnotationSelector, s.LabelSelector) +} + +// SelectorRegex is a Selector with regex in GVK +// Any resource that matches intersection of all conditions +// is included in this set. +type SelectorRegex struct { + selector *Selector + groupRegex *regexp.Regexp + versionRegex *regexp.Regexp + kindRegex *regexp.Regexp + nameRegex *regexp.Regexp + namespaceRegex *regexp.Regexp +} + +// NewSelectorRegex returns a pointer to a new SelectorRegex +// which uses the same condition as s. +func NewSelectorRegex(s *Selector) (*SelectorRegex, error) { + sr := new(SelectorRegex) + var err error + sr.selector = s + sr.groupRegex, err = regexp.Compile(anchorRegex(s.Gvk.Group)) + if err != nil { + return nil, err + } + sr.versionRegex, err = regexp.Compile(anchorRegex(s.Gvk.Version)) + if err != nil { + return nil, err + } + sr.kindRegex, err = regexp.Compile(anchorRegex(s.Gvk.Kind)) + if err != nil { + return nil, err + } + sr.nameRegex, err = regexp.Compile(anchorRegex(s.Name)) + if err != nil { + return nil, err + } + sr.namespaceRegex, err = regexp.Compile(anchorRegex(s.Namespace)) + if err != nil { + return nil, err + } + return sr, nil +} + +func anchorRegex(pattern string) string { + if pattern == "" { + return pattern + } + return "^(?:" + pattern + ")$" +} + +// MatchGvk return true if gvk can be matched by s. +func (s *SelectorRegex) MatchGvk(gvk resid.Gvk) bool { + if len(s.selector.Gvk.Group) > 0 { + if !s.groupRegex.MatchString(gvk.Group) { + return false + } + } + if len(s.selector.Gvk.Version) > 0 { + if !s.versionRegex.MatchString(gvk.Version) { + return false + } + } + if len(s.selector.Gvk.Kind) > 0 { + if !s.kindRegex.MatchString(gvk.Kind) { + return false + } + } + return true +} + +// MatchName returns true if the name in selector is +// empty or the n can be matches by the name in selector +func (s *SelectorRegex) MatchName(n string) bool { + if s.selector.Name == "" { + return true + } + return s.nameRegex.MatchString(n) +} + +// MatchNamespace returns true if the namespace in selector is +// empty or the ns can be matches by the namespace in selector +func (s *SelectorRegex) MatchNamespace(ns string) bool { + if s.selector.Namespace == "" { + return true + } + return s.namespaceRegex.MatchString(ns) +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/selector_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/selector_test.go new file mode 100644 index 000000000..96ad99e89 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/selector_test.go @@ -0,0 +1,244 @@ +package types_test + +import ( + "testing" + + . "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestSelectorRegexMatchGvk(t *testing.T) { + testcases := []struct { + S Selector + G resid.Gvk + Expected bool + }{ + { + S: Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + }, + }, + G: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "", + Kind: "", + }, + }, + }, + G: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + }, + }, + G: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "", + }, + Expected: false, + }, + { + S: Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind", + }, + }, + }, + G: resid.Gvk{ + Group: "group", + Version: "version", + Kind: "kind2", + }, + Expected: false, + }, + { + S: Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "g.*", + Version: "\\d+", + Kind: ".{4}", + }, + }, + }, + G: resid.Gvk{ + Group: "group", + Version: "123", + Kind: "abcd", + }, + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Gvk: resid.Gvk{ + Group: "g.*", + Version: "\\d+", + Kind: ".{4}", + }, + }, + }, + G: resid.Gvk{ + Group: "group", + Version: "123", + Kind: "abc", + }, + Expected: false, + }, + } + + for _, tc := range testcases { + sr, err := NewSelectorRegex(&tc.S) + if err != nil { + t.Fatal(err) + } + if sr.MatchGvk(tc.G) != tc.Expected { + t.Fatalf("unexpected result for selector gvk %s and gvk %s", + tc.S.Gvk.String(), tc.G.String()) + } + } +} + +func TestSelectorRegexMatchName(t *testing.T) { + testcases := []struct { + S Selector + Name string + Expected bool + }{ + { + S: Selector{ + ResId: resid.ResId{ + Name: "foo", + Namespace: "bar", + }, + }, + Name: "foo", + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Name: "foo", + Namespace: "bar", + }, + }, + Name: "bar", + Expected: false, + }, + { + S: Selector{ + ResId: resid.ResId{ + Name: "f.*", + }, + }, + Name: "foo", + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Name: "b.*", + }, + }, + Name: "foo", + Expected: false, + }, + } + for _, tc := range testcases { + sr, err := NewSelectorRegex(&tc.S) + if err != nil { + t.Fatal(err) + } + if sr.MatchName(tc.Name) != tc.Expected { + t.Fatalf("unexpected result for selector name %s and name %s", + tc.S.Name, tc.Name) + } + } +} + +func TestSelectorRegexMatchNamespace(t *testing.T) { + testcases := []struct { + S Selector + Namespace string + Expected bool + }{ + { + S: Selector{ + ResId: resid.ResId{ + Name: "bar", + Namespace: "foo", + }, + }, + Namespace: "foo", + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Name: "foo", + Namespace: "bar", + }, + }, + Namespace: "foo", + Expected: false, + }, + { + S: Selector{ + ResId: resid.ResId{ + Namespace: "f.*", + }, + }, + Namespace: "foo", + Expected: true, + }, + { + S: Selector{ + ResId: resid.ResId{ + Namespace: "b.*", + }, + }, + Namespace: "foo", + Expected: false, + }, + } + for _, tc := range testcases { + sr, err := NewSelectorRegex(&tc.S) + if err != nil { + t.Fatal(err) + } + if sr.MatchNamespace(tc.Namespace) != tc.Expected { + t.Fatalf("unexpected result for selector namespace %s and namespace %s", + tc.S.Namespace, tc.Namespace) + } + } +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/typemeta.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/typemeta.go new file mode 100644 index 000000000..0ddafd3d8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/typemeta.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta +// No need for a direct dependence; the fields are stable. +type TypeMeta struct { + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` +} diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/var.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/var.go new file mode 100644 index 000000000..0ca5579c0 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/var.go @@ -0,0 +1,211 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "reflect" + "sort" + "strings" + + "sigs.k8s.io/kustomize/kyaml/resid" +) + +// Var represents a variable whose value will be sourced +// from a field in a Kubernetes object. +type Var struct { + // Value of identifier name e.g. FOO used in container args, annotations + // Appears in pod template as $(FOO) + Name string `json:"name" yaml:"name"` + + // ObjRef must refer to a Kubernetes resource under the + // purview of this kustomization. ObjRef should use the + // raw name of the object (the name specified in its YAML, + // before addition of a namePrefix and a nameSuffix). + ObjRef Target `json:"objref" yaml:"objref"` + + // FieldRef refers to the field of the object referred to by + // ObjRef whose value will be extracted for use in + // replacing $(FOO). + // If unspecified, this defaults to fieldPath: $defaultFieldPath + FieldRef FieldSelector `json:"fieldref,omitempty" yaml:"fieldref,omitempty"` +} + +// Target refers to a kubernetes object by Group, Version, Kind and Name +// gvk.Gvk contains Group, Version and Kind +// APIVersion is added to keep the backward compatibility of using ObjectReference +// for Var.ObjRef +type Target struct { + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` + Name string `json:"name" yaml:"name"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} + +// GVK returns the Gvk object in Target +func (t *Target) GVK() resid.Gvk { + if t.APIVersion == "" { + return t.Gvk + } + versions := strings.Split(t.APIVersion, "/") + if len(versions) == 2 { + t.Group = versions[0] + t.Version = versions[1] + } + if len(versions) == 1 { + t.Version = versions[0] + } + return t.Gvk +} + +// FieldSelector contains the fieldPath to an object field. +// This struct is added to keep the backward compatibility of using ObjectFieldSelector +// for Var.FieldRef +type FieldSelector struct { + FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"` +} + +// defaulting sets reference to field used by default. +func (v *Var) Defaulting() { + if v.FieldRef.FieldPath == "" { + v.FieldRef.FieldPath = DefaultReplacementFieldPath + } + v.ObjRef.GVK() +} + +// DeepEqual returns true if var a and b are Equals. +// Note 1: The objects are unchanged by the VarEqual +// Note 2: Should be normalize be FieldPath before doing +// the DeepEqual. spec.a[b] is supposed to be the same +// as spec.a.b +func (v Var) DeepEqual(other Var) bool { + v.Defaulting() + other.Defaulting() + return reflect.DeepEqual(v, other) +} + +// VarSet is a set of Vars where no var.Name is repeated. +type VarSet struct { + set map[string]Var +} + +// NewVarSet returns an initialized VarSet +func NewVarSet() VarSet { + return VarSet{set: map[string]Var{}} +} + +// AsSlice returns the vars as a slice. +func (vs *VarSet) AsSlice() []Var { + s := make([]Var, len(vs.set)) + i := 0 + for _, v := range vs.set { + s[i] = v + i++ + } + sort.Sort(byName(s)) + return s +} + +// Copy returns a copy of the var set. +func (vs *VarSet) Copy() VarSet { + newSet := make(map[string]Var, len(vs.set)) + for k, v := range vs.set { + newSet[k] = v + } + return VarSet{set: newSet} +} + +// MergeSet absorbs other vars with error on name collision. +func (vs *VarSet) MergeSet(incoming VarSet) error { + for _, incomingVar := range incoming.set { + if err := vs.Merge(incomingVar); err != nil { + return err + } + } + return nil +} + +// MergeSlice absorbs a Var slice with error on name collision. +// Empty fields in incoming vars are defaulted. +func (vs *VarSet) MergeSlice(incoming []Var) error { + for _, v := range incoming { + if err := vs.Merge(v); err != nil { + return err + } + } + return nil +} + +// Merge absorbs another Var with error on name collision. +// Empty fields in incoming Var is defaulted. +func (vs *VarSet) Merge(v Var) error { + if vs.Contains(v) { + return fmt.Errorf( + "var '%s' already encountered", v.Name) + } + v.Defaulting() + vs.set[v.Name] = v + return nil +} + +// AbsorbSet absorbs other vars with error on (name,value) collision. +func (vs *VarSet) AbsorbSet(incoming VarSet) error { + for _, v := range incoming.set { + if err := vs.Absorb(v); err != nil { + return err + } + } + return nil +} + +// AbsorbSlice absorbs a Var slice with error on (name,value) collision. +// Empty fields in incoming vars are defaulted. +func (vs *VarSet) AbsorbSlice(incoming []Var) error { + for _, v := range incoming { + if err := vs.Absorb(v); err != nil { + return err + } + } + return nil +} + +// Absorb absorbs another Var with error on (name,value) collision. +// Empty fields in incoming Var is defaulted. +func (vs *VarSet) Absorb(v Var) error { + conflicting := vs.Get(v.Name) + if conflicting == nil { + // no conflict. The var is valid. + v.Defaulting() + vs.set[v.Name] = v + return nil + } + + if !reflect.DeepEqual(v, *conflicting) { + // two vars with the same name are pointing at two + // different resources. + return fmt.Errorf( + "var '%s' already encountered", v.Name) + } + return nil +} + +// Contains is true if the set has the other var. +func (vs *VarSet) Contains(other Var) bool { + return vs.Get(other.Name) != nil +} + +// Get returns the var with the given name, else nil. +func (vs *VarSet) Get(name string) *Var { + if v, found := vs.set[name]; found { + return &v + } + return nil +} + +// byName is a sort interface which sorts Vars by name alphabetically +type byName []Var + +func (v byName) Len() int { return len(v) } +func (v byName) Swap(i, j int) { v[i], v[j] = v[j], v[i] } +func (v byName) Less(i, j int) bool { return v[i].Name < v[j].Name } diff --git a/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/var_test.go b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/var_test.go new file mode 100644 index 000000000..2ef14b2b8 --- /dev/null +++ b/functions/go/gcp-set-project-id/thirdparty/kustomize/api/types/var_test.go @@ -0,0 +1,171 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "reflect" + "strings" + "testing" + + "gopkg.in/yaml.v2" + "sigs.k8s.io/kustomize/kyaml/resid" +) + +func TestGVK(t *testing.T) { + type testcase struct { + data string + expected resid.Gvk + } + + testcases := []testcase{ + { + data: ` +apiVersion: v1 +kind: Secret +name: my-secret +`, + expected: resid.Gvk{Group: "", Version: "v1", Kind: "Secret"}, + }, + { + data: ` +apiVersion: myapps/v1 +kind: MyKind +name: my-kind +`, + expected: resid.Gvk{Group: "myapps", Version: "v1", Kind: "MyKind"}, + }, + { + data: ` +version: v2 +kind: MyKind +name: my-kind +`, + expected: resid.Gvk{Version: "v2", Kind: "MyKind"}, + }, + } + + for _, tc := range testcases { + var targ Target + err := yaml.Unmarshal([]byte(tc.data), &targ) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if !reflect.DeepEqual(targ.GVK(), tc.expected) { + t.Fatalf("Expected %v, but got %v", tc.expected, targ.GVK()) + } + } +} + +func TestDefaulting(t *testing.T) { + v := &Var{ + Name: "SOME_VARIABLE_NAME", + ObjRef: Target{ + Gvk: resid.Gvk{ + Version: "v1", + Kind: "Secret", + }, + Name: "my-secret", + }, + } + v.Defaulting() + if v.FieldRef.FieldPath != DefaultReplacementFieldPath { + t.Fatalf("expected %s, got %v", + DefaultReplacementFieldPath, v.FieldRef.FieldPath) + } +} + +func TestVarSet(t *testing.T) { + set := NewVarSet() + vars := []Var{ + { + Name: "SHELLVARS", + ObjRef: Target{ + APIVersion: "v7", + Gvk: resid.Gvk{Kind: "ConfigMap"}, + Name: "bash"}, + }, + { + Name: "BACKEND", + ObjRef: Target{ + APIVersion: "v7", + Gvk: resid.Gvk{Kind: "Deployment"}, + Name: "myTiredBackend"}, + }, + { + Name: "AWARD", + ObjRef: Target{ + APIVersion: "v7", + Gvk: resid.Gvk{Kind: "Service"}, + Name: "nobelPrize"}, + FieldRef: FieldSelector{FieldPath: "some.arbitrary.path"}, + }, + } + err := set.MergeSlice(vars) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + for _, v := range vars { + if !set.Contains(v) { + t.Fatalf("set %v should contain var %v", set.AsSlice(), v) + } + } + set2 := NewVarSet() + err = set2.MergeSet(set) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + err = set2.MergeSlice(vars) + if err == nil { + t.Fatalf("expected err") + } + if !strings.Contains(err.Error(), "var 'SHELLVARS' already encountered") { + t.Fatalf("unexpected err: %v", err) + } + v := set2.Get("BACKEND") + if v == nil { + t.Fatalf("expected var") + } + // Confirm defaulting. + if v.FieldRef.FieldPath != DefaultReplacementFieldPath { + t.Fatalf("unexpected field path: %v", v.FieldRef.FieldPath) + } + // Confirm sorting. + names := set2.AsSlice() + if names[0].Name != "AWARD" || + names[1].Name != "BACKEND" || + names[2].Name != "SHELLVARS" { + t.Fatalf("unexpected order in : %v", names) + } +} + +func TestVarSetCopy(t *testing.T) { + set1 := NewVarSet() + vars := []Var{ + {Name: "First"}, + {Name: "Second"}, + {Name: "Third"}, + } + err := set1.MergeSlice(vars) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + // Confirm copying + set2 := set1.Copy() + for _, varInSet1 := range set1.AsSlice() { + if v := set2.Get(varInSet1.Name); v == nil { + t.Fatalf("set %v should contain a Var named %s", set2.AsSlice(), varInSet1) + } else if !set2.Contains(*v) { + t.Fatalf("set %v should contain %v", set2.AsSlice(), v) + } + } + // Confirm that the copy is deep + w := Var{Name: "Only in set2"} + set2.Merge(w) + if !set2.Contains(w) { + t.Fatalf("set %v should contain %v", set2.AsSlice(), w) + } + if set1.Contains(w) { + t.Fatalf("set %v should not contain %v", set1.AsSlice(), w) + } +} diff --git a/functions/go/gcp-set-project-id/transformer.go b/functions/go/gcp-set-project-id/transformer.go new file mode 100644 index 000000000..bd9db6e1f --- /dev/null +++ b/functions/go/gcp-set-project-id/transformer.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/gcp-set-project-id/consts" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/gcp-set-project-id/plugins" + "sigs.k8s.io/kustomize/api/resmap" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +const ( + ProjectAnnotationKey = "cnrm.cloud.google.com/project-id" +) + +type ProjectIDTransformer struct { + annotation plugins.AnnotationPlugin + custom plugins.CustomFieldSpecPlugin +} + +func (p *ProjectIDTransformer) Config(fnConfigNode *yaml.RNode) error { + // Get ProjectID + data := fnConfigNode.GetDataMap() + if data == nil { + return fmt.Errorf("missing `data` field in `ConfigMap` FunctionConfig") + } + projectID, ok := data[projectIDKey] + if !ok { + return fmt.Errorf("missing `.data.%s` field in `ConfigMap` FunctionConfig", projectIDKey) + } + + // Set transformer to update custom field spec paths + if err := p.custom.Config([]byte(consts.ProjectFieldSpecs)); err != nil { + return err + } + p.custom.ProjectID = projectID + + // Set transformer to update annotation paths (metadata and inner resources' metadata). + if err := p.annotation.Config(nil, []byte(consts.AnnotationFieldSpecs)); err != nil { + return err + } + p.annotation.Annotations = map[string]string{ProjectAnnotationKey: projectID} + return nil +} + +func (p *ProjectIDTransformer) Transform(m resmap.ResMap) error { + if err := p.annotation.Transform(m); err != nil { + return fmt.Errorf("set projectID to annotations fail %v", err) + } + if err := p.custom.Transform(m); err != nil { + return fmt.Errorf("set projectID to custom fieldSpec fail %v", err) + } + return nil +} diff --git a/functions/go/set-project-id/README.md b/functions/go/set-project-id/README.md deleted file mode 100644 index 0f6b1c00f..000000000 --- a/functions/go/set-project-id/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# set-project-id - -## Overview - - - -The `set-project-id` function sets 'project-id' -[setter](https://catalog.kpt.dev/apply-setters/v0.2/?id=definitions) and -`cnrm.cloud.google.com/project-id` annotation to the provided project ID, only -if they are not already set. - - - - - -## Usage - -set-project-id function is expected to be executed imperatively like: - -```shell -kpt fn eval --include-meta-resources --image gcr.io/kpt-fn/set-project-id:unstable -- 'project-id=foo' -``` - -The `set-project-id` function does the following: - -1. Sets the the 'project-id' setter to the provided project ID. - * If an 'apply-setters' function is present in the Kptfile which does not - have the 'project-id' setter or sets it to an empty value, update that - config to set the “project-id” setter to the provided project ID value. - * If no 'apply-setters' function is present in the Kptfile, add one with - just the 'project-id' setter set to the provided project ID value. - * If no pipeline is declared in the Kptfile, declare it and add - 'apply-setters' function as mutator with the 'project-id' setter set to - the provided project ID value. -2. For all - [Config Connector resources](https://cloud.google.com/config-connector/docs/reference/overview), - check if they have the `cnrm.cloud.google.com/project-id` annotation set. If - the annotation is not present or is set to an empty value, set it to the - provided project ID value. - -### FunctionConfig - -This function supports `ConfigMap` `functionConfig` and expects the 'project-id' -value to be present in the map. - - - -## Examples - - - -Setting the `project-id` setter on the package without setters. - -Let's start with the Kptfile of an example package. - -```yaml -apiVersion: kpt.dev/v1 -kind: Kptfile -metadata: - name: example-package -``` - -Invoke the function: - -```shell -kpt fn eval --include-meta-resources --image gcr.io/kpt-fn/set-project-id:unstable -- 'project-id=foo' -``` - -Kptfile will be updated to the following: - -```yaml -apiVersion: kpt.dev/v1 -kind: Kptfile -metadata: - name: example-package -pipeline: - mutators: - - image: gcr.io/kpt-fn/apply-setters:v0.2 - configMap: - project-id: foo -``` - - diff --git a/functions/go/set-project-id/go.sum b/functions/go/set-project-id/go.sum deleted file mode 100644 index c5d02d7dd..000000000 --- a/functions/go/set-project-id/go.sum +++ /dev/null @@ -1,559 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleContainerTools/kpt-functions-sdk/go v0.0.0-20210810181223-632b30549de6 h1:oo4q344mHs4eg8puEsXdyikhwORcu2cLsKGsNFDqxqM= -github.com/GoogleContainerTools/kpt-functions-sdk/go v0.0.0-20210810181223-632b30549de6/go.mod h1:k86q33ABlA9TnUqRmHH9dnKY2Edh8YbxjRyPfjlM8jE= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/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/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/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/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v0.0.0-20181025225059-d3de96c4c28e/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gorilla/mux v0.0.0-20181024020800-521ea7b17d02/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/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/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= -github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.3.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/open-policy-agent/frameworks/constraint v0.0.0-20200420221412-5bae2037a343/go.mod h1:Dr3QxvH+NTQcPPZWSt1ueNOsxW4VwgUltaLL7Ttnrac= -github.com/open-policy-agent/gatekeeper v3.0.4-beta.2+incompatible/go.mod h1:gWd63apzboCsahWE6btvIzIcXcNxT+lJvUAaluqVu/E= -github.com/open-policy-agent/opa v0.19.1/go.mod h1:rrwxoT/b011T0cyj+gg2VvxqTtn6N3gp/jzmr3fjW44= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20181025174421-f30f42803563/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.0-20181021141114-fe5e611709b0/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v0.0.0-20181024212040-082b515c9490/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -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 v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= -github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= -go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/lint v0.0.0-20181023182221-1baf3a9d7d67/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= -k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= -k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= -k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY= -k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= -k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= -k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= -k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg= -k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= -k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= -k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= -k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= -k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE= -k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= -k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA= -k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= -sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA= -sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/functions/go/set-project-id/main.go b/functions/go/set-project-id/main.go deleted file mode 100644 index 04d5c71ce..000000000 --- a/functions/go/set-project-id/main.go +++ /dev/null @@ -1,182 +0,0 @@ -package main - -import ( - "fmt" - "os" - "regexp" - "strings" - - "sigs.k8s.io/kustomize/kyaml/fn/framework" - "sigs.k8s.io/kustomize/kyaml/fn/framework/command" - "sigs.k8s.io/kustomize/kyaml/kio/kioutil" - "sigs.k8s.io/kustomize/kyaml/yaml" - - kptfilev1 "github.com/GoogleContainerTools/kpt-functions-sdk/go/pkg/api/kptfile/v1" - kptutil "github.com/GoogleContainerTools/kpt-functions-sdk/go/pkg/api/util" -) - -var ( - kccAPIVersionRegex = regexp.MustCompile(`^([\w]+)\.cnrm\.cloud\.google\.com\/[\w]+$`) -) - -const ( - projectIDSetterName = "project-id" - projectIDAnnotation = "cnrm.cloud.google.com/project-id" -) - -func findSetterNode(nodes []*yaml.RNode, path string) (*yaml.RNode, error) { - for _, node := range nodes { - np := node.GetAnnotations()[kioutil.PathAnnotation] - if np == path { - return node, nil - } - } - return nil, fmt.Errorf(`file %s doesn't exist, please ensure the file specified in "configPath" exists and retry`, path) -} - -func findKptfiles(nodes []*yaml.RNode) ([]*kptfilev1.KptFile, error) { - kptfiles := []*kptfilev1.KptFile{} - for _, node := range nodes { - if node.GetKind() == kptfilev1.KptFileKind { - s, err := node.String() - if err != nil { - return nil, fmt.Errorf("unable to read Kptfile: %w", err) - } - kf, err := kptutil.DecodeKptfile(s) - if err != nil { - return nil, fmt.Errorf("failed to read Kptfile: %w", err) - } - kptfiles = append(kptfiles, kf) - } - } - if len(kptfiles) == 0 { - return nil, fmt.Errorf("unable to find Kptfile, please include --include-meta-resources flag if a Kptfile is present") - } - return kptfiles, nil -} - -func setKptfile(nodes []*yaml.RNode, kf *kptfilev1.KptFile) error { - b, err := yaml.Marshal(kf) - if err != nil { - return fmt.Errorf("failed to marshal updated Kptfile: %w", err) - } - kNode, err := yaml.Parse(string(b)) - if err != nil { - return fmt.Errorf("failed to parse updated Kptfile: %w", err) - } - - for i := range nodes { - if nodes[i].GetAnnotations()[kioutil.PathAnnotation] == kf.Annotations[kioutil.PathAnnotation] { - nodes[i] = kNode - return nil - } - } - return nil - -} - -func setSettersOnKptfile(nodes []*yaml.RNode, kf *kptfilev1.KptFile, projectID string) error { - if kf.Pipeline == nil { - kf.Pipeline = &kptfilev1.Pipeline{} - } - for _, fn := range kf.Pipeline.Mutators { - if !strings.Contains(fn.Image, "apply-setters") { - continue - } - if fn.ConfigMap != nil { - if fn.ConfigMap[projectIDSetterName] == "" { - fn.ConfigMap[projectIDSetterName] = projectID - } - if err := setKptfile(nodes, kf); err != nil { - return fmt.Errorf("failed to update Kptfile file: %w", err) - } - return nil - } else if fn.ConfigPath != "" { - settersConfig, err := findSetterNode(nodes, fn.ConfigPath) - if err != nil { - return fmt.Errorf("failed to find setter file: %w", err) - } - dataMap := settersConfig.GetDataMap() - if dataMap[projectIDSetterName] == "" { - dataMap[projectIDSetterName] = projectID - settersConfig.SetDataMap(dataMap) - } - return nil - } else { - return fmt.Errorf("unable to find `ConfigMap` or `configPath` as the `functionConfig` for apply-setters") - } - } - - fn := kptfilev1.Function{ - Image: "gcr.io/kpt-fn/apply-setters:v0.2", - ConfigMap: map[string]string{ - projectIDSetterName: projectID, - }, - } - kf.Pipeline.Mutators = append(kf.Pipeline.Mutators, fn) - if err := setKptfile(nodes, kf); err != nil { - return fmt.Errorf("failed to update Kptfile file: %w", err) - } - - return nil -} - -func setSetters(nodes []*yaml.RNode, projectID string) error { - kptfiles, err := findKptfiles(nodes) - if err != nil { - return fmt.Errorf("failed to find Kptfile: %v", err) - } - - for _, kf := range kptfiles { - if err := setSettersOnKptfile(nodes, kf, projectID); err != nil { - return fmt.Errorf("error updating Kptfile: %w", err) - } - } - - return nil -} - -func setProjectIDAnnotation(nodes []*yaml.RNode, projectID string) error { - for _, node := range nodes { - matches := kccAPIVersionRegex.FindStringSubmatch(node.GetApiVersion()) - // Check if it's a Config Connector resource (apiVersion: *.cnrm.cloud.google.com/*). - // Ignore Config Connector system resources (apiVersion: core.cnrm.cloud.google.com/*). - if len(matches) == 2 && matches[1] != "core" { - annotations := node.GetAnnotations() - if _, ok := annotations[projectIDAnnotation]; !ok { - annotations[projectIDAnnotation] = projectID - if err := node.SetAnnotations(annotations); err != nil { - return fmt.Errorf("failed to set project-id annotation: %w", err) - } - continue - } - } - } - return nil -} - -type projectIDProcessor struct{} - -func (p *projectIDProcessor) Process(resourceList *framework.ResourceList) error { - dm := resourceList.FunctionConfig.GetDataMap() - projectID := dm[projectIDSetterName] - - if err := setSetters(resourceList.Items, projectID); err != nil { - return fmt.Errorf("failed to set project-id setter: %w", err) - } - if err := setProjectIDAnnotation(resourceList.Items, projectID); err != nil { - return fmt.Errorf("failed to set project-id annotation: %w", err) - } - - return nil -} - -func main() { - pp := projectIDProcessor{} - cmd := command.Build(&pp, command.StandaloneEnabled, false) - - if err := cmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} diff --git a/functions/go/set-project-id/metadata.yaml b/functions/go/set-project-id/metadata.yaml deleted file mode 100644 index dfbdec41d..000000000 --- a/functions/go/set-project-id/metadata.yaml +++ /dev/null @@ -1,13 +0,0 @@ -image: gcr.io/kpt-fn/set-project-id -description: Set project id setter and annotatons if unset. -tags: - - mutator - - GCP -sourceURL: https://github.com/GoogleContainerTools/kpt-functions-catalog/tree/master/functions/go/set-project-id -examplePackageURLs: - - https://github.com/GoogleContainerTools/kpt-functions-catalog/tree/master/examples/set-project-id-simple - - https://github.com/GoogleContainerTools/kpt-functions-catalog/tree/master/examples/set-project-id-advanced -emails: - - kpt-team@google.com -license: Apache-2.0 -