diff --git a/functions/go/configmap-generator/Dockerfile b/functions/go/configmap-generator/Dockerfile new file mode 100644 index 000000000..54bff2986 --- /dev/null +++ b/functions/go/configmap-generator/Dockerfile @@ -0,0 +1,16 @@ +FROM --platform=$BUILDPLATFORM golang:1.17-alpine3.15 AS build +ENV CGO_ENABLED=0 +WORKDIR /go/src/ + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +ARG TARGETOS TARGETARCH +RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /usr/local/bin/function ./ + +############################################# + +FROM alpine:3.15 +COPY --from=build /usr/local/bin/function /usr/local/bin/function +ENTRYPOINT ["function"] diff --git a/functions/go/configmap-generator/abc b/functions/go/configmap-generator/abc new file mode 100644 index 000000000..d734161a8 --- /dev/null +++ b/functions/go/configmap-generator/abc @@ -0,0 +1,21 @@ +apiVersion: config.kubernetes.io/v1 +kind: ResourceList +items: +- apiVersion: fn.kpt.dev/v1alpha1 + kind: BuiltInNonKrm + spec: + filename: my.cnf + content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=16M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid" + metadata: + name: mycnf-ini-file +functionConfig: + apiVersion: fn.kpt.dev/v1alpha1 + kind: ConfigMapGenerator + metadata: + name: mariadb + namespace: example + spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true \ No newline at end of file diff --git a/functions/go/configmap-generator/abc-dup b/functions/go/configmap-generator/abc-dup new file mode 100644 index 000000000..1fab417ac --- /dev/null +++ b/functions/go/configmap-generator/abc-dup @@ -0,0 +1,60 @@ +apiVersion: config.kubernetes.io/v1 +kind: ResourceList +items: +- apiVersion: fn.kpt.dev/v1alpha1 + kind: BuiltInNonKrm + spec: + filename: my.cnf + content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=32M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid" + metadata: + name: mycnf-ini-file +- kind: ConfigMap + apiVersion: v1 + metadata: # kpt-merge: /mariadb-92256k9879 + name: mariadb-92256k9879 + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/upstream-identifier: '|ConfigMap|default|mariadb-92256k9879' + data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + immutable: true + +functionConfig: + apiVersion: fn.kpt.dev/v1alpha1 + kind: ConfigMapGenerator + metadata: + name: mariadb + namespace: example + spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true diff --git a/functions/go/configmap-generator/abc-previous b/functions/go/configmap-generator/abc-previous new file mode 100644 index 000000000..40542cbde --- /dev/null +++ b/functions/go/configmap-generator/abc-previous @@ -0,0 +1,227 @@ +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user2 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" + config.kubernetes.io/path: 'Kptfile' + internal.config.kubernetes.io/path: 'Kptfile' +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 099d8ab734d0364cd362f040ad2d9c453a65b27f +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-internal-4ftgd48d67 + namespace: example + annotations: + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + config.kubernetes.io/path: 'generated/ConfigMap_mariadb-internal-4ftgd48d67.yaml' + internal.config.kubernetes.io/path: 'generated/ConfigMap_mariadb-internal-4ftgd48d67.yaml' +data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 16M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3306" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-92256k9879 + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + config.kubernetes.io/path: 'generated/configmap_mariadb-92256k9879.yaml' + internal.config.kubernetes.io/path: 'generated/configmap_mariadb-92256k9879.yaml' +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' + config.kubernetes.io/path: 'service-mariadb.yaml' + internal.config.kubernetes.io/path: 'service-mariadb.yaml' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + nodePort: null + selector: + app.kubernetes.io/name: mariadb +--- +# Source: ghost/charts/mariadb/templates/primary/statefulset.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: 'apps|StatefulSet|example|mariadb' + config.kubernetes.io/path: 'statefulset-mariadb.yaml' + internal.config.kubernetes.io/path: 'statefulset-mariadb.yaml' +spec: + replicas: 1 + revisionHistoryLimit: 10 + serviceName: mariadb + updateStrategy: + type: RollingUpdate + template: + spec: + securityContext: + fsGroup: 1001 + containers: + - name: mariadb + image: docker.io/bitnami/mariadb:10.6.7-debian-10-r62 + imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "true" + - name: MARIADB_USER + value: bn_ghost + - name: MARIADB_DATABASE + value: bitnami_ghost + - name: ALLOW_EMPTY_PASSWORD + value: "true" + ports: + - name: mysql + containerPort: 3306 + resources: + limits: {} + requests: {} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.ini + subPath: my.ini + volumes: + - name: config + configMap: + name: mariadb-92256k9879 + metadata: + labels: + app.kubernetes.io/name: mariadb + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/name: mariadb + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + selector: + matchLabels: + app.kubernetes.io/name: mariadb +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=32M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: + name: mycnf-ini-file \ No newline at end of file diff --git a/functions/go/configmap-generator/abc-write b/functions/go/configmap-generator/abc-write new file mode 100644 index 000000000..c695b8d5a --- /dev/null +++ b/functions/go/configmap-generator/abc-write @@ -0,0 +1,126 @@ +apiVersion: config.kubernetes.io/v1 +kind: ResourceList +items: +- apiVersion: fn.kpt.dev/v1alpha1 + kind: ConfigMapGenerator + metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'fn-config.yaml' + internal.config.kubernetes.io/path: 'fn-config.yaml' + config.kubernetes.io/index: '0' + internal.config.kubernetes.io/index: '0' + internal.config.kubernetes.io/seqindent: 'compact' + spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf +- kind: ConfigMap + apiVersion: v1 + metadata: + name: mariadb-6bttht6hbm + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.config.kubernetes.io/path: resource.yaml + config.kubernetes.io/path: resource.yaml + config.kubernetes.io/index: "2" + internal.config.kubernetes.io/index: '2' + internal.config.kubernetes.io/seqindent: 'compact' + data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3308 # a different change from upstream + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + upstream_field=yuwen-bbbbbbbbbb + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + immutable: true +- kind: ConfigMap + apiVersion: v1 + metadata: # kpt-merge: example/mariadb-internal-gg58g98t77 + name: mariadb-internal-9b77bh28t5 + namespace: example + annotations: + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/upstream-identifier: '|ConfigMap|example|mariadb-internal-gg58g98t77' + config.kubernetes.io/path: 'resource.yaml' + internal.config.kubernetes.io/path: 'resource.yaml' + internal.kpt.dev/generated-builtin-merged: "true" + config.kubernetes.io/index: '3' + internal.config.kubernetes.io/index: '3' + internal.config.kubernetes.io/seqindent: 'compact' + data: + cm.client.default-character-set: UTF8 + cm.client.new_field: yuwenma-aaaaaaa + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 32M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3308" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp + cm.mysqld.upstream_field: yuwen-bbbbbbbbbb + +functionConfig: + apiVersion: fn.kpt.dev/v1alpha1 + kind: ConfigMapGenerator + metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'fn-config.yaml' + internal.config.kubernetes.io/path: 'fn-config.yaml' + config.kubernetes.io/index: '0' + internal.config.kubernetes.io/index: '0' + internal.config.kubernetes.io/seqindent: 'compact' + spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf \ No newline at end of file diff --git a/functions/go/configmap-generator/api/kptfile/v1/types.go b/functions/go/configmap-generator/api/kptfile/v1/types.go new file mode 100644 index 000000000..265e0c39e --- /dev/null +++ b/functions/go/configmap-generator/api/kptfile/v1/types.go @@ -0,0 +1,368 @@ +// Copyright 2021 Google LLC +// +// 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 defines Kptfile schema. +// Version: v1 +// swagger:meta +package v1 + +import ( + "fmt" +) + +const ( + KptFileName = "Kptfile" + KptFileKind = "Kptfile" + KptFileGroup = "kpt.dev" + KptFileVersion = "v1" + KptFileAPIVersion = KptFileGroup + "/" + KptFileVersion +) + +// ResourceMeta contains the metadata for a both Resource Type and Resource. +type ResourceMeta struct { + TypeMeta `json:",inline" yaml:",inline"` + // ObjectMeta is the metadata field of a Resource + ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` +} + +// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta +// No need for a direct dependence; the fields are stable. +type TypeMeta struct { + // APIVersion is the apiVersion field of a Resource + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + // Kind is the kind field of a Resource + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` +} + +// ObjectMeta contains metadata about a Resource +type ObjectMeta struct { + NameMeta `json:",inline" yaml:",inline"` + // Labels is the metadata.labels field of a Resource + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` + // Annotations is the metadata.annotations field of a Resource. + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` +} + +// NameMeta contains name information. +type NameMeta struct { + // Name is the metadata.name field of a Resource + Name string `json:"name,omitempty" yaml:"name,omitempty"` + // Namespace is the metadata.namespace field of a Resource + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} + +// KptFile contains information about a package managed with kpt. +// swagger:model kptfile +type KptFile struct { + ResourceMeta `yaml:",inline" json:",inline"` + + Upstream *Upstream `yaml:"upstream,omitempty" json:"upstream,omitempty"` + + // UpstreamLock is a resolved locator for the last fetch of the package. + UpstreamLock *UpstreamLock `yaml:"upstreamLock,omitempty" json:"upstreamLock,omitempty"` + + // Info contains metadata such as license, documentation, etc. + Info *PackageInfo `yaml:"info,omitempty" json:"info,omitempty"` + + // Pipeline declares the pipeline of functions. + Pipeline *Pipeline `yaml:"pipeline,omitempty" json:"pipeline,omitempty"` + + // Inventory contains parameters for the inventory object used in apply. + Inventory *Inventory `yaml:"inventory,omitempty" json:"inventory,omitempty"` +} + +// OriginType defines the type of origin for a package. +type OriginType string + +const ( + // GitOrigin specifies a package as having been cloned from a git repository. + GitOrigin OriginType = "git" +) + +// UpdateStrategyType defines the strategy for updating a package from upstream. +type UpdateStrategyType string + +// ToUpdateStrategy takes a string representing an update strategy and will +// return the strategy as an UpdateStrategyType. If the provided string does +// not match any known update strategies, an error will be returned. +func ToUpdateStrategy(strategy string) (UpdateStrategyType, error) { + switch strategy { + case string(ResourceMerge): + return ResourceMerge, nil + case string(FastForward): + return FastForward, nil + case string(ForceDeleteReplace): + return ForceDeleteReplace, nil + default: + return "", fmt.Errorf("unknown update strategy %q", strategy) + } +} + +const ( + // ResourceMerge performs a structural schema-aware comparison and + // merges the changes into the local package. + ResourceMerge UpdateStrategyType = "resource-merge" + // FastForward fails without updating if the local package was modified + // since it was fetched. + FastForward UpdateStrategyType = "fast-forward" + // ForceDeleteReplace wipes all local changes to the package. + ForceDeleteReplace UpdateStrategyType = "force-delete-replace" +) + +// UpdateStrategies is a slice with all the supported update strategies. +var UpdateStrategies = []UpdateStrategyType{ + ResourceMerge, + FastForward, + ForceDeleteReplace, +} + +// UpdateStrategiesAsStrings returns a list of update strategies as strings. +func UpdateStrategiesAsStrings() []string { + var strs []string + for _, s := range UpdateStrategies { + strs = append(strs, string(s)) + } + return strs +} + +// Upstream is a user-specified upstream locator for a package. +type Upstream struct { + // Type is the type of origin. + Type OriginType `yaml:"type,omitempty" json:"type,omitempty"` + + // Git is the locator for a package stored on Git. + Git *Git `yaml:"git,omitempty" json:"git,omitempty"` + + // UpdateStrategy declares how a package will be updated from upstream. + UpdateStrategy UpdateStrategyType `yaml:"updateStrategy,omitempty" json:"updateStrategy,omitempty"` +} + +// Git is the user-specified locator for a package on Git. +type Git struct { + // Repo is the git repository the package. + // e.g. 'https://github.com/kubernetes/examples.git' + Repo string `yaml:"repo,omitempty" json:"repo,omitempty"` + + // Directory is the sub directory of the git repository. + // e.g. 'staging/cockroachdb' + Directory string `yaml:"directory,omitempty" json:"directory,omitempty"` + + // Ref can be a Git branch, tag, or a commit SHA-1. + Ref string `yaml:"ref,omitempty" json:"ref,omitempty"` +} + +// UpstreamLock is a resolved locator for the last fetch of the package. +type UpstreamLock struct { + // Type is the type of origin. + Type OriginType `yaml:"type,omitempty" json:"type,omitempty"` + + // Git is the resolved locator for a package on Git. + Git *GitLock `yaml:"git,omitempty" json:"git,omitempty"` +} + +// GitLock is the resolved locator for a package on Git. +type GitLock struct { + // Repo is the git repository that was fetched. + // e.g. 'https://github.com/kubernetes/examples.git' + Repo string `yaml:"repo,omitempty" json:"repo,omitempty"` + + // Directory is the sub directory of the git repository that was fetched. + // e.g. 'staging/cockroachdb' + Directory string `yaml:"directory,omitempty" json:"directory,omitempty"` + + // Ref can be a Git branch, tag, or a commit SHA-1 that was fetched. + // e.g. 'master' + Ref string `yaml:"ref,omitempty" json:"ref,omitempty"` + + // Commit is the SHA-1 for the last fetch of the package. + // This is set by kpt for bookkeeping purposes. + Commit string `yaml:"commit,omitempty" json:"commit,omitempty"` +} + +// PackageInfo contains optional information about the package such as license, documentation, etc. +// These fields are not consumed by any functionality in kpt and are simply passed through. +// Note that like any other KRM resource, humans and automation can also use `metadata.labels` and +// `metadata.annotations` as the extension mechanism. +type PackageInfo struct { + // Site is the URL for package web page. + Site string `yaml:"site,omitempty" json:"site,omitempty"` + + // Email is the list of emails for the package authors. + Emails []string `yaml:"emails,omitempty" json:"emails,omitempty"` + + // SPDX license identifier (e.g. "Apache-2.0"). See: https://spdx.org/licenses/ + License string `yaml:"license,omitempty" json:"license,omitempty"` + + // Relative slash-delimited path to the license file (e.g. LICENSE.txt) + LicenseFile string `yaml:"licenseFile,omitempty" json:"licenseFile,omitempty"` + + // Description contains a short description of the package. + Description string `yaml:"description,omitempty" json:"description,omitempty"` + + // Keywords is a list of keywords for this package. + Keywords []string `yaml:"keywords,omitempty" json:"keywords,omitempty"` + + // Man is the path to documentation about the package + Man string `yaml:"man,omitempty" json:"man,omitempty"` +} + +// Subpackages declares a local or remote subpackage. +type Subpackage struct { + // Name of the immediate subdirectory relative to this Kptfile where the suppackage + // either exists (local subpackages) or will be fetched to (remote subpckages). + // This must be unique across all subpckages of a package. + LocalDir string `yaml:"localDir,omitempty" json:"localDir,omitempty"` + + // Upstream is a reference to where the subpackage should be fetched from. + // Whether a subpackage is local or remote is determined by whether Upstream is specified. + Upstream *Upstream `yaml:"upstream,omitempty" json:"upstream,omitempty"` +} + +// Pipeline declares a pipeline of functions used to mutate or validate resources. +type Pipeline struct { + // Sources defines the source packages to resolve as input to the pipeline. Possible values: + // a) A slash-separated, OS-agnostic relative package path which may include '.' and '..' e.g. './base', '../foo' + // The source package is resolved recursively. + // b) Resources in this package using '.'. Meta resources such as the Kptfile, Pipeline, and function configs + // are excluded. + // c) Resources in this package AND all resolved subpackages using './*' + // + // Resultant list of resources are ordered: + // - According to the order of sources specified in this array. + // - When using './*': Subpackages are resolved in alphanumerical order before package resources. + // + // When omitted, defaults to './*'. + // Sources []string `yaml:"sources,omitempty"` + + // Following fields define the sequence of functions in the pipeline. + // Input of the first function is the resolved sources. + // Input of the second function is the output of the first function, and so on. + // Order of operation: mutators, validators + + // Mutators defines a list of of KRM functions that mutate resources. + Mutators []Function `yaml:"mutators,omitempty" json:"mutators,omitempty"` + + // Validators defines a list of KRM functions that validate resources. + // Validators are not permitted to mutate resources. + Validators []Function `yaml:"validators,omitempty" json:"validators,omitempty"` +} + +// String returns the string representation of Pipeline struct +// The string returned is the struct content in Go default format. +func (p *Pipeline) String() string { + return fmt.Sprintf("%+v", *p) +} + +// IsEmpty returns true if the pipeline doesn't contain any functions in any of +// the function chains (mutators, validators). +func (p *Pipeline) IsEmpty() bool { + if p == nil { + return true + } + if len(p.Mutators) == 0 && len(p.Validators) == 0 { + return true + } + return false +} + +// Function specifies a KRM function. +type Function struct { + // `Image` specifies the function container image. + // It can either be fully qualified, e.g.: + // + // image: gcr.io/kpt-fn/set-labels + // + // Optionally, kpt can be configured to use a image + // registry host-path that will be used to resolve the image path in case + // the image path is missing (Defaults to gcr.io/kpt-fn). + // e.g. The following resolves to gcr.io/kpt-fn/set-labels: + // + // image: set-labels + Image string `yaml:"image,omitempty" json:"image,omitempty"` + + // Exec specifies the function binary executable. + // The executable can be fully qualified or it must exists in the $PATH e.g: + // + // exec: set-namespace + // exec: /usr/local/bin/my-custom-fn + Exec string `yaml:"exec,omitempty" json:"exec,omitempty"` + + // `ConfigPath` specifies a slash-delimited relative path to a file in the current directory + // containing a KRM resource used as the function config. This resource is + // excluded when resolving 'sources', and as a result cannot be operated on + // by the pipeline. + ConfigPath string `yaml:"configPath,omitempty" json:"configPath,omitempty"` + + // `ConfigMap` is a convenient way to specify a function config of kind ConfigMap. + ConfigMap map[string]string `yaml:"configMap,omitempty" json:"configMap,omitempty"` + + // `Name` is used to uniquely identify the function declaration + // this is primarily used for merging function declaration with upstream counterparts + Name string `yaml:"name,omitempty" json:"name,omitempty"` + + // `Selectors` are used to specify resources on which the function should be executed + // if not specified, all resources are selected + Selectors []Selector `yaml:"selectors,omitempty" json:"selectors,omitempty"` + + // `Exclude` are used to specify resources on which the function should NOT be executed. + // If not specified, all resources selected by `Selectors` are selected. + Exclusions []Selector `yaml:"exclude,omitempty" json:"exclude,omitempty"` +} + +// Selector specifies the selection criteria +// please update IsEmpty method if more properties are added +type Selector struct { + // APIVersion of the target resources + APIVersion string `yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"` + // Kind of the target resources + Kind string `yaml:"kind,omitempty" json:"kind,omitempty"` + // Name of the target resources + Name string `yaml:"name,omitempty" json:"name,omitempty"` + // Namespace of the target resources + Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"` + // Labels on the target resources + Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"` + // Annotations on the target resources + Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"` +} + +// IsEmpty returns true of none of the selection criteria is specified +func (s Selector) IsEmpty() bool { + return s.APIVersion == "" && + s.Namespace == "" && + s.Name == "" && + s.Kind == "" && + len(s.Labels) == 0 && + len(s.Annotations) == 0 +} + +// Inventory encapsulates the parameters for the inventory resource applied to a cluster. +// All of the the parameters are required if any are set. +type Inventory struct { + // Namespace for the inventory resource. + Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"` + // Name of the inventory resource. + Name string `yaml:"name,omitempty" json:"name,omitempty"` + // Unique label to identify inventory resource in cluster. + InventoryID string `yaml:"inventoryID,omitempty" json:"inventoryID,omitempty"` + Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"` + Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"` +} + +func (i Inventory) IsValid() bool { + // Name and Namespace are required inventory fields, so we check these 2 fields. + // InventoryID is an optional field since we only store it locally if the user + // specifies one. + return i.Name != "" && i.Namespace != "" +} diff --git a/functions/go/configmap-generator/api/util/kptfileutil.go b/functions/go/configmap-generator/api/util/kptfileutil.go new file mode 100644 index 000000000..e94e3fff1 --- /dev/null +++ b/functions/go/configmap-generator/api/util/kptfileutil.go @@ -0,0 +1,35 @@ +// Copyright 2021 Google LLC +// +// 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 util + +import ( + "fmt" + "strings" + + kptfilev1 "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/api/kptfile/v1" + "gopkg.in/yaml.v3" +) + +// DecodeKptfile decodes a KptFile from a yaml string. +func DecodeKptfile(kf string) (*kptfilev1.KptFile, error) { + kptfile := &kptfilev1.KptFile{} + f := strings.NewReader(kf) + d := yaml.NewDecoder(f) + d.KnownFields(true) + if err := d.Decode(&kptfile); err != nil { + return &kptfilev1.KptFile{}, fmt.Errorf("invalid 'v1' Kptfile: %w", err) + } + return kptfile, nil +} diff --git a/functions/go/configmap-generator/defaults.env b/functions/go/configmap-generator/defaults.env new file mode 100644 index 000000000..90cb17c3a --- /dev/null +++ b/functions/go/configmap-generator/defaults.env @@ -0,0 +1,2 @@ +BUILDER_IMAGE=golang:1.17-alpine3.15 +BASE_IMAGE=alpine:3.15 diff --git a/functions/go/configmap-generator/fn/.golangci.yml b/functions/go/configmap-generator/fn/.golangci.yml new file mode 100644 index 000000000..e273601c5 --- /dev/null +++ b/functions/go/configmap-generator/fn/.golangci.yml @@ -0,0 +1,64 @@ +# Copyright 2022 Google LLC +# +# 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. + +run: + deadline: 5m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + # - funlen + - gochecknoinits + - goconst + # - gocritic + # - gocyclo + - gofmt + - goimports + - golint + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - lll + - misspell + - nakedret + - scopelint + - staticcheck + - structcheck + # stylecheck demands that acronyms not be treated as words + # in camelCase, so JsonOp become JSONOp, etc. Yuck. + # - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + +linters-settings: + dupl: + threshold: 400 + lll: + line-length: 170 + gocyclo: + min-complexity: 15 + golint: + min-confidence: 0.85 diff --git a/functions/go/configmap-generator/fn/canonical.go b/functions/go/configmap-generator/fn/canonical.go new file mode 100644 index 000000000..087623239 --- /dev/null +++ b/functions/go/configmap-generator/fn/canonical.go @@ -0,0 +1,37 @@ +package fn + +func NewNonKrmResource() *NonKrmResource { + resource := &NonKrmResource{} + resource.Kind = NonKrmKind + resource.APIVersion = KptFunctionApiVersion + return resource +} + +type NonKrmResource struct { + TypeMeta `json:",inline" yaml:",inline"` + NameMeta `json:",inline" yaml:",inline"` + Spec NonKrmResourceSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +type NonKrmResourceSpec struct { + FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"` + LocalPath string `json:"localPath,omitempty" yaml:"localPath,omitempty"` + Content string `json:"content,omitempty" yaml:"content,omitempty"` +} + +// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta +// No need for a direct dependence; the fields are stable. +type TypeMeta struct { + // APIVersion is the apiVersion field of a Resource + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + // Kind is the kind field of a Resource + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` +} + +// NameMeta contains name information. +type NameMeta struct { + // Name is the metadata.name field of a Resource + Name string `json:"name,omitempty" yaml:"name,omitempty"` + // Namespace is the metadata.namespace field of a Resource + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} diff --git a/functions/go/configmap-generator/fn/const.go b/functions/go/configmap-generator/fn/const.go new file mode 100644 index 000000000..bb95d6594 --- /dev/null +++ b/functions/go/configmap-generator/fn/const.go @@ -0,0 +1,77 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +const ( + // internalPrefix is the prefix given to internal annotations that are used + // internally by the orchestrator + internalPrefix string = "internal.config.kubernetes.io/" + + // IndexAnnotation records the index of a specific resource in a file or input stream. + IndexAnnotation string = internalPrefix + "index" + + // PathAnnotation records the path to the file the Resource was read from + PathAnnotation string = internalPrefix + "path" + + // SeqIndentAnnotation records the sequence nodes indentation of the input resource + SeqIndentAnnotation string = internalPrefix + "seqindent" + + // IdAnnotation records the id of the resource to map inputs to outputs + IdAnnotation string = internalPrefix + "id" + + // InternalAnnotationsMigrationResourceIDAnnotation is used to uniquely identify + // resources during round trip to and from a function execution. We will use it + // to track the internal annotations and reconcile them if needed. + InternalAnnotationsMigrationResourceIDAnnotation = internalPrefix + "annotations-migration-resource-id" + + // ConfigPrefix is the prefix given to the custom kubernetes annotations. + ConfigPrefix string = "config.kubernetes.io/" + + // KptLocalConfig marks a KRM resource to be skipped from deploying to the cluster via `kpt live apply`. + KptLocalConfig = ConfigPrefix + "local-config" +) + +// For Kpt use only constants +const ( + // KptUseOnlyPrefix is the prefix of kpt-only annotations. Users are not expected to touch these annotations. + KptUseOnlyPrefix = "internal.kpt.dev/" + + // UpstreamIdentifier is the annotation to record a resource's upstream origin. + // It is in the form of ||| + UpstreamIdentifier = KptUseOnlyPrefix + "upstream-identifier" + + // UnknownNamespace is the special char for cluster-scoped or unknown-scoped resources. This is only used in upstream-identifier + UnknownNamespace = "~C" + // DefaultNamespace is the actual namespace value if a namespace-scoped resource has its namespace field unspecified. + DefaultNamespace = "default" + + // e.g. ConfigMap Generator should be the functionConfig GKNN in the form of ||| + GeneratorIdentifier = KptUseOnlyPrefix + "generator" + GeneratorBuiltinIdentifier = KptUseOnlyPrefix + "generator-builtin-only" +) + +// For KPT Function Configuration +const ( + // KptFunctionGroup is the group name for the KRM resource which defines the configuration of a function execution. + // See KRM function specification `ResourceList.FunctionConfig` + KptFunctionGroup = "fn.kpt.dev" + // KptFunctionGroup is the version for the KRM resource which defines the configuration of a function execution. + // See KRM function specification `ResourceList.FunctionConfig` + KptFunctionVersion = "v1alpha1" + // KptFunctionGroup is the ApiVersion for the KRM resource which defines the configuration of a function execution. + // See KRM function specification `ResourceList.FunctionConfig` + KptFunctionApiVersion = KptFunctionGroup + "/" + KptFunctionVersion + + NonKrmKind = "BuiltInNonKrm" +) diff --git a/functions/go/configmap-generator/fn/doc.go b/functions/go/configmap-generator/fn/doc.go new file mode 100644 index 000000000..066f79f20 --- /dev/null +++ b/functions/go/configmap-generator/fn/doc.go @@ -0,0 +1,78 @@ +// Copyright 2022 Google LLC +// +// 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 fn provides the SDK to write KRM functions. + +Before you start + +This fn SDK requires some basic KRM function Specification knowledge. To make the best usage of your time, we recommend +you to be familiar with "ResourceList" before moving forward. + + The KRM Function Specification, or "ResourceList", defines the standards of the inter-process communication between + the orchestrator (i.e. kpt CLI) and functions. + +See KRM Function Specification reference in https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md + +KRM Function + +A KRM function can mutate and/or validate Kubernetes resources in a ResourceList. + +The ResourceList type and the KubeObject type are the core parts of this package. +The ResourceList type maps to the ResourceList in the function spec. + +Read more about how to use KRM functions in https://kpt.dev/book/04-using-functions/ + +Read more about how to develop a KRM function in https://kpt.dev/book/05-developing-functions/ + +A general workflow is: + 1. Reads the "ResourceList" object from STDIN. + 2. Gets the function configs from the "ResourceList.FunctionConfig". + 3. Mutate or validate the Kubernetes YAML resources from the "ResourceList.Items" field with the function configs. + 4. Writes the modified "ResourceList" to STDOUT. + 5. Write function message to "ResourceList.Results" with severity "Info", "Warning" or "Error" + +KubeObject + +The KubeObject is the basic unit to perform operations on KRM resources. + +In the "AsMain", both "Items" and "FunctionConfig" +are converted to the KubeObject(s). + +If you are familiar with unstructured.Unstructured, using KubeObject is as simple as using unstructured.Unstructured. +You can call function like `NestedStringOrDie` `SetNestedStringMap`, etc. + +Except that KubeObject will not have pass-in interface arguments, nor will return an interface. +Instead, you shall treat each KubeObject field (slice, or non-string map)as SubObject. + +SubObject also have most of the KubeObject methods, except the MetaType or NameType specific methods like "GetNamespace", "SetLabel". +This is because SubObject is designed as a sub object of KubeObject. SubObject to KubeObject is like `spec` section to `Deployment`. +You can get the Deployment name from `metadata.name`, KubeObject.GetName() or KubeObject.NestedString("metadata", "name"). +But you cannot get "metadata.name" from a Deployment "spec". For "spec" SubObject, you can get the ".replicas" field by +SubObject.NestedInt64("replicas") + +Besides unstructured style, another way to use KubeObject is to purely work on the KubeObject/SubObject by calling +"GetMap", "GetSlice", "UpsertMap" which expects the return to be SubObject(s) pointer. + +AsMain + +"AsMain" is the main entrypoint. In most cases, you only need to provide the mutator or validation logic and have AsMain +handles the ResourceList parsing, KRM resource field type detection, read from STDIN and write to STDOUT. + +"AsMain" accepts a struct that either implement the ResourceListProcessor interface or Runner interface. + +See github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/examples for detailed usage. + +*/ +package fn diff --git a/functions/go/configmap-generator/fn/errors.go b/functions/go/configmap-generator/fn/errors.go new file mode 100644 index 000000000..7b687134c --- /dev/null +++ b/functions/go/configmap-generator/fn/errors.go @@ -0,0 +1,73 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "strings" +) + +// ErrMissingFnConfig raises error if a required functionConfig is missing. +type ErrMissingFnConfig struct{} + +func (ErrMissingFnConfig) Error() string { + return "unable to find the functionConfig in the resourceList" +} + +// errKubeObjectFields raises if the KubeObject operation panics. +type errKubeObjectFields struct { + obj *KubeObject + fields []string +} + +func (e *errKubeObjectFields) Error() string { + return fmt.Sprintf("Resource(apiVersion=%v, kind=%v, Name=%v) has unmatched field type: `%v", + e.obj.GetAPIVersion(), e.obj.GetKind(), e.obj.GetName(), strings.Join(e.fields, "/")) +} + +// errSubObjectFields raises if the SubObject operation panics. +type errSubObjectFields struct { + fields []string +} + +func (e *errSubObjectFields) Error() string { + return fmt.Sprintf("SubObject has unmatched field type: `%v", strings.Join(e.fields, "/")) +} + +type errResultEnd struct { + obj *KubeObject + message string +} + +func (e *errResultEnd) Error() string { + if e.obj != nil { + return fmt.Sprintf("function is terminated by %v: %v", e.obj.ShortString(), e.message) + } + return fmt.Sprintf("function is terminated: %v", e.message) +} + +type ErrAttemptToTouchUpstreamIdentifier struct{} + +func (ErrAttemptToTouchUpstreamIdentifier) Error() string { + return fmt.Sprintf("annotation %v is managed by kpt and should not be modified", UpstreamIdentifier) +} + +type ErrInternalAnnotation struct { + Message string +} + +func (e *ErrInternalAnnotation) Error() string { + return e.Message +} diff --git a/functions/go/configmap-generator/fn/functionGenerator.go b/functions/go/configmap-generator/fn/functionGenerator.go new file mode 100644 index 000000000..2290cbace --- /dev/null +++ b/functions/go/configmap-generator/fn/functionGenerator.go @@ -0,0 +1,5 @@ +package fn + +type Generator interface { + Generate(context *Context, functionConfig *KubeObject, items KubeObjects) KubeObjects +} diff --git a/functions/go/configmap-generator/fn/functionrunner.go b/functions/go/configmap-generator/fn/functionrunner.go new file mode 100644 index 000000000..e3ffa726c --- /dev/null +++ b/functions/go/configmap-generator/fn/functionrunner.go @@ -0,0 +1,19 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +type Runner interface { + Run(context *Context, functionConfig *KubeObject, items KubeObjects) +} diff --git a/functions/go/configmap-generator/fn/generatorProcessor.go b/functions/go/configmap-generator/fn/generatorProcessor.go new file mode 100644 index 000000000..ae0babbd5 --- /dev/null +++ b/functions/go/configmap-generator/fn/generatorProcessor.go @@ -0,0 +1,38 @@ +package fn + +import ( + "fmt" + "reflect" +) + +type generatorProcessor struct { + fnGenerator Generator +} + +func (r generatorProcessor) Process(rl *ResourceList) (bool, error) { + ctx := &Context{results: &rl.Results} + r.config(ctx, rl.FunctionConfig) + rl.Items = r.fnGenerator.Generate(ctx, rl.FunctionConfig, rl.Items) + return true, nil +} + +func (r *generatorProcessor) config(ctx *Context, o *KubeObject) { + fnName := reflect.ValueOf(r.fnGenerator).Elem().Type().Name() + switch true { + case o.IsEmpty(): + ctx.Result("`FunctionConfig` is not given", Info) + case o.IsGVK("", "v1", "ConfigMap"): + data := o.NestedStringMapOrDie("data") + fnRunnerElem := reflect.ValueOf(r.fnGenerator).Elem() + for i := 0; i < fnRunnerElem.NumField(); i++ { + if fnRunnerElem.Field(i).Kind() == reflect.Map { + fnRunnerElem.Field(i).Set(reflect.ValueOf(data)) + break + } + } + case o.IsGVK("fn.kpt.dev", "v1alpha1", fnName): + o.AsOrDie(r.fnGenerator) + default: + ctx.ResultErrAndDie(fmt.Sprintf("unknown FunctionConfig `%v`, expect %v", o.GetKind(), fnName), o) + } +} diff --git a/functions/go/configmap-generator/fn/internal/document.go b/functions/go/configmap-generator/fn/internal/document.go new file mode 100644 index 000000000..3b8f75415 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/document.go @@ -0,0 +1,71 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "bytes" + "io" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type doc struct { + nodes []*yaml.Node +} + +func NewDoc(nodes ...*yaml.Node) *doc { + return &doc{nodes: nodes} +} + +func ParseDoc(b []byte) (*doc, error) { + br := bytes.NewReader(b) + + var nodes []*yaml.Node + decoder := yaml.NewDecoder(br) + for { + node := &yaml.Node{} + if err := decoder.Decode(node); err != nil { + if err == io.EOF { + break + } + return nil, err + } + nodes = append(nodes, node) + } + + return &doc{nodes: nodes}, nil +} + +func (d *doc) ToYAML() ([]byte, error) { + var w bytes.Buffer + encoder := yaml.NewEncoder(&w) + for _, node := range d.nodes { + if node.Kind == yaml.DocumentNode { + if len(node.Content) == 0 { + // These cause errors when we try to write them + continue + } + } + if err := encoder.Encode(node); err != nil { + return nil, err + } + } + + return w.Bytes(), nil +} + +func (d *doc) Elements() ([]*MapVariant, error) { + return ExtractObjects(d.nodes...) +} diff --git a/functions/go/configmap-generator/fn/internal/map.go b/functions/go/configmap-generator/fn/internal/map.go new file mode 100644 index 000000000..4da8ecf41 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/map.go @@ -0,0 +1,312 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "fmt" + "log" + "sort" + + "k8s.io/klog/v2" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func NewMap(node *yaml.Node) *MapVariant { + if node == nil { + node = &yaml.Node{ + Kind: yaml.MappingNode, + } + } + return &MapVariant{node: node} +} + +func NewStringMapVariant(m map[string]string) *MapVariant { + node := &yaml.Node{ + Kind: yaml.MappingNode, + } + for k, v := range m { + node.Content = append(node.Content, buildStringNode(k), buildStringNode(v)) + } + return &MapVariant{node: node} +} + +type MapVariant struct { + node *yaml.Node +} + +func (o *MapVariant) GetKind() variantKind { + return variantKindMap +} + +func (o *MapVariant) Node() *yaml.Node { + return o.node +} + +func (o *MapVariant) Entries() (map[string]variant, error) { + entries := make(map[string]variant) + + ynode := o.node + children := ynode.Content + if len(children)%2 != 0 { + return nil, fmt.Errorf("unexpected number of children for map %d", len(children)) + } + + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + valueNode := children[i+1] + + keyVariant := toVariant(keyNode) + valueVariant := toVariant(valueNode) + + switch keyVariant := keyVariant.(type) { + case *scalarVariant: + sv, isString := keyVariant.StringValue() + if isString { + entries[sv] = valueVariant + } else { + return nil, fmt.Errorf("key was not a string %v", keyVariant) + } + default: + return nil, fmt.Errorf("unexpected variant kind %T", keyVariant) + } + } + return entries, nil +} + +func asString(node *yaml.Node) (string, bool) { + if node.Kind == yaml.ScalarNode && (node.Tag == "!!str" || node.Tag == "") { + return node.Value, true + } + return "", false +} + +func (o *MapVariant) getVariant(key string) (variant, bool) { + valueNode, found := getValueNode(o.node, key) + if !found { + return nil, found + } + + v := toVariant(valueNode) + return v, true +} + +func getValueNode(m *yaml.Node, key string) (*yaml.Node, bool) { + children := m.Content + if len(children)%2 != 0 { + log.Fatalf("unexpected number of children for map %d", len(children)) + } + + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + + k, ok := asString(keyNode) + if ok && k == key { + valueNode := children[i+1] + return valueNode, true + } + } + return nil, false +} + +func (o *MapVariant) set(key string, val variant) { + o.setYAMLNode(key, val.Node()) +} + +func (o *MapVariant) setYAMLNode(key string, node *yaml.Node) { + children := o.node.Content + if len(children)%2 != 0 { + log.Fatalf("unexpected number of children for map %d", len(children)) + } + + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + + k, ok := asString(keyNode) + if ok && k == key { + // TODO: Copy comments? + oldNode := children[i+1] + children[i+1] = node + children[i+1].FootComment = oldNode.FootComment + children[i+1].HeadComment = oldNode.HeadComment + children[i+1].LineComment = oldNode.LineComment + return + } + } + + o.node.Content = append(o.node.Content, buildStringNode(key), node) +} + +func (o *MapVariant) remove(key string) (bool, error) { + removed := false + + children := o.node.Content + if len(children)%2 != 0 { + return false, fmt.Errorf("unexpected number of children for map %d", len(children)) + } + + var keep []*yaml.Node + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + + k, ok := asString(keyNode) + if ok && k == key { + removed = true + continue + } + + keep = append(keep, children[i], children[i+1]) + } + + o.node.Content = keep + + return removed, nil +} + +// remove field metadata.creationTimestamp when it's null. +func (o *MapVariant) cleanupCreationTimestamp() { + if o.node.Kind != yaml.MappingNode { + return + } + scalar, found, err := o.GetNestedScalar("metadata", "creationTimestamp") + if err != nil || !found { + return + } + if scalar.IsNull() { + _, _ = o.RemoveNestedField("metadata", "creationTimestamp") + } +} + +// sortFields tried to sort fields that it understands. e.g. data should come +// after apiVersion, kind and metadata in corev1.ConfigMap. +func (o *MapVariant) sortFields() error { + return sortFields(o.node) +} + +func sortFields(ynode *yaml.Node) error { + if ynode.Kind == yaml.SequenceNode { + for _, child := range ynode.Content { + if err := sortFields(child); err != nil { + return err + } + } + return nil + } + if ynode.Kind != yaml.MappingNode { + return nil + } + + pairs, err := ynodeToYamlKeyValuePairs(ynode) + if err != nil { + return fmt.Errorf("unable to sort fields in yaml: %w", err) + } + for _, pair := range pairs { + if err = sortFields(pair.value); err != nil { + return err + } + } + sort.Sort(pairs) + ynode.Content = yamlKeyValuePairsToYnode(pairs) + return nil +} + +func ynodeToYamlKeyValuePairs(ynode *yaml.Node) (yamlKeyValuePairs, error) { + if len(ynode.Content)%2 != 0 { + return nil, fmt.Errorf("invalid number of nodes: %d", len(ynode.Content)) + } + + var pairs yamlKeyValuePairs + for i := 0; i < len(ynode.Content); i += 2 { + pairs = append(pairs, &yamlKeyValuePair{name: ynode.Content[i], value: ynode.Content[i+1]}) + } + return pairs, nil +} + +func yamlKeyValuePairsToYnode(pairs yamlKeyValuePairs) []*yaml.Node { + var nodes []*yaml.Node + for _, pair := range pairs { + nodes = append(nodes, pair.name, pair.value) + } + return nodes +} + +type yamlKeyValuePair struct { + name *yaml.Node + value *yaml.Node +} + +type yamlKeyValuePairs []*yamlKeyValuePair + +func (nodes yamlKeyValuePairs) Len() int { return len(nodes) } + +func (nodes yamlKeyValuePairs) Less(i, j int) bool { + iIndex, iFound := yaml.FieldOrder[nodes[i].name.Value] + jIndex, jFound := yaml.FieldOrder[nodes[j].name.Value] + if iFound && jFound { + return iIndex < jIndex + } + if iFound { + return true + } + if jFound { + return false + } + + if nodes[i].name != nodes[j].name { + return nodes[i].name.Value < nodes[j].name.Value + } + return false +} + +func (nodes yamlKeyValuePairs) Swap(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] } + +// UpsertMap will return the field as a map if it exists and is a map, +// otherwise it will insert a map at the specified field. +// Note that if the value exists but is not a map, it will be replaced with a map. +func (o *MapVariant) UpsertMap(field string) *MapVariant { + m := o.GetMap(field) + if m != nil { + return m + } + + keyNode := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: field, + Tag: "!!str", + } + valueNode := &yaml.Node{ + Kind: yaml.MappingNode, + } + o.node.Content = append(o.node.Content, keyNode, valueNode) + return &MapVariant{node: valueNode} +} + +// GetMap will return the field as a map if it exists and is a map, +// otherwise it will return nil. +// Note that if the value exists but is not a map, nil will be returned. +func (o *MapVariant) GetMap(field string) *MapVariant { + node, found := o.getVariant(field) + + if found { + switch node := node.(type) { + case *MapVariant: + return node + + default: + klog.Warningf("getting value of unexpected type, got %T, want map", node) + } + } + + return nil +} diff --git a/functions/go/configmap-generator/fn/internal/maphelpers.go b/functions/go/configmap-generator/fn/internal/maphelpers.go new file mode 100644 index 000000000..6af95996d --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/maphelpers.go @@ -0,0 +1,227 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "fmt" +) + +func (o *MapVariant) GetNestedValue(fields ...string) (variant, bool, error) { + current := o + n := len(fields) + for i := 0; i < n; i++ { + entry, found := current.getVariant(fields[i]) + if !found { + return nil, found, nil + } + + if i == n-1 { + return entry, true, nil + } + entryM, ok := entry.(*MapVariant) + if !ok { + return nil, found, fmt.Errorf("wrong type, got: %T", entry) + } + current = entryM + } + return nil, false, fmt.Errorf("unexpected code reached") +} + +func (o *MapVariant) SetNestedValue(val variant, fields ...string) error { + current := o + n := len(fields) + var err error + for i := 0; i < n; i++ { + if i == n-1 { + current.set(fields[i], val) + } else { + current, _, err = current.getMap(fields[i], true) + if err != nil { + return err + } + } + } + return nil +} + +func (o *MapVariant) GetNestedMap(fields ...string) (*MapVariant, bool, error) { + v, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + mv, ok := v.(*MapVariant) + if !ok { + return nil, found, fmt.Errorf("wrong type, got: %T", v) + } + return mv, found, err +} + +func (o *MapVariant) SetNestedMap(m *MapVariant, fields ...string) error { + return o.SetNestedValue(m, fields...) +} + +func (o *MapVariant) GetNestedStringMap(fields ...string) (map[string]string, bool, error) { + v, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + children := v.Node().Content + if len(children)%2 != 0 { + return nil, found, fmt.Errorf("invalid yaml map node") + } + m := make(map[string]string, len(children)/2) + for i := 0; i < len(children); i = i + 2 { + m[children[i].Value] = children[i+1].Value + } + return m, found, nil +} + +func (o *MapVariant) SetNestedStringMap(m map[string]string, fields ...string) error { + return o.SetNestedMap(NewStringMapVariant(m), fields...) +} + +func (o *MapVariant) GetNestedScalar(fields ...string) (*scalarVariant, bool, error) { + node, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + nodeS, ok := node.(*scalarVariant) + if !ok { + return nil, found, fmt.Errorf("incorrect type, was %T", node) + } + return nodeS, found, nil +} + +func (o *MapVariant) GetNestedString(fields ...string) (string, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return "", found, err + } + sv, isString := scalar.StringValue() + if isString { + return sv, found, nil + } + return "", found, fmt.Errorf("node was not a string, was %v", scalar.node.Tag) +} + +func (o *MapVariant) SetNestedString(s string, fields ...string) error { + return o.SetNestedValue(newStringScalarVariant(s), fields...) +} + +func (o *MapVariant) GetNestedBool(fields ...string) (bool, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return false, found, err + } + bv, isBool := scalar.BoolValue() + if isBool { + return bv, found, nil + } + return false, found, fmt.Errorf("node was not a bool, was %v", scalar.Node().Tag) +} + +func (o *MapVariant) SetNestedBool(b bool, fields ...string) error { + return o.SetNestedValue(newBoolScalarVariant(b), fields...) +} + +func (o *MapVariant) GetNestedInt(fields ...string) (int, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return 0, found, err + } + iv, isInt := scalar.IntValue() + if isInt { + return iv, found, nil + } + return 0, found, fmt.Errorf("node was not a int, was %v", scalar.node.Tag) +} + +func (o *MapVariant) SetNestedInt(i int, fields ...string) error { + return o.SetNestedValue(newIntScalarVariant(i), fields...) +} + +func (o *MapVariant) GetNestedFloat(fields ...string) (float64, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return 0, found, err + } + fv, isFloat := scalar.FloatValue() + if isFloat { + return fv, found, nil + } + return 0, found, fmt.Errorf("node was not a float, was %v", scalar.node.Tag) +} + +func (o *MapVariant) SetNestedFloat(f float64, fields ...string) error { + return o.SetNestedValue(newFloatScalarVariant(f), fields...) +} + +func (o *MapVariant) GetNestedSlice(fields ...string) (*sliceVariant, bool, error) { + node, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + nodeS, ok := node.(*sliceVariant) + if !ok { + return nil, found, fmt.Errorf("incorrect type, was %T", node) + } + return nodeS, found, err +} + +func (o *MapVariant) SetNestedSlice(s *sliceVariant, fields ...string) error { + return o.SetNestedValue(s, fields...) +} + +func (o *MapVariant) RemoveNestedField(fields ...string) (bool, error) { + current := o + n := len(fields) + for i := 0; i < n; i++ { + entry, found := current.getVariant(fields[i]) + if !found { + return false, nil + } + + if i == n-1 { + return current.remove(fields[i]) + } + switch entry := entry.(type) { + case *MapVariant: + current = entry + default: + return false, fmt.Errorf("value is of unexpected type %T", entry) + } + } + return false, fmt.Errorf("unexpected code reached") +} + +func (o *MapVariant) getMap(field string, create bool) (*MapVariant, bool, error) { + node, found := o.getVariant(field) + + if !found { + if !create { + return nil, found, nil + } + keyNode := buildStringNode(field) + valueNode := buildMappingNode() + o.node.Content = append(o.node.Content, keyNode, valueNode) + valueVariant := &MapVariant{node: valueNode} + return valueVariant, found, nil + } + + if node, ok := node.(*MapVariant); ok { + return node, found, nil + } + return nil, found, fmt.Errorf("incorrect type, was %T", node) +} diff --git a/functions/go/configmap-generator/fn/internal/namespace.go b/functions/go/configmap-generator/fn/internal/namespace.go new file mode 100644 index 000000000..64aec8387 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/namespace.go @@ -0,0 +1,91 @@ +package internal + +import ( + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// PrecomputedIsNamespaceScoped copies the sigs.k8s.io/kustomize/kyaml/openapi precomputedIsNamespaceScoped +var PrecomputedIsNamespaceScoped = map[yaml.TypeMeta]bool{ + {APIVersion: "admissionregistration.k8s.io/v1", Kind: "MutatingWebhookConfiguration"}: false, + {APIVersion: "admissionregistration.k8s.io/v1", Kind: "ValidatingWebhookConfiguration"}: false, + {APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration"}: false, + {APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "ValidatingWebhookConfiguration"}: false, + {APIVersion: "apiextensions.k8s.io/v1", Kind: "CustomResourceDefinition"}: false, + {APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition"}: false, + {APIVersion: "apiregistration.k8s.io/v1", Kind: "APIService"}: false, + {APIVersion: "apiregistration.k8s.io/v1beta1", Kind: "APIService"}: false, + {APIVersion: "apps/v1", Kind: "ControllerRevision"}: true, + {APIVersion: "apps/v1", Kind: "DaemonSet"}: true, + {APIVersion: "apps/v1", Kind: "Deployment"}: true, + {APIVersion: "apps/v1", Kind: "ReplicaSet"}: true, + {APIVersion: "apps/v1", Kind: "StatefulSet"}: true, + {APIVersion: "autoscaling/v1", Kind: "HorizontalPodAutoscaler"}: true, + {APIVersion: "autoscaling/v1", Kind: "Scale"}: true, + {APIVersion: "autoscaling/v2beta1", Kind: "HorizontalPodAutoscaler"}: true, + {APIVersion: "autoscaling/v2beta2", Kind: "HorizontalPodAutoscaler"}: true, + {APIVersion: "batch/v1", Kind: "CronJob"}: true, + {APIVersion: "batch/v1", Kind: "Job"}: true, + {APIVersion: "batch/v1beta1", Kind: "CronJob"}: true, + {APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"}: false, + {APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"}: false, + {APIVersion: "coordination.k8s.io/v1", Kind: "Lease"}: true, + {APIVersion: "coordination.k8s.io/v1beta1", Kind: "Lease"}: true, + {APIVersion: "discovery.k8s.io/v1", Kind: "EndpointSlice"}: true, + {APIVersion: "discovery.k8s.io/v1beta1", Kind: "EndpointSlice"}: true, + {APIVersion: "events.k8s.io/v1", Kind: "Event"}: true, + {APIVersion: "events.k8s.io/v1beta1", Kind: "Event"}: true, + {APIVersion: "extensions/v1beta1", Kind: "Ingress"}: true, + {APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "FlowSchema"}: false, + {APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "PriorityLevelConfiguration"}: false, + {APIVersion: "networking.k8s.io/v1", Kind: "Ingress"}: true, + {APIVersion: "networking.k8s.io/v1", Kind: "IngressClass"}: false, + {APIVersion: "networking.k8s.io/v1", Kind: "NetworkPolicy"}: true, + {APIVersion: "networking.k8s.io/v1beta1", Kind: "Ingress"}: true, + {APIVersion: "networking.k8s.io/v1beta1", Kind: "IngressClass"}: false, + {APIVersion: "node.k8s.io/v1", Kind: "RuntimeClass"}: false, + {APIVersion: "node.k8s.io/v1beta1", Kind: "RuntimeClass"}: false, + {APIVersion: "policy/v1", Kind: "PodDisruptionBudget"}: true, + {APIVersion: "policy/v1beta1", Kind: "PodDisruptionBudget"}: true, + {APIVersion: "policy/v1beta1", Kind: "PodSecurityPolicy"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRoleBinding"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"}: true, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "RoleBinding"}: true, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRole"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRoleBinding"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "Role"}: true, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "RoleBinding"}: true, + {APIVersion: "scheduling.k8s.io/v1", Kind: "PriorityClass"}: false, + {APIVersion: "scheduling.k8s.io/v1beta1", Kind: "PriorityClass"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "CSIDriver"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "CSINode"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "StorageClass"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "VolumeAttachment"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIDriver"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSINode"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIStorageCapacity"}: true, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "StorageClass"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "VolumeAttachment"}: false, + {APIVersion: "v1", Kind: "ComponentStatus"}: false, + {APIVersion: "v1", Kind: "ConfigMap"}: true, + {APIVersion: "v1", Kind: "Endpoints"}: true, + {APIVersion: "v1", Kind: "Event"}: true, + {APIVersion: "v1", Kind: "LimitRange"}: true, + {APIVersion: "v1", Kind: "Namespace"}: false, + {APIVersion: "v1", Kind: "Node"}: false, + {APIVersion: "v1", Kind: "NodeProxyOptions"}: false, + {APIVersion: "v1", Kind: "PersistentVolume"}: false, + {APIVersion: "v1", Kind: "PersistentVolumeClaim"}: true, + {APIVersion: "v1", Kind: "Pod"}: true, + {APIVersion: "v1", Kind: "PodAttachOptions"}: true, + {APIVersion: "v1", Kind: "PodExecOptions"}: true, + {APIVersion: "v1", Kind: "PodPortForwardOptions"}: true, + {APIVersion: "v1", Kind: "PodProxyOptions"}: true, + {APIVersion: "v1", Kind: "PodTemplate"}: true, + {APIVersion: "v1", Kind: "ReplicationController"}: true, + {APIVersion: "v1", Kind: "ResourceQuota"}: true, + {APIVersion: "v1", Kind: "Secret"}: true, + {APIVersion: "v1", Kind: "Service"}: true, + {APIVersion: "v1", Kind: "ServiceAccount"}: true, + {APIVersion: "v1", Kind: "ServiceProxyOptions"}: true, +} diff --git a/functions/go/configmap-generator/fn/internal/scalar.go b/functions/go/configmap-generator/fn/internal/scalar.go new file mode 100644 index 000000000..5aa15c629 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/scalar.go @@ -0,0 +1,117 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "strconv" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +const ( + tagString = "!!str" + tagBool = "!!bool" + tagInt = "!!int" + tagFloat = "!!float" + tagNull = "!!null" +) + +type scalarVariant struct { + node *yaml.Node +} + +func (v *scalarVariant) GetKind() variantKind { + return variantKindScalar +} + +func newStringScalarVariant(s string) *scalarVariant { + return &scalarVariant{ + node: buildStringNode(s), + } +} + +func newBoolScalarVariant(b bool) *scalarVariant { + return &scalarVariant{ + node: buildBoolNode(b), + } +} + +func newIntScalarVariant(i int) *scalarVariant { + return &scalarVariant{ + node: buildIntNode(i), + } +} + +func newFloatScalarVariant(f float64) *scalarVariant { + return &scalarVariant{ + node: buildFloatNode(f), + } +} + +func (v *scalarVariant) IsNull() bool { + return v.node.Tag == tagNull +} + +func (v *scalarVariant) StringValue() (string, bool) { + switch v.node.Tag { + case tagString: + return v.node.Value, true + default: + return "", false + } +} + +func (v *scalarVariant) BoolValue() (bool, bool) { + switch v.node.Tag { + case tagBool: + b, err := strconv.ParseBool(v.node.Value) + if err != nil { + return b, false + } + return b, true + default: + return false, false + } +} + +func (v *scalarVariant) IntValue() (int, bool) { + switch v.node.Tag { + case tagInt: + i, err := strconv.Atoi(v.node.Value) + if err != nil { + return i, false + } + return i, true + default: + return 0, false + } +} + +func (v *scalarVariant) FloatValue() (float64, bool) { + switch v.node.Tag { + case tagFloat: + f, err := strconv.ParseFloat(v.node.Value, 64) + if err != nil { + return f, false + } + return f, true + default: + return 0, false + } +} + +func (v *scalarVariant) Node() *yaml.Node { + return v.node +} diff --git a/functions/go/configmap-generator/fn/internal/slice.go b/functions/go/configmap-generator/fn/internal/slice.go new file mode 100644 index 000000000..1c2f64f20 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/slice.go @@ -0,0 +1,51 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type sliceVariant struct { + node *yaml.Node +} + +func NewSliceVariant(s ...variant) *sliceVariant { + node := buildSequenceNode() + for _, v := range s { + node.Content = append(node.Content, v.Node()) + } + return &sliceVariant{node: node} +} + +func (v *sliceVariant) GetKind() variantKind { + return variantKindSlice +} + +func (v *sliceVariant) Node() *yaml.Node { + return v.node +} + +func (v *sliceVariant) Clear() { + v.node.Content = nil +} + +func (v *sliceVariant) Elements() ([]*MapVariant, error) { + return ExtractObjects(v.node.Content...) +} + +func (v *sliceVariant) Add(node variant) { + v.node.Content = append(v.node.Content, node.Node()) +} diff --git a/functions/go/configmap-generator/fn/internal/test/go.mod b/functions/go/configmap-generator/fn/internal/test/go.mod new file mode 100644 index 000000000..86bfe2f2a --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/test/go.mod @@ -0,0 +1,50 @@ +module github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/internal_test + +go 1.17 + +replace github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0 => ../.. + +require ( + github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0 + github.com/stretchr/testify v1.7.1 + k8s.io/api v0.24.0 + k8s.io/apimachinery v0.24.0 + sigs.k8s.io/kustomize/kyaml v0.13.7-0.20220418212550-9d5491c2e20c +) + +require ( + github.com/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20220720212527-133180134b93 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.2.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + 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/xlab/treeprint v1.1.0 // indirect + golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + k8s.io/klog/v2 v2.60.1 // indirect + k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect +) diff --git a/functions/go/configmap-generator/fn/internal/test/go.sum b/functions/go/configmap-generator/fn/internal/test/go.sum new file mode 100644 index 000000000..a64ccc637 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/test/go.sum @@ -0,0 +1,723 @@ +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/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20220720212527-133180134b93 h1:c1GhPzYzU2a3E+/2WB9OxoljK2pNYfsBF5Fz9GkdYXs= +github.com/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20220720212527-133180134b93/go.mod h1:gkK43tTaPXFNASpbIbQImzhmt1hdcdin++kvzTblykc= +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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +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.12.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/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +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-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +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/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/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/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +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/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.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +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 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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/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/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +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/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-20180906233101-161cd47e91fd/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-20200520004742-59133d7f0dd7/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/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +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-20180909124046-d0be0721c37e/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-20190904154756-749cb33beabd/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-20191120155948-bd437916bb0e/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-20200519105757-fe76b779f299/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-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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-20200505023115-26f46d2f7ef8/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/tools v0.1.5/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= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/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-20201019141844-1ed22bb0c154/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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.3.0/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/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg= +k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= +k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ= +k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 h1:nqYOUleKLC/0P1zbU29F5q6aoezM6MOAVz+iyfQbZ5M= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +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/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/kustomize/kyaml v0.13.7-0.20220418212550-9d5491c2e20c h1:Y0cW/MVbKH9jRlMbpLe/4gs2m6qteP1pUGP+JkWcGdA= +sigs.k8s.io/kustomize/kyaml v0.13.7-0.20220418212550-9d5491c2e20c/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +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/configmap-generator/fn/internal/test/maphelper_test.go b/functions/go/configmap-generator/fn/internal/test/maphelper_test.go new file mode 100644 index 000000000..536af6da0 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/test/maphelper_test.go @@ -0,0 +1,192 @@ +// Copyright 2022 Google LLC +// +// 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 internal_test + +import ( + "bytes" + "testing" + + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/internal" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + yaml2 "sigs.k8s.io/kustomize/kyaml/yaml" +) + +const deploymentYaml = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx + env: prod + finalizers: + - foo + - bar +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 +` + +func parseRaw(in []byte) ([]*internal.MapVariant, error) { + d := yaml2.NewDecoder(bytes.NewBuffer(in)) + node := &yaml2.Node{} + err := d.Decode(node) + if err != nil { + return nil, err + } + return internal.ExtractObjects(node) +} + +func TestHelpers(t *testing.T) { + rawmvs, _ := parseRaw([]byte(deploymentYaml)) + assert.Len(t, rawmvs, 1, "expect 1 object after parsing") + mv := rawmvs[0] + + name, found, err := mv.GetNestedString("metadata", "name") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "nginx-deployment", name) + + ns, found, err := mv.GetNestedString("metadata", "namespace") + assert.NoError(t, err) + assert.False(t, found) + assert.Equal(t, "", ns) + err = mv.SetNestedString("test-ns", "metadata", "namespace") + assert.NoError(t, err) + ns, found, err = mv.GetNestedString("metadata", "namespace") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "test-ns", ns) + + replicas, found, err := mv.GetNestedInt("spec", "replicas") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, 3, replicas) + err = mv.SetNestedInt(10, "spec", "replicas") + assert.NoError(t, err) + replicas, found, err = mv.GetNestedInt("spec", "replicas") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, 10, replicas) + + notExistInt, found, err := mv.GetNestedInt("spec", "foo") + assert.NoError(t, err) + assert.False(t, found) + assert.Equal(t, 0, notExistInt) + + labels, found, err := mv.GetNestedStringMap("metadata", "labels") + assert.NoError(t, err) + assert.True(t, found) + assert.Len(t, labels, 2) + assert.Equal(t, map[string]string{"app": "nginx", "env": "prod"}, labels) + + notExistStringMap, found, err := mv.GetNestedStringMap("metadata", "something") + assert.NoError(t, err) + assert.False(t, found) + assert.Len(t, notExistStringMap, 0) + var emptyMap map[string]string + assert.Equal(t, emptyMap, notExistStringMap) + + annotations, found, err := mv.GetNestedStringMap("metadata", "annotations") + assert.NoError(t, err) + assert.False(t, found) + assert.Len(t, annotations, 0) + assert.Equal(t, emptyMap, annotations) + err = mv.SetNestedStringMap(map[string]string{"hello": "world"}, "metadata", "annotations") + assert.NoError(t, err) + annotation, found, err := mv.GetNestedString("metadata", "annotations", "hello") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "world", annotation) + + metadata, found, err := mv.GetNestedMap("metadata") + assert.NoError(t, err) + assert.True(t, found) + err = mv.SetNestedMap(metadata, "spec", "template", "metadata") + assert.NoError(t, err) + label, found, err := mv.GetNestedString("spec", "template", "metadata", "labels", "env") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "prod", label) + + container := map[string]string{ + "name": "logger", + "image": "my-logger", + } + containers, found, err := mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + mvs, err := containers.Elements() + assert.NoError(t, err) + assert.Len(t, mvs, 1) + containers.Add(internal.NewStringMapVariant(container)) + containers, found, err = mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + mvs, err = containers.Elements() + assert.NoError(t, err) + assert.Len(t, mvs, 2) + + containers, found, err = mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + conts, err := containers.Elements() + assert.NoError(t, err) + for i, cont := range conts { + name, found, err = cont.GetNestedString("name") + if err == nil && found && name == "nginx" { + err = conts[i].SetNestedString("nginx:1.21.3", "image") + assert.NoError(t, err) + } + } + containers, found, err = mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + mvs, err = containers.Elements() + assert.NoError(t, err) + assert.Len(t, mvs, 2) + img, found, err := mvs[0].GetNestedString("image") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "nginx:1.21.3", img) + + trueVar := true + falseVar := false + sc := corev1.SecurityContext{ + RunAsNonRoot: &trueVar, + AllowPrivilegeEscalation: &falseVar, + } + scmv, err := internal.TypedObjectToMapVariant(sc) + assert.NoError(t, err) + err = mv.SetNestedMap(scmv, "spec", "template", "spec", "securityContext") + assert.NoError(t, err) + + runAsNonRoot, found, err := mv.GetNestedBool("spec", "template", "spec", "securityContext", "runAsNonRoot") + assert.NoError(t, err) + assert.True(t, found) + assert.True(t, runAsNonRoot) +} diff --git a/functions/go/configmap-generator/fn/internal/test/variant_test.go b/functions/go/configmap-generator/fn/internal/test/variant_test.go new file mode 100644 index 000000000..1cf13ad95 --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/test/variant_test.go @@ -0,0 +1,274 @@ +// Copyright 2022 Google LLC +// +// 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 internal_test + +import ( + "strings" + "testing" + + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/internal" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestMapVariantToTypedObject(t *testing.T) { + testcases := []struct { + name string + src string + dst interface{} + expected interface{} + }{ + { + name: "k8s built-in types", + src: `apiVersion: v1 +kind: ConfigMap +metadata: + name: my-cm + namespace: my-ns +data: + foo: bar +`, + dst: &corev1.ConfigMap{}, + expected: &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm", + Namespace: "my-ns", + }, + Data: map[string]string{ + "foo": "bar", + }, + }, + }, + { + name: "crd type with metav1.ObjectMeta", + src: `apiVersion: example.co/v1 +kind: Foo +metadata: + name: my-foo +desiredReplicas: 1 +`, + dst: &Foo{}, + expected: &Foo{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Foo", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-foo", + }, + DesiredReplicas: 1, + }, + }, + { + name: "crd type with yaml.ResourceIdentifier", + src: `apiVersion: example.co/v1 +kind: Bar +metadata: + name: my-bar +desiredReplicas: 1 +`, + dst: &Bar{}, + expected: &Bar{ + ResourceMeta: yaml.ResourceMeta{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Bar", + }, + ObjectMeta: yaml.ObjectMeta{ + NameMeta: yaml.NameMeta{ + Name: "my-bar", + }, + }, + }, + DesiredReplicas: 1, + }, + }, + } + + for _, tc := range testcases { + rn := yaml.MustParse(tc.src) + mv := internal.NewMap(rn.YNode()) + err := internal.MapVariantToTypedObject(mv, tc.dst) + assert.NoError(t, err) + assert.Equal(t, tc.expected, tc.dst) + } +} + +func TestNewFromTypedObject(t *testing.T) { + testcases := []struct { + name string + input interface{} + expected string + }{ + { + name: "k8s built-in types", + input: &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm", + Namespace: "my-ns", + }, + Data: map[string]string{ + "foo": "bar", + }, + }, + expected: `apiVersion: v1 +kind: ConfigMap +metadata: + name: my-cm + namespace: my-ns +data: + foo: bar +`, + }, + { + name: "crd type with metav1.ObjectMeta", + input: &Foo{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Foo", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-foo", + }, + DesiredReplicas: 1, + }, + expected: `apiVersion: example.co/v1 +kind: Foo +metadata: + name: my-foo +desiredReplicas: 1 +`, + }, + { + name: "crd type with yaml.ResourceIdentifier", + input: &Bar{ + ResourceMeta: yaml.ResourceMeta{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Bar", + }, + ObjectMeta: yaml.ObjectMeta{ + NameMeta: yaml.NameMeta{ + Name: "my-bar", + }, + }, + }, + DesiredReplicas: 1, + }, + expected: `apiVersion: example.co/v1 +kind: Bar +metadata: + name: my-bar +desiredReplicas: 1 +`, + }, + { + name: "k8s built in pod type", + input: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "container", + Image: "test", + }}, + }, + }, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: my-pod +spec: + containers: + - name: container + image: test + resources: {} +status: {} +`, + }, + } + + for _, tc := range testcases { + mv, err := fn.NewFromTypedObject(tc.input) + assert.NoError(t, err) + s := mv.String() + assert.Equal(t, tc.expected, s) + } +} + +func TestBadNewFromTypedObject(t *testing.T) { + input := []corev1.ConfigMap{ + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm", + Namespace: "my-ns", + }, + Data: map[string]string{ + "foo": "bar", + }, + }, + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm2", + Namespace: "my-ns2", + }, + Data: map[string]string{ + "foo2": "bar2", + }, + }, + } + _, err := fn.NewFromTypedObject(input) + if err == nil { + t.Errorf("expect error, got nil") + } + if !strings.Contains(err.Error(), "got reflect.Slice") { + t.Errorf("got unexpected error %v", err) + } +} + +type Foo struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + DesiredReplicas int `json:"desiredReplicas,omitempty" yaml:"desiredReplicas,omitempty"` +} + +type Bar struct { + yaml.ResourceMeta `json:",inline" yaml:",inline"` + DesiredReplicas int `json:"desiredReplicas,omitempty" yaml:"desiredReplicas,omitempty"` +} diff --git a/functions/go/configmap-generator/fn/internal/variant.go b/functions/go/configmap-generator/fn/internal/variant.go new file mode 100644 index 000000000..0d993a47d --- /dev/null +++ b/functions/go/configmap-generator/fn/internal/variant.go @@ -0,0 +1,200 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type variantKind string + +const ( + variantKindMap variantKind = "Map" + variantKindSlice variantKind = "Slice" + variantKindScalar variantKind = "Scalar" +) + +type variant interface { + GetKind() variantKind + Node() *yaml.Node +} + +// nodes are expected to be key1,value1,key2,value2,... +func buildMappingNode(nodes ...*yaml.Node) *yaml.Node { + return &yaml.Node{ + Kind: yaml.MappingNode, + Content: nodes, + } +} + +func buildSequenceNode(nodes ...*yaml.Node) *yaml.Node { + return &yaml.Node{ + Kind: yaml.SequenceNode, + Content: nodes, + } +} + +func buildStringNode(s string) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: s, + } +} + +func buildIntNode(i int) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!int", + Value: strconv.Itoa(i), + } +} + +func buildFloatNode(f float64) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!float", + Value: strconv.FormatFloat(f, 'f', -1, 64), + } +} + +func buildBoolNode(b bool) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!bool", + Value: strconv.FormatBool(b), + } +} + +func toVariant(n *yaml.Node) variant { + switch n.Kind { + case yaml.ScalarNode: + return &scalarVariant{node: n} + case yaml.MappingNode: + return &MapVariant{node: n} + case yaml.SequenceNode: + return &sliceVariant{node: n} + + default: + panic("unhandled yaml node kind") + } +} + +func ExtractObjects(nodes ...*yaml.Node) ([]*MapVariant, error) { + var objects []*MapVariant + + for _, node := range nodes { + switch node.Kind { + case yaml.DocumentNode: + children, err := ExtractObjects(node.Content...) + if err != nil { + return nil, err + } + objects = append(objects, children...) + case yaml.MappingNode: + objects = append(objects, &MapVariant{node: node}) + default: + return nil, fmt.Errorf("unhandled node kind %v", node.Kind) + } + } + return objects, nil +} + +func TypedObjectToMapVariant(v interface{}) (*MapVariant, error) { + // The built-in types only have json tags. We can't simply do ynode.Encode(v), + // since it use the lowercased field name by default if no yaml tag is specified. + // This affects both k8s built-in types (e.g. appsv1.Deployment) and any types + // that depends on built-in types (e.g. metav1.ObjectMeta, corev1.PodTemplate). + // To work around it, we rely on the json tags. We first convert v to + // map[string]interface{} through json and then convert it to ynode. + node, err := func() (*yaml.Node, error) { + j, err := json.Marshal(v) + if err != nil { + return nil, err + } + var m map[string]interface{} + if err = json.Unmarshal(j, &m); err != nil { + return nil, err + } + + node := &yaml.Node{} + if err = node.Encode(m); err != nil { + return nil, err + } + return node, nil + }() + if err != nil { + return nil, fmt.Errorf("unable to convert strong typed object to yaml node: %w", err) + } + + mv := &MapVariant{node: node} + mv.cleanupCreationTimestamp() + err = mv.sortFields() + return mv, err +} + +func TypedObjectToSliceVariant(v interface{}) (*sliceVariant, error) { + // The built-in types only have json tags. We can't simply do ynode.Encode(v), + // since it use the lowercased field name by default if no yaml tag is specified. + // This affects both k8s built-in types (e.g. appsv1.Deployment) and any types + // that depends on built-in types (e.g. metav1.ObjectMeta, corev1.PodTemplate). + // To work around it, we rely on the json tags. We first convert v to + // map[string]interface{} through json and then convert it to ynode. + node, err := func() (*yaml.Node, error) { + j, err := json.Marshal(v) + if err != nil { + return nil, err + } + var l []interface{} + if err = json.Unmarshal(j, &l); err != nil { + return nil, err + } + + node := &yaml.Node{} + if err = node.Encode(l); err != nil { + return nil, err + } + return node, nil + }() + if err != nil { + return nil, fmt.Errorf("unable to convert strong typed object to yaml node: %w", err) + } + + return &sliceVariant{node: node}, nil +} + +func MapVariantToTypedObject(mv *MapVariant, ptr interface{}) error { + if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { + return fmt.Errorf("ptr must be a pointer to an object") + } + + // The built-in types only have json tags. We can't simply do mv.Node().Decode(ptr), + // since it use the lowercased field name by default if no yaml tag is specified. + // This affects both k8s built-in types (e.g. appsv1.Deployment) and any types + // that depends on built-in types (e.g. metav1.ObjectMeta, corev1.PodTemplate). + // To work around it, we rely on the json tags. We first convert mv to json + // and then unmarshal it to ptr. + j, err := yaml.NewRNode(mv.Node()).MarshalJSON() + if err != nil { + return err + } + err = json.Unmarshal(j, ptr) + return err +} diff --git a/functions/go/configmap-generator/fn/io.go b/functions/go/configmap-generator/fn/io.go new file mode 100644 index 000000000..34d9c9b95 --- /dev/null +++ b/functions/go/configmap-generator/fn/io.go @@ -0,0 +1,74 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// byteReadWriter wraps kio.ByteReadWriter +type byteReadWriter struct { + kio.ByteReadWriter +} + +// Read decodes input bytes into a ResourceList +func (rw *byteReadWriter) Read() (*ResourceList, error) { + nodes, err := rw.ByteReadWriter.Read() + if err != nil { + return nil, err + } + var items KubeObjects + for _, n := range nodes { + obj, err := ParseKubeObject([]byte(n.MustString())) + if err != nil { + return nil, err + } + items = append(items, obj) + } + obj, err := ParseKubeObject([]byte(rw.ByteReadWriter.FunctionConfig.MustString())) + if err != nil { + return nil, err + } + return &ResourceList{ + Items: items, + FunctionConfig: obj, + }, nil +} + +// Write writes a ResourceList into bytes +func (rw *byteReadWriter) Write(rl *ResourceList) error { + if len(rl.Results) > 0 { + b, err := yaml.Marshal(rl.Results) + if err != nil { + return errors.Wrap(err) + } + y, err := yaml.Parse(string(b)) + if err != nil { + return errors.Wrap(err) + } + rw.Results = y + } + var nodes []*yaml.RNode + for _, item := range rl.Items { + node, err := yaml.Parse(item.String()) + if err != nil { + return err + } + nodes = append(nodes, node) + } + return rw.ByteReadWriter.Write(nodes) +} diff --git a/functions/go/configmap-generator/fn/log.go b/functions/go/configmap-generator/fn/log.go new file mode 100644 index 000000000..5d86389fd --- /dev/null +++ b/functions/go/configmap-generator/fn/log.go @@ -0,0 +1,28 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "os" +) + +func Log(in ...interface{}) { + fmt.Fprintln(os.Stderr, in...) +} + +func Logf(format string, in ...interface{}) { + fmt.Fprintf(os.Stderr, format, in...) +} diff --git a/functions/go/configmap-generator/fn/object.go b/functions/go/configmap-generator/fn/object.go new file mode 100644 index 000000000..e6f96f4b8 --- /dev/null +++ b/functions/go/configmap-generator/fn/object.go @@ -0,0 +1,1000 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "math" + "reflect" + "strconv" + "strings" + + v1 "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/api/kptfile/v1" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn/internal" + "sigs.k8s.io/kustomize/kyaml/kio/kioutil" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// KubeObject presents a k8s object. +type KubeObject struct { + SubObject +} + +// ParseKubeObject parses input byte slice to a KubeObject. +func ParseKubeObject(in []byte) (*KubeObject, error) { + doc, err := internal.ParseDoc(in) + if err != nil { + return nil, fmt.Errorf("failed to parse input bytes: %w", err) + } + objects, err := doc.Elements() + if err != nil { + return nil, fmt.Errorf("failed to extract objects: %w", err) + } + if len(objects) != 1 { + return nil, fmt.Errorf("expected exactly one object, got %d", len(objects)) + } + rlMap := objects[0] + return asKubeObject(rlMap), nil +} + +// GetOrDie gets the value for a nested field located by fields. A pointer must +// be passed in, and the value will be stored in ptr. If the field doesn't +// exist, the ptr will be set to nil. It will panic if it encounters any error. +func (o *SubObject) GetOrDie(ptr interface{}, fields ...string) { + _, err := o.Get(ptr, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// NestedBool returns the bool value, if the field exist and a potential error. +func (o *SubObject) NestedBool(fields ...string) (bool, bool, error) { + var val bool + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedBoolOrDie returns the bool value at `fields`. An empty string will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedBoolOrDie(fields ...string) bool { + val, _, err := o.NestedBool(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedString returns the string value, if the field exist and a potential error. +func (o *SubObject) NestedString(fields ...string) (string, bool, error) { + var val string + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedStringOrDie returns the string value at fields. An empty string will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedStringOrDie(fields ...string) string { + val, _, err := o.NestedString(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedFloat64 returns the float64 value, if the field exist and a potential error. +func (o *SubObject) NestedFloat64(fields ...string) (float64, bool, error) { + var val float64 + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedFloat64OrDie returns the string value at fields. 0 will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedFloat64OrDie(fields ...string) float64 { + val, _, err := o.NestedFloat64(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedInt64 returns the int64 value, if the field exist and a potential error. +func (o *SubObject) NestedInt64(fields ...string) (int64, bool, error) { + var val int64 + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedInt64OrDie returns the string value at fields. An empty string will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedInt64OrDie(fields ...string) int64 { + val, _, err := o.NestedInt64(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedSlice accepts a slice of `fields` which represents the path to the slice component and +// return a slice of SubObjects as the first return value; whether the component exists or +// not as the second return value, and errors as the third return value. +func (o *SubObject) NestedSlice(fields ...string) (SliceSubObjects, bool, error) { + var mapVariant *internal.MapVariant + if len(fields) > 1 { + m, found, err := o.obj.GetNestedMap(fields[:len(fields)-1]...) + if err != nil || !found { + return nil, found, err + } + mapVariant = m + } else { + mapVariant = o.obj + } + sliceVal, found, err := mapVariant.GetNestedSlice(fields[len(fields)-1]) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + if !found { + return nil, found, nil + } + objects, err := sliceVal.Elements() + if err != nil { + return nil, found, err + } + var val []*SubObject + for _, obj := range objects { + val = append(val, &SubObject{obj: obj}) + } + return val, true, nil +} + +// NestedSliceOrDie accepts a slice of `fields` which represents the path to the slice component and +// return a slice of SubObjects. +// - It returns nil if the fields does not exist. +// - It panics with errSubObjectFields error if the field is not a slice type. +func (o *SubObject) NestedSliceOrDie(fields ...string) SliceSubObjects { + val, _, err := o.NestedSlice(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedMap returns a map[string]string value of a nested field, false if not found and an error if not a map[string]string type. +func (o *SubObject) NestedStringMap(fields ...string) (map[string]string, bool, error) { + var variant map[string]string + found, err := o.Get(&variant, fields...) + if err != nil || !found { + return nil, found, err + } + return variant, found, err +} + +// NestedStringMapOrDie returns a map[string]string value of a nested field, if the fields does not exist, it returns +// empty map[string]string. It will panic if the fields are not map[string]string type. +func (o *SubObject) NestedStringMapOrDie(fields ...string) map[string]string { + val, _, err := o.NestedStringMap(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedMap returns a map[string]string value of a nested field, false if not found and an error if not a map[string]string type. +func (o *SubObject) NestedStringSlice(fields ...string) ([]string, bool, error) { + var variant []string + found, err := o.Get(&variant, fields...) + if err != nil || !found { + return nil, found, err + } + return variant, found, err +} + +// NestedMapOrDie returns a map[string]string value of a nested field. +// It will panic if the fields are not map[string]string type. +func (o *SubObject) NestedStringSliceOrDie(fields ...string) []string { + val, _, err := o.NestedStringSlice(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// RemoveNestedFieldOrDie removes the field located by fields if found. It will panic if it +// encounters any error. +func (o *SubObject) RemoveNestedFieldOrDie(fields ...string) { + if _, err := o.RemoveNestedField(fields...); err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// RemoveNestedField removes the field located by fields if found. It returns if the field +// is found and a potential error. +func (o *SubObject) RemoveNestedField(fields ...string) (bool, error) { + found, err := func() (bool, error) { + if o == nil { + return false, fmt.Errorf("the object doesn't exist") + } + return o.obj.RemoveNestedField(fields...) + }() + if err != nil { + return found, fmt.Errorf("unable to remove fields %v with error: %w", fields, err) + } + return found, nil +} + +// Get gets the value for a nested field located by fields. A pointer must be +// passed in, and the value will be stored in ptr. ptr can be a concrete type +// (e.g. string, []corev1.Container, []string, corev1.Pod, map[string]string) or +// a yaml.RNode. yaml.RNode should be used if you are dealing with comments that +// is more than what LineComment, HeadComment, SetLineComment and +// SetHeadComment can handle. It returns if the field is found and a +// potential error. +func (o *SubObject) Get(ptr interface{}, fields ...string) (bool, error) { + found, err := func() (bool, error) { + if o == nil { + return false, fmt.Errorf("the object doesn't exist") + } + if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { + return false, fmt.Errorf("ptr must be a pointer to an object") + } + + if rn, ok := ptr.(*yaml.RNode); ok { + val, found, err := o.obj.GetNestedValue(fields...) + if err != nil || !found { + return found, err + } + rn.SetYNode(val.Node()) + return found, err + } + + switch k := reflect.TypeOf(ptr).Elem().Kind(); k { + case reflect.Struct, reflect.Map: + m, found, err := o.obj.GetNestedMap(fields...) + if err != nil || !found { + return found, err + } + err = m.Node().Decode(ptr) + return found, err + case reflect.Slice: + s, found, err := o.obj.GetNestedSlice(fields...) + if err != nil || !found { + return found, err + } + err = s.Node().Decode(ptr) + return found, err + case reflect.String: + s, found, err := o.obj.GetNestedString(fields...) + if err != nil || !found { + return found, err + } + *(ptr.(*string)) = s + return found, nil + case reflect.Int, reflect.Int64: + i, found, err := o.obj.GetNestedInt(fields...) + if err != nil || !found { + return found, err + } + if k == reflect.Int { + *(ptr.(*int)) = i + } else if k == reflect.Int64 { + *(ptr.(*int64)) = int64(i) + } + return found, nil + case reflect.Float64: + f, found, err := o.obj.GetNestedFloat(fields...) + if err != nil || !found { + return found, err + } + *(ptr.(*float64)) = f + return found, nil + case reflect.Bool: + b, found, err := o.obj.GetNestedBool(fields...) + if err != nil || !found { + return found, err + } + *(ptr.(*bool)) = b + return found, nil + default: + return false, fmt.Errorf("unhandled kind %s", k) + } + }() + if err != nil { + return found, fmt.Errorf("unable to get fields %v as %T with error: %w", fields, ptr, err) + } + return found, nil +} + +// SetOrDie sets a nested field located by fields to the value provided as val. +// It will panic if it encounters any error. +func (o *SubObject) SetOrDie(val interface{}, fields ...string) { + if err := o.SetNestedField(val, fields...); err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// onLockedFields locks the SubObject fields which are expected for kpt internal use only. +func (o *SubObject) onLockedFields(val interface{}, fields ...string) error { + if o.hasUpstreamIdentifier(val, fields...) { + return ErrAttemptToTouchUpstreamIdentifier{} + } + return nil +} + +func (o *SubObject) SetSlice(objects SliceSubObjects, field string) error { + return o.SetNestedField(objects, field) +} + +// SetNestedField sets a nested field located by fields to the value provided as val. val +// should not be a yaml.RNode. If you want to deal with yaml.RNode, you should +// use Get method and modify the underlying yaml.Node. +func (o *SubObject) SetNestedField(val interface{}, fields ...string) error { + if err := o.onLockedFields(val, fields...); err != nil { + return err + } + err := func() error { + if val == nil { + return fmt.Errorf("the passed-in object must not be nil") + } + if o == nil { + return fmt.Errorf("the object doesn't exist") + } + if o.obj == nil { + o.obj = internal.NewMap(nil) + } + kind := reflect.ValueOf(val).Kind() + if kind == reflect.Ptr { + kind = reflect.TypeOf(val).Elem().Kind() + } + + switch kind { + case reflect.Struct, reflect.Map: + m, err := internal.TypedObjectToMapVariant(val) + if err != nil { + return err + } + return o.obj.SetNestedMap(m, fields...) + case reflect.Slice: + if valSliceSubObj, ok := val.(SliceSubObjects); ok { + s := internal.NewSliceVariant() + for _, element := range valSliceSubObj { + s.Add(element.obj) + } + return o.obj.SetNestedSlice(s, fields...) + } + s, err := internal.TypedObjectToSliceVariant(val) + if err != nil { + return err + } + return o.obj.SetNestedSlice(s, fields...) + case reflect.String: + var s string + switch val := val.(type) { + case string: + s = val + case *string: + s = *val + } + return o.obj.SetNestedString(s, fields...) + case reflect.Int, reflect.Int64: + var i int + switch val := val.(type) { + case int: + i = val + case *int: + i = *val + case int64: + i = int(val) + case *int64: + i = int(*val) + } + return o.obj.SetNestedInt(i, fields...) + case reflect.Float64: + var f float64 + switch val := val.(type) { + case float64: + f = val + case *float64: + f = *val + } + return o.obj.SetNestedFloat(f, fields...) + case reflect.Bool: + var b bool + switch val := val.(type) { + case bool: + b = val + case *bool: + b = *val + } + return o.obj.SetNestedBool(b, fields...) + default: + return fmt.Errorf("unhandled kind %s", kind) + } + }() + if err != nil { + return fmt.Errorf("unable to set %v at fields %v with error: %w", val, fields, err) + } + return nil +} + +// SetNestedIntOrDie sets the `fields` value to int `value`. It panics if the fields type is not int. +func (o *SubObject) SetNestedIntOrDie(value int, fields ...string) { + err := o.SetNestedInt(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedInt sets the `fields` value to int `value`. It returns error if the fields type is not int. +func (o *SubObject) SetNestedInt(value int, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedBoolOrDie sets the `fields` value to bool `value`. It panics if the fields type is not bool. +func (o *SubObject) SetNestedBoolOrDie(value bool, fields ...string) { + err := o.SetNestedBool(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedBool sets the `fields` value to bool `value`. It returns error if the fields type is not bool. +func (o *SubObject) SetNestedBool(value bool, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedStringOrDie sets the `fields` value to string `value`. It panics if the fields type is not string. +func (o *SubObject) SetNestedStringOrDie(value string, fields ...string) { + err := o.SetNestedString(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedStringOrDie sets the `fields` value to string `value`. It returns error if the fields type is not string. +func (o *SubObject) SetNestedString(value string, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedStringMapOrDie sets the `fields` value to map[string]string `value`. It panics if the fields type is not map[string]string. +func (o *SubObject) SetNestedStringMapOrDie(value map[string]string, fields ...string) { + err := o.SetNestedStringMap(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedStringMap sets the `fields` value to map[string]string `value`. It returns error if the fields type is not map[string]string. +func (o *SubObject) SetNestedStringMap(value map[string]string, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedStringSliceOrDie sets the `fields` value to []string `value`. It panics if the fields type is not []string. +func (o *SubObject) SetNestedStringSliceOrDie(value []string, fields ...string) { + err := o.SetNestedStringSlice(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedStringSlice sets the `fields` value to []string `value`. It returns error if the fields type is not []string. +func (o *SubObject) SetNestedStringSlice(value []string, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// LineComment returns the line comment, if the target field exist and a +// potential error. +func (o *KubeObject) LineComment(fields ...string) (string, bool, error) { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if !found || err != nil { + return "", found, err + } + return rn.YNode().LineComment, true, nil +} + +// HeadComment returns the head comment, if the target field exist and a +// potential error. +func (o *KubeObject) HeadComment(fields ...string) (string, bool, error) { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if !found || err != nil { + return "", found, err + } + return rn.YNode().HeadComment, true, nil +} + +func (o *KubeObject) SetLineComment(comment string, fields ...string) error { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if err != nil { + return err + } + if !found { + return fmt.Errorf("can't set line comment because the field doesn't exist") + } + rn.YNode().LineComment = comment + return nil +} + +func (o *KubeObject) SetHeadComment(comment string, fields ...string) error { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if err != nil { + return err + } + if !found { + return fmt.Errorf("can't set head comment because the field doesn't exist") + } + rn.YNode().HeadComment = comment + return nil +} + +// AsOrDie converts a KubeObject to the desired typed object. ptr must +// be a pointer to a typed object. It will panic if it encounters an error. +func (o *SubObject) AsOrDie(ptr interface{}) { + if err := o.As(ptr); err != nil { + panic(errSubObjectFields{fields: nil}) + } +} + +// As converts a KubeObject to the desired typed object. ptr must be +// a pointer to a typed object. +func (o *SubObject) As(ptr interface{}) error { + err := func() error { + if o == nil { + return fmt.Errorf("the object doesn't exist") + } + if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { + return fmt.Errorf("ptr must be a pointer to an object") + } + return internal.MapVariantToTypedObject(o.obj, ptr) + }() + if err != nil { + return fmt.Errorf("unable to convert object to %T with error: %w", ptr, err) + } + return nil +} + +// NewFromTypedObject construct a KubeObject from a typed object (e.g. corev1.Pod) +func NewFromTypedObject(v interface{}) (*KubeObject, error) { + kind := reflect.ValueOf(v).Kind() + if kind == reflect.Ptr { + kind = reflect.TypeOf(v).Elem().Kind() + } + var err error + var m *internal.MapVariant + switch kind { + case reflect.Struct, reflect.Map: + m, err = internal.TypedObjectToMapVariant(v) + case reflect.Slice: + return nil, fmt.Errorf("The typed object should be of a reflect.Struct or reflect.Map, got reflect.Slice") + } + if err != nil { + return nil, err + } + return asKubeObject(m), nil +} + +// String serializes the object in yaml format. +func (o *KubeObject) String() string { + doc := internal.NewDoc([]*yaml.Node{o.obj.Node()}...) + s, _ := doc.ToYAML() + return string(s) +} + +// ShortString provides a human readable information for the KubeObject Identifier in the form of GVKNN. +func (o *KubeObject) ShortString() string { + return fmt.Sprintf("Resource(apiVersion=%v, kind=%v, namespace=%v, name=%v)", + o.GetAPIVersion(), o.GetKind(), o.GetNamespace(), o.GetName()) +} + +// resourceIdentifier returns the resource identifier including apiVersion, kind, +// namespace and name. +func (o *KubeObject) resourceIdentifier() *yaml.ResourceIdentifier { + apiVersion := o.GetAPIVersion() + kind := o.GetKind() + name := o.GetName() + ns := o.GetNamespace() + return &yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: apiVersion, + Kind: kind, + }, + NameMeta: yaml.NameMeta{ + Name: name, + Namespace: ns, + }, + } +} + +// IsGVK compares the given group, version, and kind with KubeObject's apiVersion and Kind. +func (o *KubeObject) IsGVK(group, version, kind string) bool { + oGroup, oVersion := ParseGroupVersion(o.GetAPIVersion()) + if o.GetKind() != "" && kind != "" && o.GetKind() != kind { + return false + } + if oGroup != "" && group != "" && oGroup != group { + return false + } + if oVersion != "" && version != "" && oVersion != version { + return false + } + return true +} + +// IsLocalConfig checks the "config.kubernetes.io/local-config" field to tell +// whether a KRM resource will be skipped by `kpt live apply` or not. +func (o *KubeObject) IsLocalConfig() bool { + isLocalConfig := o.GetAnnotation(KptLocalConfig) + if isLocalConfig == "" || isLocalConfig == "false" { + return false + } + return true +} + +func (o *KubeObject) GetAPIVersion() string { + apiVersion, _, _ := o.obj.GetNestedString("apiVersion") + return apiVersion +} + +func (o *KubeObject) SetAPIVersion(apiVersion string) { + if err := o.obj.SetNestedString(apiVersion, "apiVersion"); err != nil { + panic(fmt.Errorf("cannot set apiVersion '%v': %v", apiVersion, err)) + } +} + +func (o *KubeObject) GetKind() string { + kind, _, _ := o.obj.GetNestedString("kind") + return kind +} + +func (o *KubeObject) SetKind(kind string) { + if err := o.SetNestedField(kind, "kind"); err != nil { + panic(fmt.Errorf("cannot set kind '%v': %v", kind, err)) + } +} + +func (o *KubeObject) GetName() string { + s, _, _ := o.obj.GetNestedString("metadata", "name") + return s +} + +func (o *KubeObject) SetName(name string) { + if err := o.SetNestedField(name, "metadata", "name"); err != nil { + panic(fmt.Errorf("cannot set metadata name '%v': %v", name, err)) + } +} + +func (o *KubeObject) GetNamespace() string { + s, _, _ := o.obj.GetNestedString("metadata", "namespace") + return s +} + +// IsNamespaceScoped tells whether a k8s resource is namespace scoped. If the KubeObject resource is a customized, it +// determines the namespace scope by checking whether `metadata.namespace` is set. +func (o *KubeObject) IsNamespaceScoped() bool { + tm := yaml.TypeMeta{Kind: o.GetKind(), APIVersion: o.GetAPIVersion()} + if nsScoped, ok := internal.PrecomputedIsNamespaceScoped[tm]; ok { + return nsScoped + } + // TODO(yuwenma): parse the resource openapi schema to know its scope status. + return o.HasNamespace() +} + +// IsClusterScoped tells whether a resource is cluster scoped. +func (o *KubeObject) IsClusterScoped() bool { + return !o.IsNamespaceScoped() +} + +func (o *KubeObject) HasNamespace() bool { + _, found, _ := o.obj.GetNestedString("metadata", "namespace") + return found +} + +func (o *KubeObject) SetNamespace(name string) { + if err := o.SetNestedField(name, "metadata", "namespace"); err != nil { + panic(fmt.Errorf("cannot set namespace '%v': %v", name, err)) + } +} + +func (o *KubeObject) SetAnnotation(k, v string) { + // Keep upstream-identifier untouched from users + if k == UpstreamIdentifier { + panic(ErrAttemptToTouchUpstreamIdentifier{}) + } + if err := o.SetNestedField(v, "metadata", "annotations", k); err != nil { + panic(fmt.Errorf("cannot set metadata annotations '%v': %v", k, err)) + } +} + +// GetAnnotations returns all annotations. +func (o *KubeObject) GetAnnotations() map[string]string { + v, _, _ := o.obj.GetNestedStringMap("metadata", "annotations") + return v +} + +// GetAnnotation returns one annotation with key k. +func (o *KubeObject) GetAnnotation(k string) string { + v, _, _ := o.obj.GetNestedString("metadata", "annotations", k) + return v +} + +// HasAnnotations returns whether the KubeObject has all the given annotations. +func (o *KubeObject) HasAnnotations(annotations map[string]string) bool { + kubeObjectLabels := o.GetAnnotations() + for k, v := range annotations { + kubeObjectValue, found := kubeObjectLabels[k] + if !found || kubeObjectValue != v { + return false + } + } + return true +} + +// RemoveAnnotationsIfEmpty removes the annotations field when it has zero annotations. +func (o *KubeObject) RemoveAnnotationsIfEmpty() error { + annotations, found, err := o.obj.GetNestedStringMap("metadata", "annotations") + if err != nil { + return err + } + if found && len(annotations) == 0 { + _, err = o.obj.RemoveNestedField("metadata", "annotations") + return err + } + return nil +} + +func (o *KubeObject) SetLabel(k, v string) { + if err := o.SetNestedField(v, "metadata", "labels", k); err != nil { + panic(fmt.Errorf("cannot set metadata labels '%v': %v", k, err)) + } +} + +// Label returns one label with key k. +func (o *KubeObject) GetLabel(k string) string { + v, _, _ := o.obj.GetNestedString("metadata", "labels", k) + return v +} + +// Labels returns all labels. +func (o *KubeObject) GetLabels() map[string]string { + v, _, _ := o.obj.GetNestedStringMap("metadata", "labels") + return v +} + +// HasLabels returns whether the KubeObject has all the given labels +func (o *KubeObject) HasLabels(labels map[string]string) bool { + kubeObjectLabels := o.GetLabels() + for k, v := range labels { + kubeObjectValue, found := kubeObjectLabels[k] + if !found || kubeObjectValue != v { + return false + } + } + return true +} + +func (o *KubeObject) PathAnnotation() string { + anno := o.GetAnnotation(kioutil.PathAnnotation) + return anno +} + +// IndexAnnotation return -1 if not found. +func (o *KubeObject) IndexAnnotation() int { + anno := o.GetAnnotation(kioutil.IndexAnnotation) + if anno == "" { + return -1 + } + i, _ := strconv.Atoi(anno) + return i +} + +// IdAnnotation return -1 if not found. +func (o *KubeObject) IdAnnotation() int { + anno := o.GetAnnotation(kioutil.IdAnnotation) + + if anno == "" { + return -1 + } + i, _ := strconv.Atoi(anno) + return i +} + +type KubeObjects []*KubeObject + +func (o KubeObjects) Len() int { return len(o) } +func (o KubeObjects) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o KubeObjects) Less(i, j int) bool { + idi := o[i].resourceIdentifier() + idj := o[j].resourceIdentifier() + idStrI := fmt.Sprintf("%s %s %s %s", idi.GetAPIVersion(), idi.GetKind(), idi.GetNamespace(), idi.GetName()) + idStrJ := fmt.Sprintf("%s %s %s %s", idj.GetAPIVersion(), idj.GetKind(), idj.GetNamespace(), idj.GetName()) + return idStrI < idStrJ +} + +func (o KubeObjects) String() string { + var elems []string + for _, obj := range o { + elems = append(elems, strings.TrimSpace(obj.String())) + } + return strings.Join(elems, "\n---\n") +} + +// Where will return the subset of objects in KubeObjects such that f(object) returns 'true'. +func (o KubeObjects) Where(f func(*KubeObject) bool) KubeObjects { + var result KubeObjects + for _, obj := range o { + if f(obj) { + result = append(result, obj) + } + } + return result +} + +// Not returns will return a function that returns the opposite of f(object), i.e. !f(object) +func Not(f func(*KubeObject) bool) func(o *KubeObject) bool { + return func(o *KubeObject) bool { + return !f(o) + } +} + +// WhereNot will return the subset of objects in KubeObjects such that f(object) returns 'false'. +// This is a shortcut for Where(Not(f)). +func (o KubeObjects) WhereNot(f func(o *KubeObject) bool) KubeObjects { + return o.Where(Not(f)) +} + +// IsGVK returns a function that checks if a KubeObject has a certain GVK. +func IsGVK(group, version, kind string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.IsGVK(group, version, kind) + } +} + +// GetRootKptfile returns the root Kptfile. Nested kpt packages can have multiple Kptfile files of the same GVKNN. +func (o KubeObjects) GetRootKptfile() *KubeObject { + kptfiles := o.Where(IsGVK(v1.KptFileGroup, v1.KptFileVersion, v1.KptFileKind)) + if len(kptfiles) == 0 { + return nil + } + minDepths := math.MaxInt32 + var rootKptfile *KubeObject + for _, kf := range kptfiles { + path := kf.GetAnnotation(PathAnnotation) + depths := len(strings.Split(path, "/")) + if depths <= minDepths { + minDepths = depths + rootKptfile = kf + } + } + return rootKptfile +} + +// IsName returns a function that checks if a KubeObject has a certain name. +func IsName(name string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.GetName() == name + } +} + +// IsNamespace returns a function that checks if a KubeObject has a certain namespace. +func IsNamespace(namespace string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.GetNamespace() == namespace + } +} + +// HasLabels returns a function that checks if a KubeObject has all the given labels. +func HasLabels(labels map[string]string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.HasLabels(labels) + } +} + +// HasAnnotations returns a function that checks if a KubeObject has all the given annotations. +func HasAnnotations(annotations map[string]string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.HasAnnotations(annotations) + } +} + +// IsMetaResource returns a function that checks if a KubeObject is a meta resource. For now +// this just includes the Kptfile +func IsMetaResource() func(*KubeObject) bool { + return IsGVK("kpt.dev", "v1", "Kptfile") +} + +func (o *KubeObject) IsEmpty() bool { + return yaml.IsYNodeEmptyMap(o.obj.Node()) +} + +func NewEmptyKubeObject() *KubeObject { + return &KubeObject{SubObject{internal.NewMap(nil)}} +} + +func asKubeObject(obj *internal.MapVariant) *KubeObject { + return &KubeObject{SubObject{obj}} +} + +func (o *KubeObject) node() *internal.MapVariant { + return o.obj +} + +func rnodeToKubeObject(rn *yaml.RNode) *KubeObject { + mapVariant := internal.NewMap(rn.YNode()) + return &KubeObject{SubObject{mapVariant}} +} + +// SubObject represents a map within a KubeObject +type SubObject struct { + obj *internal.MapVariant +} + +func (o *SubObject) UpsertMap(k string) *SubObject { + m := o.obj.UpsertMap(k) + return &SubObject{obj: m} +} + +// GetMap accepts a single key `k` whose value is expected to be a map. It returns +// the map in the form of a SubObject pointer. +// It panic with ErrSubObjectFields error if the field cannot be represented as a SubObject. +func (o *SubObject) GetMap(k string) *SubObject { + var variant yaml.RNode + found, err := o.Get(&variant, k) + if err != nil || !found { + return nil + } + return &SubObject{obj: internal.NewMap(variant.YNode())} +} + +// GetBool accepts a single key `k` whose value is expected to be a boolean. It returns +// the int value of the `k`. It panic with errSubObjectFields error if the +// field is not an integer type. +func (o *SubObject) GetBool(k string) bool { + return o.NestedBoolOrDie(k) +} + +// GetInt accepts a single key `k` whose value is expected to be an integer. It returns +// the int value of the `k`. It panic with errSubObjectFields error if the +// field is not an integer type. +func (o *SubObject) GetInt(k string) int64 { + return o.NestedInt64OrDie(k) +} + +// GetString accepts a single key `k` whose value is expected to be a string. It returns +// the value of the `k`. It panic with errSubObjectFields error if the +// field is not a string type. +func (o *SubObject) GetString(k string) string { + return o.NestedStringOrDie(k) +} + +// GetSlice accepts a single key `k` whose value is expected to be a slice. It returns +// the value as a slice of SubObject. It panic with errSubObjectFields error if the +// field is not a slice type. +func (o *SubObject) GetSlice(k string) SliceSubObjects { + return o.NestedSliceOrDie(k) +} + +type SliceSubObjects []*SubObject + +// MarshalJSON provides the custom encoding format for encode.json. This is used +// when KubeObject `Set` a slice of SubObjects. +func (s *SliceSubObjects) MarshalJSON() ([]byte, error) { + node := &yaml.Node{Kind: yaml.SequenceNode} + for _, subObject := range *s { + node.Content = append(node.Content, subObject.obj.Node()) + } + return yaml.NewRNode(node).MarshalJSON() +} diff --git a/functions/go/configmap-generator/fn/origin.go b/functions/go/configmap-generator/fn/origin.go new file mode 100644 index 000000000..a84d33d40 --- /dev/null +++ b/functions/go/configmap-generator/fn/origin.go @@ -0,0 +1,138 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "reflect" + "regexp" + "strings" +) + +const ( + // upstreamIdentifierRegexPattern provides the rough regex to parse a upstream-identiifier annotation. + // "group" should be a domain name. We accept empty string for kubernetes core v1 resources. + // "kind" should be the resource type with initial in capitals. + // "namespace" should follow RFC 1123 Label Names. We accept "~C~ for cluster-scoped resource or unknown scope resources. + // "name" should follow RFC 1123 Label Names https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names + upstreamIdentifierRegexPattern = `(?P[a-z0-9-.]*)\|(?P[A-Z][a-zA-Z0-9]*)\|(?P[a-z0-9-]{1,63}|~C)\|(?P[a-z0-9-]{1,63})` + upstreamIdentifierFormat = "|||" + regexPatternGroup = "group" + regexPatternKind = "kind" + regexPatterNamespace = "namespace" + regexPatternName = "name" +) + +type ResourceIdentifier struct { + Group string + Version string + Kind string + Name string + Namespace string +} + +func (r *ResourceIdentifier) String() string { + return fmt.Sprintf("%v|%v|%v|%v", r.Group, r.Kind, r.Namespace, r.Name) +} + +// hasUpstreamIdentifier determines whether the args are touching the kpt only annotation "internal.kpt.dev/upstream-identifier" +func (o *SubObject) hasUpstreamIdentifier(val interface{}, fields ...string) bool { + kind := reflect.ValueOf(val).Kind() + if kind == reflect.Ptr { + kind = reflect.TypeOf(val).Elem().Kind() + } + switch kind { + case reflect.String: + if fields[len(fields)-1] == UpstreamIdentifier { + return true + } + case reflect.Map: + if fields[len(fields)-1] == "annotations" { + for _, key := range reflect.ValueOf(val).MapKeys() { + if key.String() == UpstreamIdentifier { + return true + } + } + } + } + return false +} + +func (o *KubeObject) effectiveNamespace() string { + if o.HasNamespace() { + return o.GetNamespace() + } + if o.IsNamespaceScoped() { + return DefaultNamespace + } + return UnknownNamespace +} + +// GetId gets the Group, Kind, Namespace and Name as the ResourceIdentifier. +func (o *KubeObject) GetId() *ResourceIdentifier { + group, _ := ParseGroupVersion(o.GetAPIVersion()) + return &ResourceIdentifier{ + Group: group, + Kind: o.GetKind(), + Namespace: o.effectiveNamespace(), + Name: o.GetName(), + } +} + +func parseUpstreamIdentifier(upstreamId string) *ResourceIdentifier { + upstreamId = strings.TrimSpace(upstreamId) + r := regexp.MustCompile(upstreamIdentifierRegexPattern) + match := r.FindStringSubmatch(upstreamId) + if match == nil { + panic(ErrInternalAnnotation{Message: fmt.Sprintf("annotation %v: %v is in bad format. expect %q", + UpstreamIdentifier, upstreamId, upstreamIdentifierFormat)}) + } + matchGroups := make(map[string]string) + for i, name := range r.SubexpNames() { + if i > 0 && i <= len(match) { + matchGroups[name] = match[i] + } + } + return &ResourceIdentifier{ + Group: matchGroups[regexPatternGroup], + Kind: matchGroups[regexPatternKind], + Namespace: matchGroups[regexPatterNamespace], + Name: matchGroups[regexPatternName], + } +} + +// GetOriginId provides the `ResourceIdentifier` to identify the upstream origin of a KRM resource. +// This origin is generated and maintained by kpt pkg management and is stored in the `internal.kpt.dev/upstream-identiifer` annotation. +// If a resource does not have an upstream origin, we use its current meta resource ID instead. +func (o *KubeObject) GetOriginId() *ResourceIdentifier { + upstreamId := o.GetAnnotation(UpstreamIdentifier) + if upstreamId != "" { + return parseUpstreamIdentifier(upstreamId) + } + return o.GetId() +} + +// HasUpstreamOrigin tells whether a resource is sourced from an upstream package resource. +func (o *KubeObject) HasUpstreamOrigin() bool { + upstreamId := o.GetAnnotation(UpstreamIdentifier) + return upstreamId != "" +} + +// ParseGroupVersion parses a "apiVersion" to get the "group" and "version" values. +func ParseGroupVersion(apiVersion string) (group, version string) { + if i := strings.Index(apiVersion, "/"); i > -1 { + return apiVersion[:i], apiVersion[i+1:] + } + return "", apiVersion +} diff --git a/functions/go/configmap-generator/fn/resourcelist.go b/functions/go/configmap-generator/fn/resourcelist.go new file mode 100644 index 000000000..f7bcf1f32 --- /dev/null +++ b/functions/go/configmap-generator/fn/resourcelist.go @@ -0,0 +1,324 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fn + +import ( + "fmt" + "reflect" + "sort" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn/internal" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// ResourceList is a Kubernetes list type used as the primary data interchange format +// in the Configuration Functions Specification: +// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md +// This framework facilitates building functions that receive and emit ResourceLists, +// as required by the specification. +type ResourceList struct { + // Items is the ResourceList.items input and output value. + // + // e.g. given the function input: + // + // kind: ResourceList + // items: + // - kind: Deployment + // ... + // - kind: Service + // ... + // + // Items will be a slice containing the Deployment and Service resources + // Mutating functions will alter this field during processing. + // This field is required. + Items KubeObjects `yaml:"items" json:"items"` + + // FunctionConfig is the ResourceList.functionConfig input value. + // + // e.g. given the input: + // + // kind: ResourceList + // functionConfig: + // kind: Example + // spec: + // foo: var + // + // FunctionConfig will contain the RNodes for the Example: + // kind: Example + // spec: + // foo: var + FunctionConfig *KubeObject `yaml:"functionConfig,omitempty" json:"functionConfig,omitempty"` + + // Results is ResourceList.results output value. + // Validating functions can optionally use this field to communicate structured + // validation error data to downstream functions. + Results Results `yaml:"results,omitempty" json:"results,omitempty"` +} + +// CheckResourceDuplication checks the GVKNN of resourceList.items to make sure they are unique. It returns errors if +// found more than one resource having the same GVKNN. +func CheckResourceDuplication(rl *ResourceList) error { + idMap := map[yaml.ResourceIdentifier]struct{}{} + for _, obj := range rl.Items { + id := obj.resourceIdentifier() + if _, ok := idMap[*id]; ok { + return fmt.Errorf("duplicate Resource(apiVersion=%v, kind=%v, Namespace=%v, Name=%v)", + obj.GetAPIVersion(), obj.GetKind(), obj.GetNamespace(), obj.GetName()) + } + idMap[*id] = struct{}{} + } + return nil +} + +// ParseResourceList parses a ResourceList from the input byte array. This function can be used to parse either KRM fn input +// or KRM fn output +func ParseResourceList(in []byte) (*ResourceList, error) { + rl := &ResourceList{} + rlObj, err := ParseKubeObject(in) + if err != nil { + return nil, fmt.Errorf("failed to parse input bytes: %w", err) + } + if rlObj.GetKind() != kio.ResourceListKind { + return nil, fmt.Errorf("input was of unexpected kind %q; expected ResourceList", rlObj.GetKind()) + } + // Parse FunctionConfig. FunctionConfig can be empty, e.g. `kubeval` fn does not require a FunctionConfig. + fc, found, err := rlObj.obj.GetNestedMap("functionConfig") + if err != nil { + return nil, fmt.Errorf("failed when tried to get functionConfig: %w", err) + } + if found { + rl.FunctionConfig = asKubeObject(fc) + } else { + rl.FunctionConfig = NewEmptyKubeObject() + } + + // Parse Items. Items can be empty, e.g. an input ResourceList for a generator function may not have items. + items, found, err := rlObj.obj.GetNestedSlice("items") + if err != nil { + return nil, fmt.Errorf("failed when tried to get items: %w", err) + } + if found { + objectItems, err := items.Elements() + if err != nil { + return nil, fmt.Errorf("failed to extract objects from items: %w", err) + } + for i := range objectItems { + rl.Items = append(rl.Items, asKubeObject(objectItems[i])) + } + } + + // Parse Results. Results can be empty. + res, found, err := rlObj.obj.GetNestedSlice("results") + if err != nil { + return nil, fmt.Errorf("failed when tried to get results: %w", err) + } + if found { + var results Results + err = res.Node().Decode(&results) + if err != nil { + return nil, fmt.Errorf("failed to decode results: %w", err) + } + rl.Results = results + } + return rl, nil +} + +// toYNode converts the ResourceList to the yaml.Node representation. +func (rl *ResourceList) toYNode() (*yaml.Node, error) { + reMap := internal.NewMap(nil) + reObj := &KubeObject{SubObject{reMap}} + reObj.SetAPIVersion(kio.ResourceListAPIVersion) + reObj.SetKind(kio.ResourceListKind) + + if rl.Items != nil && len(rl.Items) > 0 { + itemsSlice := internal.NewSliceVariant() + for i := range rl.Items { + itemsSlice.Add(rl.Items[i].node()) + } + if err := reMap.SetNestedSlice(itemsSlice, "items"); err != nil { + return nil, err + } + } + if !rl.FunctionConfig.IsEmpty() { + if err := reMap.SetNestedMap(rl.FunctionConfig.node(), "functionConfig"); err != nil { + return nil, err + } + } + + if rl.Results != nil && len(rl.Results) > 0 { + resultsSlice := internal.NewSliceVariant() + for _, result := range rl.Results { + mv, err := internal.TypedObjectToMapVariant(result) + if err != nil { + return nil, err + } + resultsSlice.Add(mv) + } + if err := reMap.SetNestedSlice(resultsSlice, "results"); err != nil { + return nil, err + } + } + + return reMap.Node(), nil +} + +// ToYAML converts the ResourceList to yaml. +func (rl *ResourceList) ToYAML() ([]byte, error) { + // Sort the resources first. + rl.Sort() + ynode, err := rl.toYNode() + if err != nil { + return nil, err + } + doc := internal.NewDoc([]*yaml.Node{ynode}...) + return doc.ToYAML() +} + +// Sort sorts the ResourceList.items by apiVersion, kind, namespace and name. +func (rl *ResourceList) Sort() { + sort.Sort(rl.Items) +} + +// UpsertObjectToItems adds an object to ResourceList.items. The input object can +// be a KubeObject or any typed object (e.g. corev1.Pod). +func (rl *ResourceList) UpsertObjectToItems(obj interface{}, checkExistence func(obj, another *KubeObject) bool, replaceIfAlreadyExist bool) error { + if checkExistence == nil { + checkExistence = func(obj, another *KubeObject) bool { + ri1 := obj.resourceIdentifier() + ri2 := another.resourceIdentifier() + return reflect.DeepEqual(ri1, ri2) + } + } + + var ko *KubeObject + switch obj := obj.(type) { + case KubeObject: + ko = &obj + case *KubeObject: + ko = obj + case yaml.RNode: + ko = rnodeToKubeObject(&obj) + case *yaml.RNode: + ko = rnodeToKubeObject(obj) + case yaml.Node: + ko = rnodeToKubeObject(yaml.NewRNode(&obj)) + case *yaml.Node: + ko = rnodeToKubeObject(yaml.NewRNode(obj)) + default: + var err error + ko, err = NewFromTypedObject(obj) + if err != nil { + return err + } + } + + idx := -1 + for i, item := range rl.Items { + if checkExistence(ko, item) { + idx = i + break + } + } + if idx == -1 { + rl.Items = append(rl.Items, ko) + } else if replaceIfAlreadyExist { + rl.Items[idx] = ko + } + return nil +} + +func (rl *ResourceList) LogResult(err error) { + // If the error is not a Results type, we wrap the error as a Result. + if err == nil { + return + } + switch result := err.(type) { + case Results: + rl.Results = append(rl.Results, result...) + case Result: + rl.Results = append(rl.Results, &result) + case *Result: + rl.Results = append(rl.Results, result) + default: + rl.Results = append(rl.Results, ErrorResult(err)) + } +} + +// ResourceListProcessor is implemented by configuration functions built with this framework +// to conform to the Configuration Functions Specification: +// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md +type ResourceListProcessor interface { + Process(rl *ResourceList) (bool, error) +} + +// ResourceListProcessorFunc converts a compatible function to a ResourceListProcessor. +type ResourceListProcessorFunc func(rl *ResourceList) (bool, error) + +func (p ResourceListProcessorFunc) Process(rl *ResourceList) (bool, error) { + return p(rl) +} + +// Chain chains a list of ResourceListProcessor as a single ResourceListProcessor. +func Chain(processors ...ResourceListProcessor) ResourceListProcessor { + return ResourceListProcessorFunc(func(rl *ResourceList) (bool, error) { + success := true + for _, processor := range processors { + s, err := processor.Process(rl) + if !s { + success = false + } + if err != nil { + return false, err + } + } + return success, nil + }) +} + +// ChainFunctions chains a list of ResourceListProcessorFunc as a single +// ResourceListProcessorFunc. +func ChainFunctions(functions ...ResourceListProcessorFunc) ResourceListProcessorFunc { + return func(rl *ResourceList) (bool, error) { + success := true + for _, fn := range functions { + s, err := fn(rl) + if !s { + success = false + } + if err != nil { + return false, err + } + } + return success, nil + } +} + +// ApplyFnBySelector iterates through every object in ResourceList.items, and if +// it satisfies the selector, fn will be applied on it. +func ApplyFnBySelector(rl *ResourceList, selector func(obj *KubeObject) bool, fn func(obj *KubeObject) error) error { + var results Results + for i, obj := range rl.Items { + if !selector(obj) { + continue + } + err := fn(rl.Items[i]) + if err == nil { + continue + } + switch te := err.(type) { + case Results: + results = append(results, te...) + case *Result: + results = append(results, te) + default: + results = append(results, ErrorResult(err)) + } + } + if len(results) > 0 { + rl.Results = results + return results + } + return nil +} diff --git a/functions/go/configmap-generator/fn/result.go b/functions/go/configmap-generator/fn/result.go new file mode 100644 index 000000000..fa6db622a --- /dev/null +++ b/functions/go/configmap-generator/fn/result.go @@ -0,0 +1,299 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "sort" + "strings" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Context provides a series of functions to add `Result` to `ResourceList.Results`. +type Context struct { + results *Results +} + +func (c *Context) Result(message string, severity Severity) { + *c.results = append(*c.results, &Result{Message: message, Severity: severity}) +} + +// AddErrResultAndDie adds an Error `Result` and terminates the KRM function run. `KubeObject` can be nil. +func (c *Context) ResultErrAndDie(message string, o *KubeObject) { + c.ResultErr(message, o) + panic(errResultEnd{obj: o, message: message}) +} + +// AddErrResult adds an Error `Result` and continues the KRM function run. `KubeObject` can be nil. +func (c *Context) ResultErr(message string, o *KubeObject) { + var r *Result + if o != nil { + r = ConfigObjectResult(message, o, Error) + } else { + r = GeneralResult(message, Error) + } + *c.results = append(*c.results, r) +} + +// AddErrResult adds an Info `Result`. `KubeObject` can be nil. +func (c *Context) ResultInfo(message string, o *KubeObject) { + var r *Result + if o != nil { + r = ConfigObjectResult(message, o, Info) + } else { + r = GeneralResult(message, Info) + } + *c.results = append(*c.results, r) +} + +// AddErrResult adds an Info `Result`. `KubeObject` can be nil. +func (c *Context) ResultWarn(message string, o *KubeObject) { + var r *Result + if o != nil { + r = ConfigObjectResult(message, o, Warning) + } else { + r = GeneralResult(message, Info) + } + *c.results = append(*c.results, r) +} + +// Severity indicates the severity of the Result +type Severity string + +const ( + // Error indicates the result is an error. Will cause the function to exit non-0. + Error Severity = "error" + // Warning indicates the result is a warning + Warning Severity = "warning" + // Info indicates the result is an informative message + Info Severity = "info" +) + +// Result defines a validation result +type Result struct { + // Message is a human readable message. This field is required. + Message string `yaml:"message,omitempty" json:"message,omitempty"` + + // Severity is the severity of this result + Severity Severity `yaml:"severity,omitempty" json:"severity,omitempty"` + + // ResourceRef is a reference to a resource. + // Required fields: apiVersion, kind, name. + ResourceRef *yaml.ResourceIdentifier `yaml:"resourceRef,omitempty" json:"resourceRef,omitempty"` + + // Field is a reference to the field in a resource this result refers to + Field *Field `yaml:"field,omitempty" json:"field,omitempty"` + + // File references a file containing the resource this result refers to + File *File `yaml:"file,omitempty" json:"file,omitempty"` + + // Tags is an unstructured key value map stored with a result that may be set + // by external tools to store and retrieve arbitrary metadata + Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"` +} + +func (i Result) Error() string { + return (i).String() +} + +// String provides a human-readable message for the result item +func (i Result) String() string { + identifier := i.ResourceRef + var idStringList []string + if identifier != nil { + if identifier.APIVersion != "" { + idStringList = append(idStringList, identifier.APIVersion) + } + if identifier.Kind != "" { + idStringList = append(idStringList, identifier.Kind) + } + if identifier.Namespace != "" { + idStringList = append(idStringList, identifier.Namespace) + } + if identifier.Name != "" { + idStringList = append(idStringList, identifier.Name) + } + } + formatString := "[%s]" + severity := i.Severity + // We default Severity to Info when converting a result to a message. + if i.Severity == "" { + severity = Info + } + list := []interface{}{severity} + if len(idStringList) > 0 { + formatString += " %s" + list = append(list, strings.Join(idStringList, "/")) + } + if i.Field != nil { + formatString += " %s" + list = append(list, i.Field.Path) + } + formatString += ": %s" + list = append(list, i.Message) + return fmt.Sprintf(formatString, list...) +} + +// File references a file containing a resource +type File struct { + // Path is relative path to the file containing the resource. + // This field is required. + Path string `yaml:"path,omitempty" json:"path,omitempty"` + + // Index is the index into the file containing the resource + // (i.e. if there are multiple resources in a single file) + Index int `yaml:"index,omitempty" json:"index,omitempty"` +} + +// Field references a field in a resource +type Field struct { + // Path is the field path. This field is required. + Path string `yaml:"path,omitempty" json:"path,omitempty"` + + // CurrentValue is the current field value + CurrentValue interface{} `yaml:"currentValue,omitempty" json:"currentValue,omitempty"` + + // ProposedValue is the proposed value of the field to fix an issue. + ProposedValue interface{} `yaml:"proposedValue,omitempty" json:"proposedValue,omitempty"` +} + +type Results []*Result + +// Error enables Results to be returned as an error +func (e Results) Error() string { + var msgs []string + for _, i := range e { + msgs = append(msgs, i.String()) + } + return strings.Join(msgs, "\n\n") +} + +// ExitCode provides the exit code based on the result's severity +func (e Results) ExitCode() int { + for _, i := range e { + if i.Severity == Error { + return 1 + } + } + return 0 +} + +// Sort performs an in place stable sort of Results +func (e Results) Sort() { + sort.SliceStable(e, func(i, j int) bool { + if fileLess(e, i, j) != 0 { + return fileLess(e, i, j) < 0 + } + if severityLess(e, i, j) != 0 { + return severityLess(e, i, j) < 0 + } + return resultToString(*e[i]) < resultToString(*e[j]) + }) +} + +func severityLess(items Results, i, j int) int { + severityToNumber := map[Severity]int{ + Error: 0, + Warning: 1, + Info: 2, + } + + severityLevelI, found := severityToNumber[items[i].Severity] + if !found { + severityLevelI = 3 + } + severityLevelJ, found := severityToNumber[items[j].Severity] + if !found { + severityLevelJ = 3 + } + return severityLevelI - severityLevelJ +} + +func fileLess(items Results, i, j int) int { + var fileI, fileJ File + if items[i].File == nil { + fileI = File{} + } else { + fileI = *items[i].File + } + if items[j].File == nil { + fileJ = File{} + } else { + fileJ = *items[j].File + } + if fileI.Path != fileJ.Path { + if fileI.Path < fileJ.Path { + return -1 + } + return 1 + } + return fileI.Index - fileJ.Index +} + +func resultToString(item Result) string { + return fmt.Sprintf("resource-ref:%s,field:%s,message:%s", + item.ResourceRef, item.Field, item.Message) +} + +func ErrorConfigFileResult(err error, path string) *Result { + return ConfigFileResult(err.Error(), path, Error) +} + +func ConfigFileResult(msg, path string, severity Severity) *Result { + return &Result{ + Message: msg, + Severity: severity, + File: &File{ + Path: path, + }, + } +} + +func ErrorResult(err error) *Result { + return GeneralResult(err.Error(), Error) +} + +func GeneralResult(msg string, severity Severity) *Result { + return &Result{ + Message: msg, + Severity: severity, + } +} + +func ErrorConfigObjectResult(err error, obj *KubeObject) *Result { + return ConfigObjectResult(err.Error(), obj, Error) +} + +func ConfigObjectResult(msg string, obj *KubeObject, severity Severity) *Result { + return &Result{ + Message: msg, + Severity: severity, + ResourceRef: &yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: obj.GetAPIVersion(), + Kind: obj.GetKind(), + }, + NameMeta: yaml.NameMeta{ + Name: obj.GetName(), + Namespace: obj.GetNamespace(), + }, + }, + File: &File{ + Path: obj.PathAnnotation(), + Index: obj.IndexAnnotation(), + }, + } +} diff --git a/functions/go/configmap-generator/fn/run.go b/functions/go/configmap-generator/fn/run.go new file mode 100644 index 000000000..28ad4077f --- /dev/null +++ b/functions/go/configmap-generator/fn/run.go @@ -0,0 +1,155 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "io" + "io/ioutil" + "os" + + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +// AsMain evaluates the ResourceList from STDIN to STDOUT. +// `input` can be +// - a `ResourceListProcessor` which implements `Process` method +// - a function `Runner` which implements `Run` method +func AsMain(input interface{}) error { + err := func() error { + var p ResourceListProcessor + switch input := input.(type) { + case Runner: + p = runnerProcessor{fnRunner: input} + case *Runner: + p = runnerProcessor{fnRunner: *input} + case Generator: + p = generatorProcessor{fnGenerator: input} + case *Generator: + p = generatorProcessor{fnGenerator: *input} + case ResourceListProcessorFunc: + p = input + default: + return fmt.Errorf("unknown input type %T", input) + } + in, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return fmt.Errorf("unable to read from stdin: %v", err) + } + out, err := Run(p, in) + // If there is an error, we don't return the error immediately. + // We write out to stdout before returning any error. + _, outErr := os.Stdout.Write(out) + if outErr != nil { + return outErr + } + return err + }() + if err != nil { + Logf("failed to evaluate function: %v", err) + } + return err +} + +// Run evaluates the function. input must be a resourceList in yaml format. An +// updated resourceList will be returned. +func Run(p ResourceListProcessor, input []byte) (out []byte, err error) { + rl, err := ParseResourceList(input) + if err != nil { + return nil, err + } + defer func() { + // if we run into a panic, we still need to log the error to Results, + // and return the ResourceList and error. + v := recover() + if v != nil { + switch t := v.(type) { + case errKubeObjectFields: + err = &t + case *errKubeObjectFields: + err = t + case errSubObjectFields: + err = &t + case *errSubObjectFields: + err = t + case errResultEnd: + err = &t + case *errResultEnd: + err = t + case ErrAttemptToTouchUpstreamIdentifier: + err = &t + case *ErrAttemptToTouchUpstreamIdentifier: + err = t + case ErrInternalAnnotation: + err = &t + case *ErrInternalAnnotation: + err = t + default: + panic(v) + } + rl.LogResult(err) + out, _ = rl.ToYAML() + } + }() + success, fnErr := p.Process(rl) + out, yamlErr := rl.ToYAML() + if yamlErr != nil { + return out, yamlErr + } + if fnErr != nil { + return out, fnErr + } + if !success { + return out, fmt.Errorf("error: function failure") + } + return out, nil +} + +func Execute(p ResourceListProcessor, r io.Reader, w io.Writer) error { + rw := &byteReadWriter{ + kio.ByteReadWriter{ + Reader: r, + Writer: w, + // We should not set the id annotation in the function, since we should not + // overwrite what the orchestrator set. + OmitReaderAnnotations: true, + // We should not remove the id annotations in the function, since the + // orchestrator (e.g. kpt) may need them. + KeepReaderAnnotations: true, + }, + } + return execute(p, rw) +} + +func execute(p ResourceListProcessor, rw *byteReadWriter) error { + // Read the input + rl, err := rw.Read() + if err != nil { + return errors.WrapPrefixf(err, "failed to read ResourceList input") + } + success, fnErr := p.Process(rl) + // Write the output + if err := rw.Write(rl); err != nil { + return errors.WrapPrefixf(err, "failed to write ResourceList output") + } + if fnErr != nil { + return fnErr + } + if !success { + return fmt.Errorf("error: function failure") + } + return nil +} diff --git a/functions/go/configmap-generator/fn/runnerProcessor.go b/functions/go/configmap-generator/fn/runnerProcessor.go new file mode 100644 index 000000000..14dc77b4f --- /dev/null +++ b/functions/go/configmap-generator/fn/runnerProcessor.go @@ -0,0 +1,52 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "reflect" +) + +type runnerProcessor struct { + fnRunner Runner +} + +func (r runnerProcessor) Process(rl *ResourceList) (bool, error) { + ctx := &Context{results: &rl.Results} + r.config(ctx, rl.FunctionConfig) + r.fnRunner.Run(ctx, rl.FunctionConfig, rl.Items) + return true, nil +} + +func (r *runnerProcessor) config(ctx *Context, o *KubeObject) { + fnName := reflect.ValueOf(r.fnRunner).Elem().Type().Name() + switch true { + case o.IsEmpty(): + ctx.Result("`FunctionConfig` is not given", Info) + case o.IsGVK("", "v1", "ConfigMap"): + data := o.NestedStringMapOrDie("data") + fnRunnerElem := reflect.ValueOf(r.fnRunner).Elem() + for i := 0; i < fnRunnerElem.NumField(); i++ { + if fnRunnerElem.Field(i).Kind() == reflect.Map { + fnRunnerElem.Field(i).Set(reflect.ValueOf(data)) + break + } + } + case o.IsGVK("fn.kpt.dev", "v1alpha1", fnName): + o.AsOrDie(r.fnRunner) + default: + ctx.ResultErrAndDie(fmt.Sprintf("unknown FunctionConfig `%v`, expect %v", o.GetKind(), fnName), o) + } +} diff --git a/functions/go/configmap-generator/generator/generator.go b/functions/go/configmap-generator/generator/generator.go new file mode 100644 index 000000000..51ba3ac73 --- /dev/null +++ b/functions/go/configmap-generator/generator/generator.go @@ -0,0 +1,190 @@ +package generator + +import ( + "fmt" + "strings" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn" + generator "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/native-config-adaptor/generator" + corev1 "k8s.io/api/core/v1" +) + +type BuiltinGeneratorKind string + +const NativeConfigAdaptor BuiltinGeneratorKind = "NativeConfigAdaptor" + +var BuiltinGenerators = map[BuiltinGeneratorKind]fn.Runner{ + NativeConfigAdaptor: nil, + // ... others +} + +var _ fn.Generator = &ConfigMapGenerator{} + +type ConfigMapGenerator struct { + Spec ConfigMapGeneratorSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +type ConfigMapGeneratorSpec struct { + Source []*SourceObjectReference `json:"source,omitempty" yaml:"source,omitempty"` +} + +type SourceObjectReference struct { + corev1.TypedLocalObjectReference `json:",inline,omitempty" yaml:",inline,omitempty"` + generator.NativeConfigSource `json:",inline,omitempty" yaml:",inline,omitempty"` +} + +func (r *ConfigMapGenerator) Generate(ctx *fn.Context, functionConfig *fn.KubeObject, items fn.KubeObjects) fn.KubeObjects { + noConfigMapItemList := items.WhereNot(func(o *fn.KubeObject) bool { + return o.GetKind() == "ConfigMap" && (o.GetAnnotation(fn.GeneratorIdentifier) == functionConfig.GetId().String() || o.GetAnnotation(fn.GeneratorBuiltinIdentifier) == functionConfig.GetId().String()) + }) + previousGeneratedConfigMapAndNonKRM := items.Where(func(o *fn.KubeObject) bool { + if o.GetKind() == "ConfigMap" { + return o.GetAnnotation(fn.GeneratorIdentifier) == functionConfig.GetId().String() || o.GetAnnotation(fn.GeneratorBuiltinIdentifier) == functionConfig.GetId().String() + } + if o.GetKind() == fn.NonKrmKind { + return true + } + return false + }) + rawConfigMapName := "" + var newlyGeneratedConfigMaps, newItemsList fn.KubeObjects + data := map[string]string{} + var nativeFnConfig *fn.KubeObject + for _, source := range r.Spec.Source { + if source.LocalFile == "" && source.LocalFileRef == "" { + ctx.ResultErrAndDie("required either `spec.source.localFilePath` or `spec.source.localFileRef`", functionConfig) + } + // configmap generator always expects a ConfigMap object from NativeConfigAdaptor, not custom typed object. + source.AsConfigMap = true + if source.Kind != "" { + if source.Kind != string(NativeConfigAdaptor) { + ctx.ResultWarn(fmt.Sprintf("unknown generator type %s", r.Spec.Source), functionConfig) + return nil + } + nativeFnConfig = SelectNativeFnConfig(ctx, source, items) + if nativeFnConfig == nil { + continue + } + } else { + var err error + nativeFnConfig, err = generator.NewFromSource(&source.NativeConfigSource) + if err != nil { + ctx.ResultErrAndDie(fmt.Sprintf("bad source: %v", err), functionConfig) + } + } + nativeFnConfig.SetName(functionConfig.GetName()) + nativeConfigAdaptor := generator.NewNativeConfigAdaptor(nativeFnConfig) + newlyGeneratedConfigMaps = nativeConfigAdaptor.Generate(ctx, nativeFnConfig, previousGeneratedConfigMapAndNonKRM) + if source.Name != "" { + nativeFnConfig.SetName(source.Name) + } + if newlyGeneratedConfigMaps == nil || len(newlyGeneratedConfigMaps) == 0 { + ctx.ResultErr("no ConfigMap generated from native-config-adaptor", nativeFnConfig) + continue + } + var rawConfigMap, canonicalConfigMap *fn.KubeObject + for _, newConfigMap := range newlyGeneratedConfigMaps { + if strings.HasSuffix(newConfigMap.GetName(), "-internal") { + canonicalConfigMap = newConfigMap + } else { + rawConfigMap = newConfigMap + } + } + // Update ConfigMap data from Raw granular object. + if canonicalConfigMap != nil { + newData, found, err := canonicalConfigMap.NestedStringMap("data") + if !found || err != nil { + ctx.ResultErrAndDie(err.Error(), canonicalConfigMap) + return nil + } + for k, v := range newData { + if _, ok := data[k]; ok { + ctx.ResultErrAndDie("duplicate key value %v", nil) + } + data[k] = v + } + cmObject, err := r.CreateCanonicalConfigMap(functionConfig, canonicalConfigMap, data) + if err != nil { + ctx.ResultErrAndDie(err.Error(), nil) + } + newItemsList = append(newItemsList, cmObject) + } + // RawObject is exposed to user as immutable ConfigMap. + { + SetRawConfigMapObject(functionConfig, rawConfigMap) + newItemsList = append(newItemsList, rawConfigMap) + rawConfigMapName = rawConfigMap.GetName() + } + } + UpdateConfigMapReference(noConfigMapItemList, functionConfig.GetName(), rawConfigMapName) + ctx.ResultInfo("ConfigMap references are updated to "+rawConfigMapName, nil) + newItemsList = append(newItemsList, noConfigMapItemList...) + return newItemsList +} + +func SetRawConfigMapObject(fnConfig *fn.KubeObject, object *fn.KubeObject) { + hashsuffix, _ := Hash(object) + hashedName := fmt.Sprintf("%s-%s", fnConfig.GetName(), hashsuffix) + object.SetName(hashedName) + object.SetAnnotation(fn.GeneratorIdentifier, fnConfig.GetOriginId().String()) + object.SetNestedBool(true, "immutable") +} + +func SelectNativeFnConfig(ctx *fn.Context, source *SourceObjectReference, items fn.KubeObjects) *fn.KubeObject { + nativeFnConfigs := items.Where(func(o *fn.KubeObject) bool { + switch true { + case o.GetKind() != source.Kind: + return false + case o.GetName() != source.Name: + return false + default: + return true + } + }) + if len(nativeFnConfigs) > 1 { + ctx.ResultWarn(fmt.Sprintf("found more than one %v", source.Kind), nativeFnConfigs[0]) + return nil + } + if len(nativeFnConfigs) == 0 { + ctx.ResultWarn(fmt.Sprintf("unable to find source %v", source.Kind), nil) + return nil + } + return nativeFnConfigs[0] +} + +func UpdateConfigMapReference(items fn.KubeObjects, oldName, newName string) { + for _, object := range items { + if object.IsGVK("apps", "v1", "StatefulSet") { + volumes := object.GetMap("spec").GetMap("template").GetMap("spec").GetSlice("volumes") + for _, volume := range volumes { + var v corev1.Volume + volume.As(&v) + // StatefulSet should hold the configmap origin. + // if v.ConfigMap != nil && v.ConfigMap.Name == oldName { + if v.ConfigMap != nil { + volume.GetMap("configMap").SetNestedString(newName, "name") + } + } + } + } +} + +func (r *ConfigMapGenerator) CreateCanonicalConfigMap(functionConfig, object *fn.KubeObject, data map[string]string) (*fn.KubeObject, error) { + name := functionConfig.GetName() + "-internal" + namespace := functionConfig.GetNamespace() + object.SetName(name) + if namespace != "" { + object.SetNamespace(namespace) + } + object.SetNestedStringMap(data, "data") + hashsuffix, err := Hash(object) + if err != nil { + return nil, err + } + hashedName := fmt.Sprintf("%s-%s", name, hashsuffix) + object.SetName(hashedName) + object.SetAnnotation(fn.IndexAnnotation, "0") + object.SetAnnotation(fn.PathAnnotation, object.GetKind()+"_"+object.GetName()+".yaml") + object.SetAnnotation(fn.GeneratorBuiltinIdentifier, functionConfig.GetOriginId().String()) + return object, nil +} diff --git a/functions/go/configmap-generator/generator/hash.go b/functions/go/configmap-generator/generator/hash.go new file mode 100644 index 000000000..0cefe7d59 --- /dev/null +++ b/functions/go/configmap-generator/generator/hash.go @@ -0,0 +1,61 @@ +package generator + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn" +) + +func Hash(object *fn.KubeObject) (string, error) { + content, err := effectiveContent(object) + if err != nil { + return "", nil + } + hex256 := fmt.Sprintf("%x", sha256.Sum256([]byte(content))) + return encode(hex256) +} + +// 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 +} + +func effectiveContent(object *fn.KubeObject) (string, error) { + data, _, err := object.NestedStringMap("data") + if err != nil { + return "", err + } + m := map[string]interface{}{ + "kind": object.GetKind(), + "name": object.GetName(), + "data": data, + } + // skip binaryData + content, err := json.Marshal(m) + if err != nil { + return "", err + } + return string(content), nil +} diff --git a/functions/go/configmap-generator/go.mod b/functions/go/configmap-generator/go.mod new file mode 100644 index 000000000..0c23a86d6 --- /dev/null +++ b/functions/go/configmap-generator/go.mod @@ -0,0 +1,48 @@ +module github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator + +go 1.17 + +require ( + gopkg.in/ini.v1 v1.67.0 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.24.3 + k8s.io/klog/v2 v2.70.1 + sigs.k8s.io/kustomize/kyaml v0.13.8 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + github.com/xlab/treeprint v1.1.0 // indirect + golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 // indirect + golang.org/x/sys v0.0.0-20220731174439-a90be440212d // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/apimachinery v0.24.3 // indirect + k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/functions/go/configmap-generator/go.sum b/functions/go/configmap-generator/go.sum new file mode 100644 index 000000000..ceb752040 --- /dev/null +++ b/functions/go/configmap-generator/go.sum @@ -0,0 +1,366 @@ +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= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +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/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/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/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.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.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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +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 v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +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/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/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/yuin/goldmark v1.1.27/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= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I= +golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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-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-20201020160332-67f06af15bc9/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-20180830151530-49385e6e1522/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= +golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +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= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.3.0/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= +k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= +k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/kustomize/kyaml v0.13.8 h1:L4dSaDb6dL5mzv0UWSrUw8bskcEW+EnNtIObT5BoRsU= +sigs.k8s.io/kustomize/kyaml v0.13.8/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +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/configmap-generator/kpt-test-previous-cm-exist/ConfigMap_mariadb-internal-9gcg52fhbg.yaml b/functions/go/configmap-generator/kpt-test-previous-cm-exist/ConfigMap_mariadb-internal-9gcg52fhbg.yaml new file mode 100644 index 000000000..9be351e4d --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-previous-cm-exist/ConfigMap_mariadb-internal-9gcg52fhbg.yaml @@ -0,0 +1,30 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-internal-9gcg52fhbg + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + namespace: example +data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 32M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3306" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp diff --git a/functions/go/configmap-generator/kpt-test-previous-cm-exist/configmap_mariadb-btg654b96m.yaml b/functions/go/configmap-generator/kpt-test-previous-cm-exist/configmap_mariadb-btg654b96m.yaml new file mode 100644 index 000000000..7cd6875b9 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-previous-cm-exist/configmap_mariadb-btg654b96m.yaml @@ -0,0 +1,37 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-btg654b96m + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=32M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true diff --git a/functions/go/configmap-generator/kpt-test-previous-cm-exist/fn-config.yaml b/functions/go/configmap-generator/kpt-test-previous-cm-exist/fn-config.yaml new file mode 100644 index 000000000..ea85e5e35 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-previous-cm-exist/fn-config.yaml @@ -0,0 +1,13 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true diff --git a/functions/go/configmap-generator/kpt-test-previous-cm-exist/resources.yaml b/functions/go/configmap-generator/kpt-test-previous-cm-exist/resources.yaml new file mode 100644 index 000000000..c0abe63ca --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-previous-cm-exist/resources.yaml @@ -0,0 +1,111 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + nodePort: null + selector: + app.kubernetes.io/name: mariadb +--- +# Source: ghost/charts/mariadb/templates/primary/statefulset.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: 'apps|StatefulSet|example|mariadb' +spec: + replicas: 1 + revisionHistoryLimit: 10 + serviceName: mariadb + updateStrategy: + type: RollingUpdate + template: + spec: + securityContext: + fsGroup: 1001 + containers: + - name: mariadb + image: docker.io/bitnami/mariadb:10.6.7-debian-10-r62 + imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "true" + - name: MARIADB_USER + value: bn_ghost + - name: MARIADB_DATABASE + value: bitnami_ghost + - name: ALLOW_EMPTY_PASSWORD + value: "true" + ports: + - name: mysql + containerPort: 3306 + resources: + limits: {} + requests: {} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.ini + subPath: my.ini + volumes: + - name: config + configMap: + name: mariadb-btg654b96m + metadata: + labels: + app.kubernetes.io/name: mariadb + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/name: mariadb + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + selector: + matchLabels: + app.kubernetes.io/name: mariadb +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=32M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: + name: mycnf-ini-file diff --git a/functions/go/configmap-generator/kpt-test-write/fn-config.yaml b/functions/go/configmap-generator/kpt-test-write/fn-config.yaml new file mode 100644 index 000000000..2fc485f55 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-write/fn-config.yaml @@ -0,0 +1,17 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf \ No newline at end of file diff --git a/functions/go/configmap-generator/kpt-test-write/resource.yaml b/functions/go/configmap-generator/kpt-test-write/resource.yaml new file mode 100644 index 000000000..f086c93f1 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-write/resource.yaml @@ -0,0 +1,157 @@ + +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf +--- +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user2 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" + config.kubernetes.io/path: 'Kptfile' + internal.config.kubernetes.io/path: 'Kptfile' +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 39702b84d39b23140a48f0e8adafe80ff630da72 +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-6bttht6hbm + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.config.kubernetes.io/path: generated/configmap_mariadb-6bttht6hbm.yaml + config.kubernetes.io/path: generated/configmap_mariadb-6bttht6hbm.yaml + config.kubernetes.io/index: "" +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3308 # a different change from upstream + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + upstream_field=yuwen-bbbbbbbbbb + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +--- +kind: ConfigMap +apiVersion: v1 +metadata: # kpt-merge: example/mariadb-internal-gg58g98t77 + name: mariadb-internal-9b77bh28t5 + namespace: example + annotations: + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/upstream-identifier: '|ConfigMap|example|mariadb-internal-gg58g98t77' + config.kubernetes.io/path: 'generated/ConfigMap_mariadb-internal-9b77bh28t5.yaml' + internal.config.kubernetes.io/path: 'generated/ConfigMap_mariadb-internal-9b77bh28t5.yaml' + internal.kpt.dev/generated-builtin-merged: "true" +data: + cm.client.default-character-set: UTF8 + cm.client.new_field: yuwenma-aaaaaaa + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 32M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3308" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp + cm.mysqld.upstream_field: yuwen-bbbbbbbbbb +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' + config.kubernetes.io/path: 'service-mariadb.yaml' + internal.config.kubernetes.io/path: 'service-mariadb.yaml' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + selector: + app.kubernetes.io/name: mariadb \ No newline at end of file diff --git a/functions/go/configmap-generator/kpt-test-write/result/ConfigMap_mariadb-internal-b279d662d8.yaml b/functions/go/configmap-generator/kpt-test-write/result/ConfigMap_mariadb-internal-b279d662d8.yaml new file mode 100644 index 000000000..50a96b03b --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-write/result/ConfigMap_mariadb-internal-b279d662d8.yaml @@ -0,0 +1,8 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-internal-b279d662d8 + namespace: example + annotations: + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb +data: {} diff --git a/functions/go/configmap-generator/kpt-test-write/result/fn-config.yaml b/functions/go/configmap-generator/kpt-test-write/result/fn-config.yaml new file mode 100644 index 000000000..6a4cdbbb8 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-write/result/fn-config.yaml @@ -0,0 +1,15 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf diff --git a/functions/go/configmap-generator/kpt-test-write/result/resource.yaml b/functions/go/configmap-generator/kpt-test-write/result/resource.yaml new file mode 100644 index 000000000..0c3a69a4f --- /dev/null +++ b/functions/go/configmap-generator/kpt-test-write/result/resource.yaml @@ -0,0 +1,72 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf +--- +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user2 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 39702b84d39b23140a48f0e8adafe80ff630da72 +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + selector: + app.kubernetes.io/name: mariadb diff --git a/functions/go/configmap-generator/kpt-test/fn-config.yaml b/functions/go/configmap-generator/kpt-test/fn-config.yaml new file mode 100644 index 000000000..18520d218 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test/fn-config.yaml @@ -0,0 +1,15 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true diff --git a/functions/go/configmap-generator/kpt-test/fn-resource.yaml b/functions/go/configmap-generator/kpt-test/fn-resource.yaml new file mode 100644 index 000000000..047028411 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test/fn-resource.yaml @@ -0,0 +1,18 @@ + +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig + localFile: my.cnf diff --git a/functions/go/configmap-generator/kpt-test/resource.yaml b/functions/go/configmap-generator/kpt-test/resource.yaml new file mode 100644 index 000000000..32a072ce9 --- /dev/null +++ b/functions/go/configmap-generator/kpt-test/resource.yaml @@ -0,0 +1,235 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: 'apps|StatefulSet|example|mariadb' + config.kubernetes.io/path: 'statefulset-mariadb.yaml' + internal.config.kubernetes.io/path: 'statefulset-mariadb.yaml' +spec: + replicas: 1 + revisionHistoryLimit: 10 + serviceName: mariadb + updateStrategy: + type: RollingUpdate + template: + spec: + securityContext: + fsGroup: 1001 + containers: + - name: mariadb + image: docker.io/bitnami/mariadb:10.6.7-debian-10-r62 + imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "true" + - name: MARIADB_USER + value: bn_ghost + - name: MARIADB_DATABASE + value: bitnami_ghost + - name: ALLOW_EMPTY_PASSWORD + value: "true" + ports: + - name: mysql + containerPort: 3306 + resources: + limits: {} + requests: {} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.ini + subPath: my.ini + volumes: + - name: config + configMap: + name: mariadb-92256k9879 + metadata: + labels: + app.kubernetes.io/name: mariadb + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/name: mariadb + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + selector: + matchLabels: + app.kubernetes.io/name: mariadb +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3308 # a different change from upstream\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=16M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\nupstream_field=yuwen-bbbbbbbbbb\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: + name: mycnf-ini-file + annotations: + internal.config.kubernetes.io/path: generated/builtinnonkrm_mycnf-ini-file.yaml + config.kubernetes.io/path: generated/builtinnonkrm_mycnf-ini-file.yaml + config.kubernetes.io/index: "" +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true +--- +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user2 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" + config.kubernetes.io/path: 'Kptfile' + internal.config.kubernetes.io/path: 'Kptfile' +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 39702b84d39b23140a48f0e8adafe80ff630da72 +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-6bttht6hbm + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.config.kubernetes.io/path: generated/configmap_mariadb-6bttht6hbm.yaml + config.kubernetes.io/path: generated/configmap_mariadb-6bttht6hbm.yaml + config.kubernetes.io/index: "" +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3308 # a different change from upstream + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + upstream_field=yuwen-bbbbbbbbbb + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-internal-9b77bh28t5 + namespace: example + annotations: + internal.config.kubernetes.io/index: "0" + internal.config.kubernetes.io/path: generated/ConfigMap_mariadb-internal-9b77bh28t5.yaml + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + config.kubernetes.io/path: generated/ConfigMap_mariadb-internal-9b77bh28t5.yaml + config.kubernetes.io/index: "0" + internal.kpt.dev/generated-builtin-merged: "true" +data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 16M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3308" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp + cm.mysqld.upstream_field: yuwen-bbbbbbbbbb +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' + config.kubernetes.io/path: 'service-mariadb.yaml' + internal.config.kubernetes.io/path: 'service-mariadb.yaml' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + selector: + app.kubernetes.io/name: mariadb[RUNNING] "gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1" diff --git a/functions/go/configmap-generator/kpt-test/resources.yaml b/functions/go/configmap-generator/kpt-test/resources.yaml new file mode 100644 index 000000000..6add2459c --- /dev/null +++ b/functions/go/configmap-generator/kpt-test/resources.yaml @@ -0,0 +1,279 @@ +kind: ConfigMap +apiVersion: v1 +metadata: # kpt-merge: example/mariadb-internal-9gcg52fhbg + name: mariadb-internal-9gcg52fhbg + namespace: example + annotations: + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/upstream-identifier: '|ConfigMap|example|mariadb-internal-9gcg52fhbg' + config.kubernetes.io/path: 'generated/ConfigMap_mariadb-internal-9gcg52fhbg.yaml' + internal.config.kubernetes.io/path: 'generated/ConfigMap_mariadb-internal-9gcg52fhbg.yaml' +data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 32M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3306" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp +--- +kind: ConfigMap +apiVersion: v1 +metadata: # kpt-merge: /mariadb-92256k9879 + name: mariadb-92256k9879 + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/upstream-identifier: '|ConfigMap|default|mariadb-92256k9879' + config.kubernetes.io/path: 'generated/configmap_mariadb-92256k9879.yaml' + internal.config.kubernetes.io/path: 'generated/configmap_mariadb-92256k9879.yaml' +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +--- +kind: ConfigMap +apiVersion: v1 +metadata: # kpt-merge: /mariadb-btg654b96m + name: mariadb-btg654b96m + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/upstream-identifier: '|ConfigMap|default|mariadb-btg654b96m' + config.kubernetes.io/path: 'generated/configmap_mariadb-btg654b96m.yaml' + internal.config.kubernetes.io/path: 'generated/configmap_mariadb-btg654b96m.yaml' +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=32M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +YUWEN DEBUG 3: new generated input: +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user1 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" + config.kubernetes.io/path: 'Kptfile' + internal.config.kubernetes.io/path: 'Kptfile' +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 099d8ab734d0364cd362f040ad2d9c453a65b27f +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=32M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: # kpt-merge: /mycnf-ini-file + name: mycnf-ini-file + annotations: + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|BuiltInNonKrm|default|mycnf-ini-file' + config.kubernetes.io/path: 'generated/builtinnonkrm_mycnf-ini-file.yaml' + internal.config.kubernetes.io/path: 'generated/builtinnonkrm_mycnf-ini-file.yaml' +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' + config.kubernetes.io/path: 'service-mariadb.yaml' + internal.config.kubernetes.io/path: 'service-mariadb.yaml' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + selector: + app.kubernetes.io/name: mariadb +--- +# Source: ghost/charts/mariadb/templates/primary/statefulset.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: 'apps|StatefulSet|example|mariadb' + config.kubernetes.io/path: 'statefulset-mariadb.yaml' + internal.config.kubernetes.io/path: 'statefulset-mariadb.yaml' +spec: + replicas: 1 + revisionHistoryLimit: 10 + serviceName: mariadb + updateStrategy: + type: RollingUpdate + template: + spec: + securityContext: + fsGroup: 1001 + containers: + - name: mariadb + image: docker.io/bitnami/mariadb:10.6.7-debian-10-r62 + imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "true" + - name: MARIADB_USER + value: bn_ghost + - name: MARIADB_DATABASE + value: bitnami_ghost + - name: ALLOW_EMPTY_PASSWORD + value: "true" + ports: + - name: mysql + containerPort: 3306 + resources: + limits: {} + requests: {} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.ini + subPath: my.ini + volumes: + - name: config + configMap: + name: mariadb-92256k9879 + metadata: + labels: + app.kubernetes.io/name: mariadb + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/name: mariadb + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + selector: + matchLabels: + app.kubernetes.io/name: mariadb +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=32M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: + name: mycnf-ini-file \ No newline at end of file diff --git a/functions/go/configmap-generator/main.go b/functions/go/configmap-generator/main.go new file mode 100644 index 000000000..2bd171323 --- /dev/null +++ b/functions/go/configmap-generator/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "os" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/generator" +) + +func main() { + if err := fn.AsMain(&generator.ConfigMapGenerator{}); err != nil { + os.Exit(1) + } +} diff --git a/functions/go/configmap-generator/native-config-adaptor/generator/adaptor.go b/functions/go/configmap-generator/native-config-adaptor/generator/adaptor.go new file mode 100644 index 000000000..7941f6971 --- /dev/null +++ b/functions/go/configmap-generator/native-config-adaptor/generator/adaptor.go @@ -0,0 +1,193 @@ +// Copyright 2022 Google LLC +// +// 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 generator + +import ( + "path/filepath" + "reflect" + "strings" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/native-config-adaptor/parsers" +) + +const NativeConfigOpBackToNative = "writeBackToNativeConfig" + +var SupportedFromNative = map[string]func(object *fn.KubeObject, content string, ShouldFlatten bool) error{ + "IniFile": parsers.FromIniFile, +} + +var SupportedToNative = map[string]func(object *fn.KubeObject, fileRef, filename string, IsFlatten bool) (*fn.KubeObject, error){ + "IniFile": parsers.ToIniFile, +} + +var _ fn.Generator = &NativeConfigAdaptor{} + +func NewNativeConfigAdaptor(config *fn.KubeObject) *NativeConfigAdaptor { + var cfg NativeConfigAdaptor + config.As(&cfg) + return &cfg +} + +func NewFromSource(source *NativeConfigSource) (*fn.KubeObject, error) { + nca := &NativeConfigAdaptor{ + + Spec: &NativeConfigSpec{ + Source: []*NativeConfigSource{ + source, + }, + }, + } + object, err := fn.NewFromTypedObject(nca) + if err != nil { + return nil, err + } + object.SetKind(reflect.TypeOf(NativeConfigAdaptor{}).Name()) + object.SetAPIVersion(fn.KptFunctionApiVersion) + return object, nil +} + +func (t *NativeConfigAdaptor) Generate(ctx *fn.Context, fnConfig *fn.KubeObject, items fn.KubeObjects) fn.KubeObjects { + var generatedObjects []*fn.KubeObject + for _, source := range t.Spec.Source { + if source.LocalFileRef != "" { + localfileObjects := items.Where(func(o *fn.KubeObject) bool { + switch true { + case o.GetKind() != fn.NonKrmKind: + return false + case o.GetAPIVersion() != fn.KptFunctionApiVersion: + return false + case o.GetName() != source.LocalFileRef: + return false + default: + return true + } + }) + sotObjects := items.Where(func(o *fn.KubeObject) bool { + return o.GetAnnotation(fn.GeneratorBuiltinIdentifier) != "" + }) + switch source.Operation { + case NativeConfigOpBackToNative: + ctx.ResultInfo( + "The structured ConfigMap is the Source-of-True, generating to non KRM object and appliable ConfigMap...", nil) + for _, canonicalObject := range sotObjects { + nonKrmObject := t.WriteFromCanonicalToNative(ctx, canonicalObject, source) + if !source.AsConfigMap { + ctx.ResultErrAndDie("unimplemented type", nonKrmObject) + } + newRawConfigMap := StoreRawDataInConfigMap(fnConfig.GetName(), + filepath.Base(source.LocalFile), + nonKrmObject.NestedStringOrDie("spec", "content")) + existingRrawConfigMaps := items.Where(func(o *fn.KubeObject) bool { + return o.GetId() == newRawConfigMap.GetId() && o.GetKind() == "ConfigMap" + }) + if len(existingRrawConfigMaps) > 0 { + existingRrawConfigMaps[0].SetNestedStringMapOrDie(newRawConfigMap.NestedStringMapOrDie("data"), "data") + generatedObjects = append(generatedObjects, existingRrawConfigMaps[0]) + } else { + generatedObjects = append(generatedObjects, newRawConfigMap) + } + canonicalObject.SetName(fnConfig.GetName() + "-internal") + generatedObjects = append(generatedObjects, canonicalObject) + } + default: + ctx.ResultInfo("The non KRM object is the Source-of-True, generating the structured ConfigMap and appliable ConfigMap...", nil) + for _, object := range localfileObjects { + content := object.NestedStringOrDie("spec", "content") + localFileName := object.NestedStringOrDie("spec", "filename") + newObject := t.ParseFromNativeToCanonical(ctx, object.GetName(), content, source) + rawObject := StoreRawDataInConfigMap(object.GetName(), localFileName, content) + generatedObjects = append(generatedObjects, newObject, rawObject) + } + } + } + } + return generatedObjects +} + +func StoreRawDataInConfigMap(name, filename, content string) *fn.KubeObject { + object := fn.NewEmptyKubeObject() + object.SetKind("ConfigMap") + object.SetAPIVersion("v1") + object.SetName(name) + data := map[string]string{ + filename: content, + } + object.SetNestedStringMap(data, "data") + return object +} + +func (t *NativeConfigAdaptor) WriteFromCanonicalToNative(ctx *fn.Context, canonicalObject *fn.KubeObject, source *NativeConfigSource) *fn.KubeObject { + toFn, ok := SupportedToNative[source.Format] + if !ok { + ctx.ResultErrAndDie("unknown parsing type "+source.Format, nil) + return nil + } + nonKrmObject, err := toFn(canonicalObject, source.LocalFileRef, source.LocalFile, source.AsConfigMap) + if err != nil { + ctx.ResultErrAndDie(err.Error(), canonicalObject) + } + return nonKrmObject +} + +func (t *NativeConfigAdaptor) ParseFromNativeToCanonical(ctx *fn.Context, name, content string, source *NativeConfigSource) *fn.KubeObject { + fromFn, ok := SupportedFromNative[source.Format] + if !ok { + ctx.ResultErrAndDie("unknown parsing type "+source.Format, nil) + return nil + } + object := NewCanonicalObject(name, source) + if err := fromFn(object, content, source.AsConfigMap); err != nil { + ctx.ResultErrAndDie(err.Error(), nil) + } + return object +} + +func NewCanonicalObject(name string, source *NativeConfigSource) *fn.KubeObject { + lenseNameSlitted := strings.Split(source.Format, "_") + camelcaseLense := "" + for _, segment := range lenseNameSlitted { + camelcaseLense += strings.ToUpper(string(segment[0])) + segment[1:] + } + + object := fn.NewEmptyKubeObject() + if source.AsConfigMap { + object.SetKind("ConfigMap") + object.SetAPIVersion("v1") + object.SetName(name + "-internal") + object.SetAnnotation(fn.KptLocalConfig, "true") + } else { + object.SetKind(camelcaseLense) + object.SetAPIVersion("config.kpt.dev/v1alpha1") + object.SetName(name + "-internal") + object.SetAnnotation(fn.KptLocalConfig, "true") + } + return object +} + +type NativeConfigAdaptor struct { + Spec *NativeConfigSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +type NativeConfigSpec struct { + Source []*NativeConfigSource `json:"source,omitempty" yaml:"source,omitempty"` +} + +type NativeConfigSource struct { + LocalFileRef string `json:"localFileRef,omitempty" yaml:"localFileRef,omitempty"` + LocalFile string `json:"localFile,omitempty" yaml:"localFile,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + Operation string `json:"operation,omitempty" yaml:"operation,omitempty"` + AsConfigMap bool `json:"asConfigMap,omitempty" yaml:"asConfigMap,omitempty"` +} diff --git a/functions/go/configmap-generator/native-config-adaptor/parsers/inifile.go b/functions/go/configmap-generator/native-config-adaptor/parsers/inifile.go new file mode 100644 index 000000000..60fea734d --- /dev/null +++ b/functions/go/configmap-generator/native-config-adaptor/parsers/inifile.go @@ -0,0 +1,78 @@ +package parsers + +import ( + "bytes" + "fmt" + "strings" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/configmap-generator/fn" + "gopkg.in/ini.v1" +) + +const ( + ConfigMapPrefix = "cm." +) + +func ToIniFile(canonicalObject *fn.KubeObject, fileRef, fileName string, IsFlatten bool) (*fn.KubeObject, error) { + if !IsFlatten { + return nil, fmt.Errorf("not implemented") + } + cfg := ini.Empty(ini.LoadOptions{ + SkipUnrecognizableLines: true, + AllowBooleanKeys: true, + IgnoreInlineComment: false, + }) + for key, value := range canonicalObject.NestedStringMapOrDie("data") { + prefixSectionKey := strings.Split(key, ".") + var createSectionErr error + section, err := cfg.GetSection(prefixSectionKey[1]) + if err != nil { + section, createSectionErr = cfg.NewSection(prefixSectionKey[1]) + if createSectionErr != nil { + return nil, createSectionErr + } + } + section.NewKey(prefixSectionKey[2], value) + } + out := &bytes.Buffer{} + ini.PrettyFormat = false + cfg.WriteTo(out) + newNonKrmObject := fn.NewNonKrmResource() + p, e := fn.NewFromTypedObject(newNonKrmObject) + p.SetName(fileRef) + p.SetNestedStringOrDie(fileName, "spec", "filename") + p.SetNestedStringOrDie(out.String(), "spec", "content") + return p, e +} + +// Read IniFile from content, write to Object +func FromIniFile(object *fn.KubeObject, content string, ShouldFlatten bool) error { + cfg, err := ini.LoadSources(ini.LoadOptions{SkipUnrecognizableLines: true}, []byte(content)) + if err != nil { + return err + } + if ShouldFlatten { + data := map[string]string{} + for _, section := range cfg.Sections() { + for _, key := range section.Keys() { + cmKey := ConfigMapPrefix + section.Name() + "." + key.Name() + data[cmKey] = key.Value() + } + } + if err = object.SetNestedStringMap(data, "data"); err != nil { + return err + } + return nil + } + + spec := object.UpsertMap("spec") + for _, section := range cfg.Sections() { + subObject := spec.UpsertMap(section.Name()) + for _, key := range section.Keys() { + if err = subObject.SetNestedString(key.Value(), key.Name()); err != nil { + return err + } + } + } + return nil +} diff --git a/functions/go/native-config-adaptor/Dockerfile b/functions/go/native-config-adaptor/Dockerfile new file mode 100644 index 000000000..54bff2986 --- /dev/null +++ b/functions/go/native-config-adaptor/Dockerfile @@ -0,0 +1,16 @@ +FROM --platform=$BUILDPLATFORM golang:1.17-alpine3.15 AS build +ENV CGO_ENABLED=0 +WORKDIR /go/src/ + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +ARG TARGETOS TARGETARCH +RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /usr/local/bin/function ./ + +############################################# + +FROM alpine:3.15 +COPY --from=build /usr/local/bin/function /usr/local/bin/function +ENTRYPOINT ["function"] diff --git a/functions/go/native-config-adaptor/README.md b/functions/go/native-config-adaptor/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/functions/go/native-config-adaptor/abc b/functions/go/native-config-adaptor/abc new file mode 100644 index 000000000..0c5cfe625 --- /dev/null +++ b/functions/go/native-config-adaptor/abc @@ -0,0 +1,21 @@ +apiVersion: config.kubernetes.io/v1 +kind: ResourceList +items: +- apiVersion: fn.kpt.dev/v1alpha1 + kind: BuiltInNonKrm + spec: + filename: my.cnf + content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=16M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid" + metadata: + name: mycnf-ini-file +functionConfig: + apiVersion: fn.kpt.dev/v1alpha1 + kind: NativeConfigAdaptor + metadata: + name: mariadb + namespace: example + spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true diff --git a/functions/go/native-config-adaptor/api/kptfile/v1/types.go b/functions/go/native-config-adaptor/api/kptfile/v1/types.go new file mode 100644 index 000000000..265e0c39e --- /dev/null +++ b/functions/go/native-config-adaptor/api/kptfile/v1/types.go @@ -0,0 +1,368 @@ +// Copyright 2021 Google LLC +// +// 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 defines Kptfile schema. +// Version: v1 +// swagger:meta +package v1 + +import ( + "fmt" +) + +const ( + KptFileName = "Kptfile" + KptFileKind = "Kptfile" + KptFileGroup = "kpt.dev" + KptFileVersion = "v1" + KptFileAPIVersion = KptFileGroup + "/" + KptFileVersion +) + +// ResourceMeta contains the metadata for a both Resource Type and Resource. +type ResourceMeta struct { + TypeMeta `json:",inline" yaml:",inline"` + // ObjectMeta is the metadata field of a Resource + ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` +} + +// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta +// No need for a direct dependence; the fields are stable. +type TypeMeta struct { + // APIVersion is the apiVersion field of a Resource + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + // Kind is the kind field of a Resource + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` +} + +// ObjectMeta contains metadata about a Resource +type ObjectMeta struct { + NameMeta `json:",inline" yaml:",inline"` + // Labels is the metadata.labels field of a Resource + Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` + // Annotations is the metadata.annotations field of a Resource. + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` +} + +// NameMeta contains name information. +type NameMeta struct { + // Name is the metadata.name field of a Resource + Name string `json:"name,omitempty" yaml:"name,omitempty"` + // Namespace is the metadata.namespace field of a Resource + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} + +// KptFile contains information about a package managed with kpt. +// swagger:model kptfile +type KptFile struct { + ResourceMeta `yaml:",inline" json:",inline"` + + Upstream *Upstream `yaml:"upstream,omitempty" json:"upstream,omitempty"` + + // UpstreamLock is a resolved locator for the last fetch of the package. + UpstreamLock *UpstreamLock `yaml:"upstreamLock,omitempty" json:"upstreamLock,omitempty"` + + // Info contains metadata such as license, documentation, etc. + Info *PackageInfo `yaml:"info,omitempty" json:"info,omitempty"` + + // Pipeline declares the pipeline of functions. + Pipeline *Pipeline `yaml:"pipeline,omitempty" json:"pipeline,omitempty"` + + // Inventory contains parameters for the inventory object used in apply. + Inventory *Inventory `yaml:"inventory,omitempty" json:"inventory,omitempty"` +} + +// OriginType defines the type of origin for a package. +type OriginType string + +const ( + // GitOrigin specifies a package as having been cloned from a git repository. + GitOrigin OriginType = "git" +) + +// UpdateStrategyType defines the strategy for updating a package from upstream. +type UpdateStrategyType string + +// ToUpdateStrategy takes a string representing an update strategy and will +// return the strategy as an UpdateStrategyType. If the provided string does +// not match any known update strategies, an error will be returned. +func ToUpdateStrategy(strategy string) (UpdateStrategyType, error) { + switch strategy { + case string(ResourceMerge): + return ResourceMerge, nil + case string(FastForward): + return FastForward, nil + case string(ForceDeleteReplace): + return ForceDeleteReplace, nil + default: + return "", fmt.Errorf("unknown update strategy %q", strategy) + } +} + +const ( + // ResourceMerge performs a structural schema-aware comparison and + // merges the changes into the local package. + ResourceMerge UpdateStrategyType = "resource-merge" + // FastForward fails without updating if the local package was modified + // since it was fetched. + FastForward UpdateStrategyType = "fast-forward" + // ForceDeleteReplace wipes all local changes to the package. + ForceDeleteReplace UpdateStrategyType = "force-delete-replace" +) + +// UpdateStrategies is a slice with all the supported update strategies. +var UpdateStrategies = []UpdateStrategyType{ + ResourceMerge, + FastForward, + ForceDeleteReplace, +} + +// UpdateStrategiesAsStrings returns a list of update strategies as strings. +func UpdateStrategiesAsStrings() []string { + var strs []string + for _, s := range UpdateStrategies { + strs = append(strs, string(s)) + } + return strs +} + +// Upstream is a user-specified upstream locator for a package. +type Upstream struct { + // Type is the type of origin. + Type OriginType `yaml:"type,omitempty" json:"type,omitempty"` + + // Git is the locator for a package stored on Git. + Git *Git `yaml:"git,omitempty" json:"git,omitempty"` + + // UpdateStrategy declares how a package will be updated from upstream. + UpdateStrategy UpdateStrategyType `yaml:"updateStrategy,omitempty" json:"updateStrategy,omitempty"` +} + +// Git is the user-specified locator for a package on Git. +type Git struct { + // Repo is the git repository the package. + // e.g. 'https://github.com/kubernetes/examples.git' + Repo string `yaml:"repo,omitempty" json:"repo,omitempty"` + + // Directory is the sub directory of the git repository. + // e.g. 'staging/cockroachdb' + Directory string `yaml:"directory,omitempty" json:"directory,omitempty"` + + // Ref can be a Git branch, tag, or a commit SHA-1. + Ref string `yaml:"ref,omitempty" json:"ref,omitempty"` +} + +// UpstreamLock is a resolved locator for the last fetch of the package. +type UpstreamLock struct { + // Type is the type of origin. + Type OriginType `yaml:"type,omitempty" json:"type,omitempty"` + + // Git is the resolved locator for a package on Git. + Git *GitLock `yaml:"git,omitempty" json:"git,omitempty"` +} + +// GitLock is the resolved locator for a package on Git. +type GitLock struct { + // Repo is the git repository that was fetched. + // e.g. 'https://github.com/kubernetes/examples.git' + Repo string `yaml:"repo,omitempty" json:"repo,omitempty"` + + // Directory is the sub directory of the git repository that was fetched. + // e.g. 'staging/cockroachdb' + Directory string `yaml:"directory,omitempty" json:"directory,omitempty"` + + // Ref can be a Git branch, tag, or a commit SHA-1 that was fetched. + // e.g. 'master' + Ref string `yaml:"ref,omitempty" json:"ref,omitempty"` + + // Commit is the SHA-1 for the last fetch of the package. + // This is set by kpt for bookkeeping purposes. + Commit string `yaml:"commit,omitempty" json:"commit,omitempty"` +} + +// PackageInfo contains optional information about the package such as license, documentation, etc. +// These fields are not consumed by any functionality in kpt and are simply passed through. +// Note that like any other KRM resource, humans and automation can also use `metadata.labels` and +// `metadata.annotations` as the extension mechanism. +type PackageInfo struct { + // Site is the URL for package web page. + Site string `yaml:"site,omitempty" json:"site,omitempty"` + + // Email is the list of emails for the package authors. + Emails []string `yaml:"emails,omitempty" json:"emails,omitempty"` + + // SPDX license identifier (e.g. "Apache-2.0"). See: https://spdx.org/licenses/ + License string `yaml:"license,omitempty" json:"license,omitempty"` + + // Relative slash-delimited path to the license file (e.g. LICENSE.txt) + LicenseFile string `yaml:"licenseFile,omitempty" json:"licenseFile,omitempty"` + + // Description contains a short description of the package. + Description string `yaml:"description,omitempty" json:"description,omitempty"` + + // Keywords is a list of keywords for this package. + Keywords []string `yaml:"keywords,omitempty" json:"keywords,omitempty"` + + // Man is the path to documentation about the package + Man string `yaml:"man,omitempty" json:"man,omitempty"` +} + +// Subpackages declares a local or remote subpackage. +type Subpackage struct { + // Name of the immediate subdirectory relative to this Kptfile where the suppackage + // either exists (local subpackages) or will be fetched to (remote subpckages). + // This must be unique across all subpckages of a package. + LocalDir string `yaml:"localDir,omitempty" json:"localDir,omitempty"` + + // Upstream is a reference to where the subpackage should be fetched from. + // Whether a subpackage is local or remote is determined by whether Upstream is specified. + Upstream *Upstream `yaml:"upstream,omitempty" json:"upstream,omitempty"` +} + +// Pipeline declares a pipeline of functions used to mutate or validate resources. +type Pipeline struct { + // Sources defines the source packages to resolve as input to the pipeline. Possible values: + // a) A slash-separated, OS-agnostic relative package path which may include '.' and '..' e.g. './base', '../foo' + // The source package is resolved recursively. + // b) Resources in this package using '.'. Meta resources such as the Kptfile, Pipeline, and function configs + // are excluded. + // c) Resources in this package AND all resolved subpackages using './*' + // + // Resultant list of resources are ordered: + // - According to the order of sources specified in this array. + // - When using './*': Subpackages are resolved in alphanumerical order before package resources. + // + // When omitted, defaults to './*'. + // Sources []string `yaml:"sources,omitempty"` + + // Following fields define the sequence of functions in the pipeline. + // Input of the first function is the resolved sources. + // Input of the second function is the output of the first function, and so on. + // Order of operation: mutators, validators + + // Mutators defines a list of of KRM functions that mutate resources. + Mutators []Function `yaml:"mutators,omitempty" json:"mutators,omitempty"` + + // Validators defines a list of KRM functions that validate resources. + // Validators are not permitted to mutate resources. + Validators []Function `yaml:"validators,omitempty" json:"validators,omitempty"` +} + +// String returns the string representation of Pipeline struct +// The string returned is the struct content in Go default format. +func (p *Pipeline) String() string { + return fmt.Sprintf("%+v", *p) +} + +// IsEmpty returns true if the pipeline doesn't contain any functions in any of +// the function chains (mutators, validators). +func (p *Pipeline) IsEmpty() bool { + if p == nil { + return true + } + if len(p.Mutators) == 0 && len(p.Validators) == 0 { + return true + } + return false +} + +// Function specifies a KRM function. +type Function struct { + // `Image` specifies the function container image. + // It can either be fully qualified, e.g.: + // + // image: gcr.io/kpt-fn/set-labels + // + // Optionally, kpt can be configured to use a image + // registry host-path that will be used to resolve the image path in case + // the image path is missing (Defaults to gcr.io/kpt-fn). + // e.g. The following resolves to gcr.io/kpt-fn/set-labels: + // + // image: set-labels + Image string `yaml:"image,omitempty" json:"image,omitempty"` + + // Exec specifies the function binary executable. + // The executable can be fully qualified or it must exists in the $PATH e.g: + // + // exec: set-namespace + // exec: /usr/local/bin/my-custom-fn + Exec string `yaml:"exec,omitempty" json:"exec,omitempty"` + + // `ConfigPath` specifies a slash-delimited relative path to a file in the current directory + // containing a KRM resource used as the function config. This resource is + // excluded when resolving 'sources', and as a result cannot be operated on + // by the pipeline. + ConfigPath string `yaml:"configPath,omitempty" json:"configPath,omitempty"` + + // `ConfigMap` is a convenient way to specify a function config of kind ConfigMap. + ConfigMap map[string]string `yaml:"configMap,omitempty" json:"configMap,omitempty"` + + // `Name` is used to uniquely identify the function declaration + // this is primarily used for merging function declaration with upstream counterparts + Name string `yaml:"name,omitempty" json:"name,omitempty"` + + // `Selectors` are used to specify resources on which the function should be executed + // if not specified, all resources are selected + Selectors []Selector `yaml:"selectors,omitempty" json:"selectors,omitempty"` + + // `Exclude` are used to specify resources on which the function should NOT be executed. + // If not specified, all resources selected by `Selectors` are selected. + Exclusions []Selector `yaml:"exclude,omitempty" json:"exclude,omitempty"` +} + +// Selector specifies the selection criteria +// please update IsEmpty method if more properties are added +type Selector struct { + // APIVersion of the target resources + APIVersion string `yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"` + // Kind of the target resources + Kind string `yaml:"kind,omitempty" json:"kind,omitempty"` + // Name of the target resources + Name string `yaml:"name,omitempty" json:"name,omitempty"` + // Namespace of the target resources + Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"` + // Labels on the target resources + Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"` + // Annotations on the target resources + Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"` +} + +// IsEmpty returns true of none of the selection criteria is specified +func (s Selector) IsEmpty() bool { + return s.APIVersion == "" && + s.Namespace == "" && + s.Name == "" && + s.Kind == "" && + len(s.Labels) == 0 && + len(s.Annotations) == 0 +} + +// Inventory encapsulates the parameters for the inventory resource applied to a cluster. +// All of the the parameters are required if any are set. +type Inventory struct { + // Namespace for the inventory resource. + Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"` + // Name of the inventory resource. + Name string `yaml:"name,omitempty" json:"name,omitempty"` + // Unique label to identify inventory resource in cluster. + InventoryID string `yaml:"inventoryID,omitempty" json:"inventoryID,omitempty"` + Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"` + Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"` +} + +func (i Inventory) IsValid() bool { + // Name and Namespace are required inventory fields, so we check these 2 fields. + // InventoryID is an optional field since we only store it locally if the user + // specifies one. + return i.Name != "" && i.Namespace != "" +} diff --git a/functions/go/native-config-adaptor/api/util/kptfileutil.go b/functions/go/native-config-adaptor/api/util/kptfileutil.go new file mode 100644 index 000000000..465fccc15 --- /dev/null +++ b/functions/go/native-config-adaptor/api/util/kptfileutil.go @@ -0,0 +1,35 @@ +// Copyright 2021 Google LLC +// +// 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 util + +import ( + "fmt" + "strings" + + kptfilev1 "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/api/kptfile/v1" + "gopkg.in/yaml.v3" +) + +// DecodeKptfile decodes a KptFile from a yaml string. +func DecodeKptfile(kf string) (*kptfilev1.KptFile, error) { + kptfile := &kptfilev1.KptFile{} + f := strings.NewReader(kf) + d := yaml.NewDecoder(f) + d.KnownFields(true) + if err := d.Decode(&kptfile); err != nil { + return &kptfilev1.KptFile{}, fmt.Errorf("invalid 'v1' Kptfile: %w", err) + } + return kptfile, nil +} diff --git a/functions/go/native-config-adaptor/cba b/functions/go/native-config-adaptor/cba new file mode 100644 index 000000000..338ab3807 --- /dev/null +++ b/functions/go/native-config-adaptor/cba @@ -0,0 +1,45 @@ +apiVersion: config.kubernetes.io/v1 +kind: ResourceList +items: +- kind: ConfigMap + apiVersion: v1 + metadata: + name: mycnf-ini-file-internal + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/generator-builtin-only: "fn.kpt.dev|ConfigMapGenerator|default|mariadb" + data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 16M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3306" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp +functionConfig: + apiVersion: fn.kpt.dev/v1alpha1 + kind: NativeConfigAdaptor + metadata: + name: mariadb + namespace: example + spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + localFile: my.cnf + asConfigMap: true + operation: writeBackToNativeConfig diff --git a/functions/go/native-config-adaptor/fn/.golangci.yml b/functions/go/native-config-adaptor/fn/.golangci.yml new file mode 100644 index 000000000..e273601c5 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/.golangci.yml @@ -0,0 +1,64 @@ +# Copyright 2022 Google LLC +# +# 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. + +run: + deadline: 5m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + # - funlen + - gochecknoinits + - goconst + # - gocritic + # - gocyclo + - gofmt + - goimports + - golint + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - lll + - misspell + - nakedret + - scopelint + - staticcheck + - structcheck + # stylecheck demands that acronyms not be treated as words + # in camelCase, so JsonOp become JSONOp, etc. Yuck. + # - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + +linters-settings: + dupl: + threshold: 400 + lll: + line-length: 170 + gocyclo: + min-complexity: 15 + golint: + min-confidence: 0.85 diff --git a/functions/go/native-config-adaptor/fn/canonical.go b/functions/go/native-config-adaptor/fn/canonical.go new file mode 100644 index 000000000..087623239 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/canonical.go @@ -0,0 +1,37 @@ +package fn + +func NewNonKrmResource() *NonKrmResource { + resource := &NonKrmResource{} + resource.Kind = NonKrmKind + resource.APIVersion = KptFunctionApiVersion + return resource +} + +type NonKrmResource struct { + TypeMeta `json:",inline" yaml:",inline"` + NameMeta `json:",inline" yaml:",inline"` + Spec NonKrmResourceSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +type NonKrmResourceSpec struct { + FileName string `json:"fileName,omitempty" yaml:"fileName,omitempty"` + LocalPath string `json:"localPath,omitempty" yaml:"localPath,omitempty"` + Content string `json:"content,omitempty" yaml:"content,omitempty"` +} + +// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta +// No need for a direct dependence; the fields are stable. +type TypeMeta struct { + // APIVersion is the apiVersion field of a Resource + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + // Kind is the kind field of a Resource + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` +} + +// NameMeta contains name information. +type NameMeta struct { + // Name is the metadata.name field of a Resource + Name string `json:"name,omitempty" yaml:"name,omitempty"` + // Namespace is the metadata.namespace field of a Resource + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +} diff --git a/functions/go/native-config-adaptor/fn/const.go b/functions/go/native-config-adaptor/fn/const.go new file mode 100644 index 000000000..bb95d6594 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/const.go @@ -0,0 +1,77 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +const ( + // internalPrefix is the prefix given to internal annotations that are used + // internally by the orchestrator + internalPrefix string = "internal.config.kubernetes.io/" + + // IndexAnnotation records the index of a specific resource in a file or input stream. + IndexAnnotation string = internalPrefix + "index" + + // PathAnnotation records the path to the file the Resource was read from + PathAnnotation string = internalPrefix + "path" + + // SeqIndentAnnotation records the sequence nodes indentation of the input resource + SeqIndentAnnotation string = internalPrefix + "seqindent" + + // IdAnnotation records the id of the resource to map inputs to outputs + IdAnnotation string = internalPrefix + "id" + + // InternalAnnotationsMigrationResourceIDAnnotation is used to uniquely identify + // resources during round trip to and from a function execution. We will use it + // to track the internal annotations and reconcile them if needed. + InternalAnnotationsMigrationResourceIDAnnotation = internalPrefix + "annotations-migration-resource-id" + + // ConfigPrefix is the prefix given to the custom kubernetes annotations. + ConfigPrefix string = "config.kubernetes.io/" + + // KptLocalConfig marks a KRM resource to be skipped from deploying to the cluster via `kpt live apply`. + KptLocalConfig = ConfigPrefix + "local-config" +) + +// For Kpt use only constants +const ( + // KptUseOnlyPrefix is the prefix of kpt-only annotations. Users are not expected to touch these annotations. + KptUseOnlyPrefix = "internal.kpt.dev/" + + // UpstreamIdentifier is the annotation to record a resource's upstream origin. + // It is in the form of ||| + UpstreamIdentifier = KptUseOnlyPrefix + "upstream-identifier" + + // UnknownNamespace is the special char for cluster-scoped or unknown-scoped resources. This is only used in upstream-identifier + UnknownNamespace = "~C" + // DefaultNamespace is the actual namespace value if a namespace-scoped resource has its namespace field unspecified. + DefaultNamespace = "default" + + // e.g. ConfigMap Generator should be the functionConfig GKNN in the form of ||| + GeneratorIdentifier = KptUseOnlyPrefix + "generator" + GeneratorBuiltinIdentifier = KptUseOnlyPrefix + "generator-builtin-only" +) + +// For KPT Function Configuration +const ( + // KptFunctionGroup is the group name for the KRM resource which defines the configuration of a function execution. + // See KRM function specification `ResourceList.FunctionConfig` + KptFunctionGroup = "fn.kpt.dev" + // KptFunctionGroup is the version for the KRM resource which defines the configuration of a function execution. + // See KRM function specification `ResourceList.FunctionConfig` + KptFunctionVersion = "v1alpha1" + // KptFunctionGroup is the ApiVersion for the KRM resource which defines the configuration of a function execution. + // See KRM function specification `ResourceList.FunctionConfig` + KptFunctionApiVersion = KptFunctionGroup + "/" + KptFunctionVersion + + NonKrmKind = "BuiltInNonKrm" +) diff --git a/functions/go/native-config-adaptor/fn/doc.go b/functions/go/native-config-adaptor/fn/doc.go new file mode 100644 index 000000000..066f79f20 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/doc.go @@ -0,0 +1,78 @@ +// Copyright 2022 Google LLC +// +// 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 fn provides the SDK to write KRM functions. + +Before you start + +This fn SDK requires some basic KRM function Specification knowledge. To make the best usage of your time, we recommend +you to be familiar with "ResourceList" before moving forward. + + The KRM Function Specification, or "ResourceList", defines the standards of the inter-process communication between + the orchestrator (i.e. kpt CLI) and functions. + +See KRM Function Specification reference in https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md + +KRM Function + +A KRM function can mutate and/or validate Kubernetes resources in a ResourceList. + +The ResourceList type and the KubeObject type are the core parts of this package. +The ResourceList type maps to the ResourceList in the function spec. + +Read more about how to use KRM functions in https://kpt.dev/book/04-using-functions/ + +Read more about how to develop a KRM function in https://kpt.dev/book/05-developing-functions/ + +A general workflow is: + 1. Reads the "ResourceList" object from STDIN. + 2. Gets the function configs from the "ResourceList.FunctionConfig". + 3. Mutate or validate the Kubernetes YAML resources from the "ResourceList.Items" field with the function configs. + 4. Writes the modified "ResourceList" to STDOUT. + 5. Write function message to "ResourceList.Results" with severity "Info", "Warning" or "Error" + +KubeObject + +The KubeObject is the basic unit to perform operations on KRM resources. + +In the "AsMain", both "Items" and "FunctionConfig" +are converted to the KubeObject(s). + +If you are familiar with unstructured.Unstructured, using KubeObject is as simple as using unstructured.Unstructured. +You can call function like `NestedStringOrDie` `SetNestedStringMap`, etc. + +Except that KubeObject will not have pass-in interface arguments, nor will return an interface. +Instead, you shall treat each KubeObject field (slice, or non-string map)as SubObject. + +SubObject also have most of the KubeObject methods, except the MetaType or NameType specific methods like "GetNamespace", "SetLabel". +This is because SubObject is designed as a sub object of KubeObject. SubObject to KubeObject is like `spec` section to `Deployment`. +You can get the Deployment name from `metadata.name`, KubeObject.GetName() or KubeObject.NestedString("metadata", "name"). +But you cannot get "metadata.name" from a Deployment "spec". For "spec" SubObject, you can get the ".replicas" field by +SubObject.NestedInt64("replicas") + +Besides unstructured style, another way to use KubeObject is to purely work on the KubeObject/SubObject by calling +"GetMap", "GetSlice", "UpsertMap" which expects the return to be SubObject(s) pointer. + +AsMain + +"AsMain" is the main entrypoint. In most cases, you only need to provide the mutator or validation logic and have AsMain +handles the ResourceList parsing, KRM resource field type detection, read from STDIN and write to STDOUT. + +"AsMain" accepts a struct that either implement the ResourceListProcessor interface or Runner interface. + +See github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/examples for detailed usage. + +*/ +package fn diff --git a/functions/go/native-config-adaptor/fn/errors.go b/functions/go/native-config-adaptor/fn/errors.go new file mode 100644 index 000000000..7b687134c --- /dev/null +++ b/functions/go/native-config-adaptor/fn/errors.go @@ -0,0 +1,73 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "strings" +) + +// ErrMissingFnConfig raises error if a required functionConfig is missing. +type ErrMissingFnConfig struct{} + +func (ErrMissingFnConfig) Error() string { + return "unable to find the functionConfig in the resourceList" +} + +// errKubeObjectFields raises if the KubeObject operation panics. +type errKubeObjectFields struct { + obj *KubeObject + fields []string +} + +func (e *errKubeObjectFields) Error() string { + return fmt.Sprintf("Resource(apiVersion=%v, kind=%v, Name=%v) has unmatched field type: `%v", + e.obj.GetAPIVersion(), e.obj.GetKind(), e.obj.GetName(), strings.Join(e.fields, "/")) +} + +// errSubObjectFields raises if the SubObject operation panics. +type errSubObjectFields struct { + fields []string +} + +func (e *errSubObjectFields) Error() string { + return fmt.Sprintf("SubObject has unmatched field type: `%v", strings.Join(e.fields, "/")) +} + +type errResultEnd struct { + obj *KubeObject + message string +} + +func (e *errResultEnd) Error() string { + if e.obj != nil { + return fmt.Sprintf("function is terminated by %v: %v", e.obj.ShortString(), e.message) + } + return fmt.Sprintf("function is terminated: %v", e.message) +} + +type ErrAttemptToTouchUpstreamIdentifier struct{} + +func (ErrAttemptToTouchUpstreamIdentifier) Error() string { + return fmt.Sprintf("annotation %v is managed by kpt and should not be modified", UpstreamIdentifier) +} + +type ErrInternalAnnotation struct { + Message string +} + +func (e *ErrInternalAnnotation) Error() string { + return e.Message +} diff --git a/functions/go/native-config-adaptor/fn/functionGenerator.go b/functions/go/native-config-adaptor/fn/functionGenerator.go new file mode 100644 index 000000000..2290cbace --- /dev/null +++ b/functions/go/native-config-adaptor/fn/functionGenerator.go @@ -0,0 +1,5 @@ +package fn + +type Generator interface { + Generate(context *Context, functionConfig *KubeObject, items KubeObjects) KubeObjects +} diff --git a/functions/go/native-config-adaptor/fn/functionrunner.go b/functions/go/native-config-adaptor/fn/functionrunner.go new file mode 100644 index 000000000..e3ffa726c --- /dev/null +++ b/functions/go/native-config-adaptor/fn/functionrunner.go @@ -0,0 +1,19 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +type Runner interface { + Run(context *Context, functionConfig *KubeObject, items KubeObjects) +} diff --git a/functions/go/native-config-adaptor/fn/generatorProcessor.go b/functions/go/native-config-adaptor/fn/generatorProcessor.go new file mode 100644 index 000000000..5e6359d91 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/generatorProcessor.go @@ -0,0 +1,41 @@ +package fn + +import ( + "fmt" + "reflect" +) + +type generatorProcessor struct { + fnGenerator Generator +} + +func (r generatorProcessor) Process(rl *ResourceList) (bool, error) { + ctx := &Context{results: &rl.Results} + r.config(ctx, rl.FunctionConfig) + newItems := r.fnGenerator.Generate(ctx, rl.FunctionConfig, rl.Items) + if newItems != nil { + rl.Items = append(rl.Items, newItems...) + } + return true, nil +} + +func (r *generatorProcessor) config(ctx *Context, o *KubeObject) { + fnName := reflect.ValueOf(r.fnGenerator).Elem().Type().Name() + switch true { + case o.IsEmpty(): + ctx.Result("`FunctionConfig` is not given", Info) + case o.IsGVK("", "v1", "ConfigMap"): + data := o.NestedStringMapOrDie("data") + fnRunnerElem := reflect.ValueOf(r.fnGenerator).Elem() + for i := 0; i < fnRunnerElem.NumField(); i++ { + if fnRunnerElem.Field(i).Kind() == reflect.Map { + fnRunnerElem.Field(i).Set(reflect.ValueOf(data)) + break + } + } + case o.IsGVK("fn.kpt.dev", "v1alpha1", fnName): + o.AsOrDie(r.fnGenerator) + default: + ctx.ResultErrAndDie(fmt.Sprintf("unknown FunctionConfig `%v`, expect %v", o.GetKind(), fnName), o) + } +} diff --git a/functions/go/native-config-adaptor/fn/internal/document.go b/functions/go/native-config-adaptor/fn/internal/document.go new file mode 100644 index 000000000..3b8f75415 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/document.go @@ -0,0 +1,71 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "bytes" + "io" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type doc struct { + nodes []*yaml.Node +} + +func NewDoc(nodes ...*yaml.Node) *doc { + return &doc{nodes: nodes} +} + +func ParseDoc(b []byte) (*doc, error) { + br := bytes.NewReader(b) + + var nodes []*yaml.Node + decoder := yaml.NewDecoder(br) + for { + node := &yaml.Node{} + if err := decoder.Decode(node); err != nil { + if err == io.EOF { + break + } + return nil, err + } + nodes = append(nodes, node) + } + + return &doc{nodes: nodes}, nil +} + +func (d *doc) ToYAML() ([]byte, error) { + var w bytes.Buffer + encoder := yaml.NewEncoder(&w) + for _, node := range d.nodes { + if node.Kind == yaml.DocumentNode { + if len(node.Content) == 0 { + // These cause errors when we try to write them + continue + } + } + if err := encoder.Encode(node); err != nil { + return nil, err + } + } + + return w.Bytes(), nil +} + +func (d *doc) Elements() ([]*MapVariant, error) { + return ExtractObjects(d.nodes...) +} diff --git a/functions/go/native-config-adaptor/fn/internal/map.go b/functions/go/native-config-adaptor/fn/internal/map.go new file mode 100644 index 000000000..4da8ecf41 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/map.go @@ -0,0 +1,312 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "fmt" + "log" + "sort" + + "k8s.io/klog/v2" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func NewMap(node *yaml.Node) *MapVariant { + if node == nil { + node = &yaml.Node{ + Kind: yaml.MappingNode, + } + } + return &MapVariant{node: node} +} + +func NewStringMapVariant(m map[string]string) *MapVariant { + node := &yaml.Node{ + Kind: yaml.MappingNode, + } + for k, v := range m { + node.Content = append(node.Content, buildStringNode(k), buildStringNode(v)) + } + return &MapVariant{node: node} +} + +type MapVariant struct { + node *yaml.Node +} + +func (o *MapVariant) GetKind() variantKind { + return variantKindMap +} + +func (o *MapVariant) Node() *yaml.Node { + return o.node +} + +func (o *MapVariant) Entries() (map[string]variant, error) { + entries := make(map[string]variant) + + ynode := o.node + children := ynode.Content + if len(children)%2 != 0 { + return nil, fmt.Errorf("unexpected number of children for map %d", len(children)) + } + + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + valueNode := children[i+1] + + keyVariant := toVariant(keyNode) + valueVariant := toVariant(valueNode) + + switch keyVariant := keyVariant.(type) { + case *scalarVariant: + sv, isString := keyVariant.StringValue() + if isString { + entries[sv] = valueVariant + } else { + return nil, fmt.Errorf("key was not a string %v", keyVariant) + } + default: + return nil, fmt.Errorf("unexpected variant kind %T", keyVariant) + } + } + return entries, nil +} + +func asString(node *yaml.Node) (string, bool) { + if node.Kind == yaml.ScalarNode && (node.Tag == "!!str" || node.Tag == "") { + return node.Value, true + } + return "", false +} + +func (o *MapVariant) getVariant(key string) (variant, bool) { + valueNode, found := getValueNode(o.node, key) + if !found { + return nil, found + } + + v := toVariant(valueNode) + return v, true +} + +func getValueNode(m *yaml.Node, key string) (*yaml.Node, bool) { + children := m.Content + if len(children)%2 != 0 { + log.Fatalf("unexpected number of children for map %d", len(children)) + } + + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + + k, ok := asString(keyNode) + if ok && k == key { + valueNode := children[i+1] + return valueNode, true + } + } + return nil, false +} + +func (o *MapVariant) set(key string, val variant) { + o.setYAMLNode(key, val.Node()) +} + +func (o *MapVariant) setYAMLNode(key string, node *yaml.Node) { + children := o.node.Content + if len(children)%2 != 0 { + log.Fatalf("unexpected number of children for map %d", len(children)) + } + + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + + k, ok := asString(keyNode) + if ok && k == key { + // TODO: Copy comments? + oldNode := children[i+1] + children[i+1] = node + children[i+1].FootComment = oldNode.FootComment + children[i+1].HeadComment = oldNode.HeadComment + children[i+1].LineComment = oldNode.LineComment + return + } + } + + o.node.Content = append(o.node.Content, buildStringNode(key), node) +} + +func (o *MapVariant) remove(key string) (bool, error) { + removed := false + + children := o.node.Content + if len(children)%2 != 0 { + return false, fmt.Errorf("unexpected number of children for map %d", len(children)) + } + + var keep []*yaml.Node + for i := 0; i < len(children); i += 2 { + keyNode := children[i] + + k, ok := asString(keyNode) + if ok && k == key { + removed = true + continue + } + + keep = append(keep, children[i], children[i+1]) + } + + o.node.Content = keep + + return removed, nil +} + +// remove field metadata.creationTimestamp when it's null. +func (o *MapVariant) cleanupCreationTimestamp() { + if o.node.Kind != yaml.MappingNode { + return + } + scalar, found, err := o.GetNestedScalar("metadata", "creationTimestamp") + if err != nil || !found { + return + } + if scalar.IsNull() { + _, _ = o.RemoveNestedField("metadata", "creationTimestamp") + } +} + +// sortFields tried to sort fields that it understands. e.g. data should come +// after apiVersion, kind and metadata in corev1.ConfigMap. +func (o *MapVariant) sortFields() error { + return sortFields(o.node) +} + +func sortFields(ynode *yaml.Node) error { + if ynode.Kind == yaml.SequenceNode { + for _, child := range ynode.Content { + if err := sortFields(child); err != nil { + return err + } + } + return nil + } + if ynode.Kind != yaml.MappingNode { + return nil + } + + pairs, err := ynodeToYamlKeyValuePairs(ynode) + if err != nil { + return fmt.Errorf("unable to sort fields in yaml: %w", err) + } + for _, pair := range pairs { + if err = sortFields(pair.value); err != nil { + return err + } + } + sort.Sort(pairs) + ynode.Content = yamlKeyValuePairsToYnode(pairs) + return nil +} + +func ynodeToYamlKeyValuePairs(ynode *yaml.Node) (yamlKeyValuePairs, error) { + if len(ynode.Content)%2 != 0 { + return nil, fmt.Errorf("invalid number of nodes: %d", len(ynode.Content)) + } + + var pairs yamlKeyValuePairs + for i := 0; i < len(ynode.Content); i += 2 { + pairs = append(pairs, &yamlKeyValuePair{name: ynode.Content[i], value: ynode.Content[i+1]}) + } + return pairs, nil +} + +func yamlKeyValuePairsToYnode(pairs yamlKeyValuePairs) []*yaml.Node { + var nodes []*yaml.Node + for _, pair := range pairs { + nodes = append(nodes, pair.name, pair.value) + } + return nodes +} + +type yamlKeyValuePair struct { + name *yaml.Node + value *yaml.Node +} + +type yamlKeyValuePairs []*yamlKeyValuePair + +func (nodes yamlKeyValuePairs) Len() int { return len(nodes) } + +func (nodes yamlKeyValuePairs) Less(i, j int) bool { + iIndex, iFound := yaml.FieldOrder[nodes[i].name.Value] + jIndex, jFound := yaml.FieldOrder[nodes[j].name.Value] + if iFound && jFound { + return iIndex < jIndex + } + if iFound { + return true + } + if jFound { + return false + } + + if nodes[i].name != nodes[j].name { + return nodes[i].name.Value < nodes[j].name.Value + } + return false +} + +func (nodes yamlKeyValuePairs) Swap(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] } + +// UpsertMap will return the field as a map if it exists and is a map, +// otherwise it will insert a map at the specified field. +// Note that if the value exists but is not a map, it will be replaced with a map. +func (o *MapVariant) UpsertMap(field string) *MapVariant { + m := o.GetMap(field) + if m != nil { + return m + } + + keyNode := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: field, + Tag: "!!str", + } + valueNode := &yaml.Node{ + Kind: yaml.MappingNode, + } + o.node.Content = append(o.node.Content, keyNode, valueNode) + return &MapVariant{node: valueNode} +} + +// GetMap will return the field as a map if it exists and is a map, +// otherwise it will return nil. +// Note that if the value exists but is not a map, nil will be returned. +func (o *MapVariant) GetMap(field string) *MapVariant { + node, found := o.getVariant(field) + + if found { + switch node := node.(type) { + case *MapVariant: + return node + + default: + klog.Warningf("getting value of unexpected type, got %T, want map", node) + } + } + + return nil +} diff --git a/functions/go/native-config-adaptor/fn/internal/maphelpers.go b/functions/go/native-config-adaptor/fn/internal/maphelpers.go new file mode 100644 index 000000000..6af95996d --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/maphelpers.go @@ -0,0 +1,227 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "fmt" +) + +func (o *MapVariant) GetNestedValue(fields ...string) (variant, bool, error) { + current := o + n := len(fields) + for i := 0; i < n; i++ { + entry, found := current.getVariant(fields[i]) + if !found { + return nil, found, nil + } + + if i == n-1 { + return entry, true, nil + } + entryM, ok := entry.(*MapVariant) + if !ok { + return nil, found, fmt.Errorf("wrong type, got: %T", entry) + } + current = entryM + } + return nil, false, fmt.Errorf("unexpected code reached") +} + +func (o *MapVariant) SetNestedValue(val variant, fields ...string) error { + current := o + n := len(fields) + var err error + for i := 0; i < n; i++ { + if i == n-1 { + current.set(fields[i], val) + } else { + current, _, err = current.getMap(fields[i], true) + if err != nil { + return err + } + } + } + return nil +} + +func (o *MapVariant) GetNestedMap(fields ...string) (*MapVariant, bool, error) { + v, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + mv, ok := v.(*MapVariant) + if !ok { + return nil, found, fmt.Errorf("wrong type, got: %T", v) + } + return mv, found, err +} + +func (o *MapVariant) SetNestedMap(m *MapVariant, fields ...string) error { + return o.SetNestedValue(m, fields...) +} + +func (o *MapVariant) GetNestedStringMap(fields ...string) (map[string]string, bool, error) { + v, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + children := v.Node().Content + if len(children)%2 != 0 { + return nil, found, fmt.Errorf("invalid yaml map node") + } + m := make(map[string]string, len(children)/2) + for i := 0; i < len(children); i = i + 2 { + m[children[i].Value] = children[i+1].Value + } + return m, found, nil +} + +func (o *MapVariant) SetNestedStringMap(m map[string]string, fields ...string) error { + return o.SetNestedMap(NewStringMapVariant(m), fields...) +} + +func (o *MapVariant) GetNestedScalar(fields ...string) (*scalarVariant, bool, error) { + node, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + nodeS, ok := node.(*scalarVariant) + if !ok { + return nil, found, fmt.Errorf("incorrect type, was %T", node) + } + return nodeS, found, nil +} + +func (o *MapVariant) GetNestedString(fields ...string) (string, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return "", found, err + } + sv, isString := scalar.StringValue() + if isString { + return sv, found, nil + } + return "", found, fmt.Errorf("node was not a string, was %v", scalar.node.Tag) +} + +func (o *MapVariant) SetNestedString(s string, fields ...string) error { + return o.SetNestedValue(newStringScalarVariant(s), fields...) +} + +func (o *MapVariant) GetNestedBool(fields ...string) (bool, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return false, found, err + } + bv, isBool := scalar.BoolValue() + if isBool { + return bv, found, nil + } + return false, found, fmt.Errorf("node was not a bool, was %v", scalar.Node().Tag) +} + +func (o *MapVariant) SetNestedBool(b bool, fields ...string) error { + return o.SetNestedValue(newBoolScalarVariant(b), fields...) +} + +func (o *MapVariant) GetNestedInt(fields ...string) (int, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return 0, found, err + } + iv, isInt := scalar.IntValue() + if isInt { + return iv, found, nil + } + return 0, found, fmt.Errorf("node was not a int, was %v", scalar.node.Tag) +} + +func (o *MapVariant) SetNestedInt(i int, fields ...string) error { + return o.SetNestedValue(newIntScalarVariant(i), fields...) +} + +func (o *MapVariant) GetNestedFloat(fields ...string) (float64, bool, error) { + scalar, found, err := o.GetNestedScalar(fields...) + if err != nil || !found { + return 0, found, err + } + fv, isFloat := scalar.FloatValue() + if isFloat { + return fv, found, nil + } + return 0, found, fmt.Errorf("node was not a float, was %v", scalar.node.Tag) +} + +func (o *MapVariant) SetNestedFloat(f float64, fields ...string) error { + return o.SetNestedValue(newFloatScalarVariant(f), fields...) +} + +func (o *MapVariant) GetNestedSlice(fields ...string) (*sliceVariant, bool, error) { + node, found, err := o.GetNestedValue(fields...) + if err != nil || !found { + return nil, found, err + } + nodeS, ok := node.(*sliceVariant) + if !ok { + return nil, found, fmt.Errorf("incorrect type, was %T", node) + } + return nodeS, found, err +} + +func (o *MapVariant) SetNestedSlice(s *sliceVariant, fields ...string) error { + return o.SetNestedValue(s, fields...) +} + +func (o *MapVariant) RemoveNestedField(fields ...string) (bool, error) { + current := o + n := len(fields) + for i := 0; i < n; i++ { + entry, found := current.getVariant(fields[i]) + if !found { + return false, nil + } + + if i == n-1 { + return current.remove(fields[i]) + } + switch entry := entry.(type) { + case *MapVariant: + current = entry + default: + return false, fmt.Errorf("value is of unexpected type %T", entry) + } + } + return false, fmt.Errorf("unexpected code reached") +} + +func (o *MapVariant) getMap(field string, create bool) (*MapVariant, bool, error) { + node, found := o.getVariant(field) + + if !found { + if !create { + return nil, found, nil + } + keyNode := buildStringNode(field) + valueNode := buildMappingNode() + o.node.Content = append(o.node.Content, keyNode, valueNode) + valueVariant := &MapVariant{node: valueNode} + return valueVariant, found, nil + } + + if node, ok := node.(*MapVariant); ok { + return node, found, nil + } + return nil, found, fmt.Errorf("incorrect type, was %T", node) +} diff --git a/functions/go/native-config-adaptor/fn/internal/namespace.go b/functions/go/native-config-adaptor/fn/internal/namespace.go new file mode 100644 index 000000000..64aec8387 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/namespace.go @@ -0,0 +1,91 @@ +package internal + +import ( + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// PrecomputedIsNamespaceScoped copies the sigs.k8s.io/kustomize/kyaml/openapi precomputedIsNamespaceScoped +var PrecomputedIsNamespaceScoped = map[yaml.TypeMeta]bool{ + {APIVersion: "admissionregistration.k8s.io/v1", Kind: "MutatingWebhookConfiguration"}: false, + {APIVersion: "admissionregistration.k8s.io/v1", Kind: "ValidatingWebhookConfiguration"}: false, + {APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration"}: false, + {APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "ValidatingWebhookConfiguration"}: false, + {APIVersion: "apiextensions.k8s.io/v1", Kind: "CustomResourceDefinition"}: false, + {APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition"}: false, + {APIVersion: "apiregistration.k8s.io/v1", Kind: "APIService"}: false, + {APIVersion: "apiregistration.k8s.io/v1beta1", Kind: "APIService"}: false, + {APIVersion: "apps/v1", Kind: "ControllerRevision"}: true, + {APIVersion: "apps/v1", Kind: "DaemonSet"}: true, + {APIVersion: "apps/v1", Kind: "Deployment"}: true, + {APIVersion: "apps/v1", Kind: "ReplicaSet"}: true, + {APIVersion: "apps/v1", Kind: "StatefulSet"}: true, + {APIVersion: "autoscaling/v1", Kind: "HorizontalPodAutoscaler"}: true, + {APIVersion: "autoscaling/v1", Kind: "Scale"}: true, + {APIVersion: "autoscaling/v2beta1", Kind: "HorizontalPodAutoscaler"}: true, + {APIVersion: "autoscaling/v2beta2", Kind: "HorizontalPodAutoscaler"}: true, + {APIVersion: "batch/v1", Kind: "CronJob"}: true, + {APIVersion: "batch/v1", Kind: "Job"}: true, + {APIVersion: "batch/v1beta1", Kind: "CronJob"}: true, + {APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"}: false, + {APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"}: false, + {APIVersion: "coordination.k8s.io/v1", Kind: "Lease"}: true, + {APIVersion: "coordination.k8s.io/v1beta1", Kind: "Lease"}: true, + {APIVersion: "discovery.k8s.io/v1", Kind: "EndpointSlice"}: true, + {APIVersion: "discovery.k8s.io/v1beta1", Kind: "EndpointSlice"}: true, + {APIVersion: "events.k8s.io/v1", Kind: "Event"}: true, + {APIVersion: "events.k8s.io/v1beta1", Kind: "Event"}: true, + {APIVersion: "extensions/v1beta1", Kind: "Ingress"}: true, + {APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "FlowSchema"}: false, + {APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "PriorityLevelConfiguration"}: false, + {APIVersion: "networking.k8s.io/v1", Kind: "Ingress"}: true, + {APIVersion: "networking.k8s.io/v1", Kind: "IngressClass"}: false, + {APIVersion: "networking.k8s.io/v1", Kind: "NetworkPolicy"}: true, + {APIVersion: "networking.k8s.io/v1beta1", Kind: "Ingress"}: true, + {APIVersion: "networking.k8s.io/v1beta1", Kind: "IngressClass"}: false, + {APIVersion: "node.k8s.io/v1", Kind: "RuntimeClass"}: false, + {APIVersion: "node.k8s.io/v1beta1", Kind: "RuntimeClass"}: false, + {APIVersion: "policy/v1", Kind: "PodDisruptionBudget"}: true, + {APIVersion: "policy/v1beta1", Kind: "PodDisruptionBudget"}: true, + {APIVersion: "policy/v1beta1", Kind: "PodSecurityPolicy"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRoleBinding"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"}: true, + {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "RoleBinding"}: true, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRole"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRoleBinding"}: false, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "Role"}: true, + {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "RoleBinding"}: true, + {APIVersion: "scheduling.k8s.io/v1", Kind: "PriorityClass"}: false, + {APIVersion: "scheduling.k8s.io/v1beta1", Kind: "PriorityClass"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "CSIDriver"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "CSINode"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "StorageClass"}: false, + {APIVersion: "storage.k8s.io/v1", Kind: "VolumeAttachment"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIDriver"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSINode"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIStorageCapacity"}: true, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "StorageClass"}: false, + {APIVersion: "storage.k8s.io/v1beta1", Kind: "VolumeAttachment"}: false, + {APIVersion: "v1", Kind: "ComponentStatus"}: false, + {APIVersion: "v1", Kind: "ConfigMap"}: true, + {APIVersion: "v1", Kind: "Endpoints"}: true, + {APIVersion: "v1", Kind: "Event"}: true, + {APIVersion: "v1", Kind: "LimitRange"}: true, + {APIVersion: "v1", Kind: "Namespace"}: false, + {APIVersion: "v1", Kind: "Node"}: false, + {APIVersion: "v1", Kind: "NodeProxyOptions"}: false, + {APIVersion: "v1", Kind: "PersistentVolume"}: false, + {APIVersion: "v1", Kind: "PersistentVolumeClaim"}: true, + {APIVersion: "v1", Kind: "Pod"}: true, + {APIVersion: "v1", Kind: "PodAttachOptions"}: true, + {APIVersion: "v1", Kind: "PodExecOptions"}: true, + {APIVersion: "v1", Kind: "PodPortForwardOptions"}: true, + {APIVersion: "v1", Kind: "PodProxyOptions"}: true, + {APIVersion: "v1", Kind: "PodTemplate"}: true, + {APIVersion: "v1", Kind: "ReplicationController"}: true, + {APIVersion: "v1", Kind: "ResourceQuota"}: true, + {APIVersion: "v1", Kind: "Secret"}: true, + {APIVersion: "v1", Kind: "Service"}: true, + {APIVersion: "v1", Kind: "ServiceAccount"}: true, + {APIVersion: "v1", Kind: "ServiceProxyOptions"}: true, +} diff --git a/functions/go/native-config-adaptor/fn/internal/scalar.go b/functions/go/native-config-adaptor/fn/internal/scalar.go new file mode 100644 index 000000000..5aa15c629 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/scalar.go @@ -0,0 +1,117 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "strconv" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +const ( + tagString = "!!str" + tagBool = "!!bool" + tagInt = "!!int" + tagFloat = "!!float" + tagNull = "!!null" +) + +type scalarVariant struct { + node *yaml.Node +} + +func (v *scalarVariant) GetKind() variantKind { + return variantKindScalar +} + +func newStringScalarVariant(s string) *scalarVariant { + return &scalarVariant{ + node: buildStringNode(s), + } +} + +func newBoolScalarVariant(b bool) *scalarVariant { + return &scalarVariant{ + node: buildBoolNode(b), + } +} + +func newIntScalarVariant(i int) *scalarVariant { + return &scalarVariant{ + node: buildIntNode(i), + } +} + +func newFloatScalarVariant(f float64) *scalarVariant { + return &scalarVariant{ + node: buildFloatNode(f), + } +} + +func (v *scalarVariant) IsNull() bool { + return v.node.Tag == tagNull +} + +func (v *scalarVariant) StringValue() (string, bool) { + switch v.node.Tag { + case tagString: + return v.node.Value, true + default: + return "", false + } +} + +func (v *scalarVariant) BoolValue() (bool, bool) { + switch v.node.Tag { + case tagBool: + b, err := strconv.ParseBool(v.node.Value) + if err != nil { + return b, false + } + return b, true + default: + return false, false + } +} + +func (v *scalarVariant) IntValue() (int, bool) { + switch v.node.Tag { + case tagInt: + i, err := strconv.Atoi(v.node.Value) + if err != nil { + return i, false + } + return i, true + default: + return 0, false + } +} + +func (v *scalarVariant) FloatValue() (float64, bool) { + switch v.node.Tag { + case tagFloat: + f, err := strconv.ParseFloat(v.node.Value, 64) + if err != nil { + return f, false + } + return f, true + default: + return 0, false + } +} + +func (v *scalarVariant) Node() *yaml.Node { + return v.node +} diff --git a/functions/go/native-config-adaptor/fn/internal/slice.go b/functions/go/native-config-adaptor/fn/internal/slice.go new file mode 100644 index 000000000..1c2f64f20 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/slice.go @@ -0,0 +1,51 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type sliceVariant struct { + node *yaml.Node +} + +func NewSliceVariant(s ...variant) *sliceVariant { + node := buildSequenceNode() + for _, v := range s { + node.Content = append(node.Content, v.Node()) + } + return &sliceVariant{node: node} +} + +func (v *sliceVariant) GetKind() variantKind { + return variantKindSlice +} + +func (v *sliceVariant) Node() *yaml.Node { + return v.node +} + +func (v *sliceVariant) Clear() { + v.node.Content = nil +} + +func (v *sliceVariant) Elements() ([]*MapVariant, error) { + return ExtractObjects(v.node.Content...) +} + +func (v *sliceVariant) Add(node variant) { + v.node.Content = append(v.node.Content, node.Node()) +} diff --git a/functions/go/native-config-adaptor/fn/internal/test/go.mod b/functions/go/native-config-adaptor/fn/internal/test/go.mod new file mode 100644 index 000000000..86bfe2f2a --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/test/go.mod @@ -0,0 +1,50 @@ +module github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/internal_test + +go 1.17 + +replace github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0 => ../.. + +require ( + github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0 + github.com/stretchr/testify v1.7.1 + k8s.io/api v0.24.0 + k8s.io/apimachinery v0.24.0 + sigs.k8s.io/kustomize/kyaml v0.13.7-0.20220418212550-9d5491c2e20c +) + +require ( + github.com/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20220720212527-133180134b93 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.2.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + 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/xlab/treeprint v1.1.0 // indirect + golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + k8s.io/klog/v2 v2.60.1 // indirect + k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect +) diff --git a/functions/go/native-config-adaptor/fn/internal/test/go.sum b/functions/go/native-config-adaptor/fn/internal/test/go.sum new file mode 100644 index 000000000..a64ccc637 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/test/go.sum @@ -0,0 +1,723 @@ +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/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20220720212527-133180134b93 h1:c1GhPzYzU2a3E+/2WB9OxoljK2pNYfsBF5Fz9GkdYXs= +github.com/GoogleContainerTools/kpt-functions-sdk/go/api v0.0.0-20220720212527-133180134b93/go.mod h1:gkK43tTaPXFNASpbIbQImzhmt1hdcdin++kvzTblykc= +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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +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.12.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/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +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-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +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/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/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/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +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/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.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +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 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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/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/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +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/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-20180906233101-161cd47e91fd/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-20200520004742-59133d7f0dd7/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/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +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-20180909124046-d0be0721c37e/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-20190904154756-749cb33beabd/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-20191120155948-bd437916bb0e/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-20200519105757-fe76b779f299/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-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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-20200505023115-26f46d2f7ef8/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/tools v0.1.5/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= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/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-20201019141844-1ed22bb0c154/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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.3.0/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/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg= +k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= +k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ= +k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 h1:nqYOUleKLC/0P1zbU29F5q6aoezM6MOAVz+iyfQbZ5M= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +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/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= +sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/kustomize/kyaml v0.13.7-0.20220418212550-9d5491c2e20c h1:Y0cW/MVbKH9jRlMbpLe/4gs2m6qteP1pUGP+JkWcGdA= +sigs.k8s.io/kustomize/kyaml v0.13.7-0.20220418212550-9d5491c2e20c/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +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/native-config-adaptor/fn/internal/test/maphelper_test.go b/functions/go/native-config-adaptor/fn/internal/test/maphelper_test.go new file mode 100644 index 000000000..536af6da0 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/test/maphelper_test.go @@ -0,0 +1,192 @@ +// Copyright 2022 Google LLC +// +// 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 internal_test + +import ( + "bytes" + "testing" + + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/internal" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + yaml2 "sigs.k8s.io/kustomize/kyaml/yaml" +) + +const deploymentYaml = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx + env: prod + finalizers: + - foo + - bar +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 +` + +func parseRaw(in []byte) ([]*internal.MapVariant, error) { + d := yaml2.NewDecoder(bytes.NewBuffer(in)) + node := &yaml2.Node{} + err := d.Decode(node) + if err != nil { + return nil, err + } + return internal.ExtractObjects(node) +} + +func TestHelpers(t *testing.T) { + rawmvs, _ := parseRaw([]byte(deploymentYaml)) + assert.Len(t, rawmvs, 1, "expect 1 object after parsing") + mv := rawmvs[0] + + name, found, err := mv.GetNestedString("metadata", "name") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "nginx-deployment", name) + + ns, found, err := mv.GetNestedString("metadata", "namespace") + assert.NoError(t, err) + assert.False(t, found) + assert.Equal(t, "", ns) + err = mv.SetNestedString("test-ns", "metadata", "namespace") + assert.NoError(t, err) + ns, found, err = mv.GetNestedString("metadata", "namespace") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "test-ns", ns) + + replicas, found, err := mv.GetNestedInt("spec", "replicas") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, 3, replicas) + err = mv.SetNestedInt(10, "spec", "replicas") + assert.NoError(t, err) + replicas, found, err = mv.GetNestedInt("spec", "replicas") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, 10, replicas) + + notExistInt, found, err := mv.GetNestedInt("spec", "foo") + assert.NoError(t, err) + assert.False(t, found) + assert.Equal(t, 0, notExistInt) + + labels, found, err := mv.GetNestedStringMap("metadata", "labels") + assert.NoError(t, err) + assert.True(t, found) + assert.Len(t, labels, 2) + assert.Equal(t, map[string]string{"app": "nginx", "env": "prod"}, labels) + + notExistStringMap, found, err := mv.GetNestedStringMap("metadata", "something") + assert.NoError(t, err) + assert.False(t, found) + assert.Len(t, notExistStringMap, 0) + var emptyMap map[string]string + assert.Equal(t, emptyMap, notExistStringMap) + + annotations, found, err := mv.GetNestedStringMap("metadata", "annotations") + assert.NoError(t, err) + assert.False(t, found) + assert.Len(t, annotations, 0) + assert.Equal(t, emptyMap, annotations) + err = mv.SetNestedStringMap(map[string]string{"hello": "world"}, "metadata", "annotations") + assert.NoError(t, err) + annotation, found, err := mv.GetNestedString("metadata", "annotations", "hello") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "world", annotation) + + metadata, found, err := mv.GetNestedMap("metadata") + assert.NoError(t, err) + assert.True(t, found) + err = mv.SetNestedMap(metadata, "spec", "template", "metadata") + assert.NoError(t, err) + label, found, err := mv.GetNestedString("spec", "template", "metadata", "labels", "env") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "prod", label) + + container := map[string]string{ + "name": "logger", + "image": "my-logger", + } + containers, found, err := mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + mvs, err := containers.Elements() + assert.NoError(t, err) + assert.Len(t, mvs, 1) + containers.Add(internal.NewStringMapVariant(container)) + containers, found, err = mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + mvs, err = containers.Elements() + assert.NoError(t, err) + assert.Len(t, mvs, 2) + + containers, found, err = mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + conts, err := containers.Elements() + assert.NoError(t, err) + for i, cont := range conts { + name, found, err = cont.GetNestedString("name") + if err == nil && found && name == "nginx" { + err = conts[i].SetNestedString("nginx:1.21.3", "image") + assert.NoError(t, err) + } + } + containers, found, err = mv.GetNestedSlice("spec", "template", "spec", "containers") + assert.NoError(t, err) + assert.True(t, found) + mvs, err = containers.Elements() + assert.NoError(t, err) + assert.Len(t, mvs, 2) + img, found, err := mvs[0].GetNestedString("image") + assert.NoError(t, err) + assert.True(t, found) + assert.Equal(t, "nginx:1.21.3", img) + + trueVar := true + falseVar := false + sc := corev1.SecurityContext{ + RunAsNonRoot: &trueVar, + AllowPrivilegeEscalation: &falseVar, + } + scmv, err := internal.TypedObjectToMapVariant(sc) + assert.NoError(t, err) + err = mv.SetNestedMap(scmv, "spec", "template", "spec", "securityContext") + assert.NoError(t, err) + + runAsNonRoot, found, err := mv.GetNestedBool("spec", "template", "spec", "securityContext", "runAsNonRoot") + assert.NoError(t, err) + assert.True(t, found) + assert.True(t, runAsNonRoot) +} diff --git a/functions/go/native-config-adaptor/fn/internal/test/variant_test.go b/functions/go/native-config-adaptor/fn/internal/test/variant_test.go new file mode 100644 index 000000000..1cf13ad95 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/test/variant_test.go @@ -0,0 +1,274 @@ +// Copyright 2022 Google LLC +// +// 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 internal_test + +import ( + "strings" + "testing" + + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn/internal" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestMapVariantToTypedObject(t *testing.T) { + testcases := []struct { + name string + src string + dst interface{} + expected interface{} + }{ + { + name: "k8s built-in types", + src: `apiVersion: v1 +kind: ConfigMap +metadata: + name: my-cm + namespace: my-ns +data: + foo: bar +`, + dst: &corev1.ConfigMap{}, + expected: &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm", + Namespace: "my-ns", + }, + Data: map[string]string{ + "foo": "bar", + }, + }, + }, + { + name: "crd type with metav1.ObjectMeta", + src: `apiVersion: example.co/v1 +kind: Foo +metadata: + name: my-foo +desiredReplicas: 1 +`, + dst: &Foo{}, + expected: &Foo{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Foo", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-foo", + }, + DesiredReplicas: 1, + }, + }, + { + name: "crd type with yaml.ResourceIdentifier", + src: `apiVersion: example.co/v1 +kind: Bar +metadata: + name: my-bar +desiredReplicas: 1 +`, + dst: &Bar{}, + expected: &Bar{ + ResourceMeta: yaml.ResourceMeta{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Bar", + }, + ObjectMeta: yaml.ObjectMeta{ + NameMeta: yaml.NameMeta{ + Name: "my-bar", + }, + }, + }, + DesiredReplicas: 1, + }, + }, + } + + for _, tc := range testcases { + rn := yaml.MustParse(tc.src) + mv := internal.NewMap(rn.YNode()) + err := internal.MapVariantToTypedObject(mv, tc.dst) + assert.NoError(t, err) + assert.Equal(t, tc.expected, tc.dst) + } +} + +func TestNewFromTypedObject(t *testing.T) { + testcases := []struct { + name string + input interface{} + expected string + }{ + { + name: "k8s built-in types", + input: &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm", + Namespace: "my-ns", + }, + Data: map[string]string{ + "foo": "bar", + }, + }, + expected: `apiVersion: v1 +kind: ConfigMap +metadata: + name: my-cm + namespace: my-ns +data: + foo: bar +`, + }, + { + name: "crd type with metav1.ObjectMeta", + input: &Foo{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Foo", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-foo", + }, + DesiredReplicas: 1, + }, + expected: `apiVersion: example.co/v1 +kind: Foo +metadata: + name: my-foo +desiredReplicas: 1 +`, + }, + { + name: "crd type with yaml.ResourceIdentifier", + input: &Bar{ + ResourceMeta: yaml.ResourceMeta{ + TypeMeta: yaml.TypeMeta{ + APIVersion: "example.co/v1", + Kind: "Bar", + }, + ObjectMeta: yaml.ObjectMeta{ + NameMeta: yaml.NameMeta{ + Name: "my-bar", + }, + }, + }, + DesiredReplicas: 1, + }, + expected: `apiVersion: example.co/v1 +kind: Bar +metadata: + name: my-bar +desiredReplicas: 1 +`, + }, + { + name: "k8s built in pod type", + input: &corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "container", + Image: "test", + }}, + }, + }, + expected: `apiVersion: v1 +kind: Pod +metadata: + name: my-pod +spec: + containers: + - name: container + image: test + resources: {} +status: {} +`, + }, + } + + for _, tc := range testcases { + mv, err := fn.NewFromTypedObject(tc.input) + assert.NoError(t, err) + s := mv.String() + assert.Equal(t, tc.expected, s) + } +} + +func TestBadNewFromTypedObject(t *testing.T) { + input := []corev1.ConfigMap{ + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm", + Namespace: "my-ns", + }, + Data: map[string]string{ + "foo": "bar", + }, + }, + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cm2", + Namespace: "my-ns2", + }, + Data: map[string]string{ + "foo2": "bar2", + }, + }, + } + _, err := fn.NewFromTypedObject(input) + if err == nil { + t.Errorf("expect error, got nil") + } + if !strings.Contains(err.Error(), "got reflect.Slice") { + t.Errorf("got unexpected error %v", err) + } +} + +type Foo struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + DesiredReplicas int `json:"desiredReplicas,omitempty" yaml:"desiredReplicas,omitempty"` +} + +type Bar struct { + yaml.ResourceMeta `json:",inline" yaml:",inline"` + DesiredReplicas int `json:"desiredReplicas,omitempty" yaml:"desiredReplicas,omitempty"` +} diff --git a/functions/go/native-config-adaptor/fn/internal/variant.go b/functions/go/native-config-adaptor/fn/internal/variant.go new file mode 100644 index 000000000..0d993a47d --- /dev/null +++ b/functions/go/native-config-adaptor/fn/internal/variant.go @@ -0,0 +1,200 @@ +// Copyright 2022 Google LLC +// +// 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 internal + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +type variantKind string + +const ( + variantKindMap variantKind = "Map" + variantKindSlice variantKind = "Slice" + variantKindScalar variantKind = "Scalar" +) + +type variant interface { + GetKind() variantKind + Node() *yaml.Node +} + +// nodes are expected to be key1,value1,key2,value2,... +func buildMappingNode(nodes ...*yaml.Node) *yaml.Node { + return &yaml.Node{ + Kind: yaml.MappingNode, + Content: nodes, + } +} + +func buildSequenceNode(nodes ...*yaml.Node) *yaml.Node { + return &yaml.Node{ + Kind: yaml.SequenceNode, + Content: nodes, + } +} + +func buildStringNode(s string) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!str", + Value: s, + } +} + +func buildIntNode(i int) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!int", + Value: strconv.Itoa(i), + } +} + +func buildFloatNode(f float64) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!float", + Value: strconv.FormatFloat(f, 'f', -1, 64), + } +} + +func buildBoolNode(b bool) *yaml.Node { + return &yaml.Node{ + Kind: yaml.ScalarNode, + Tag: "!!bool", + Value: strconv.FormatBool(b), + } +} + +func toVariant(n *yaml.Node) variant { + switch n.Kind { + case yaml.ScalarNode: + return &scalarVariant{node: n} + case yaml.MappingNode: + return &MapVariant{node: n} + case yaml.SequenceNode: + return &sliceVariant{node: n} + + default: + panic("unhandled yaml node kind") + } +} + +func ExtractObjects(nodes ...*yaml.Node) ([]*MapVariant, error) { + var objects []*MapVariant + + for _, node := range nodes { + switch node.Kind { + case yaml.DocumentNode: + children, err := ExtractObjects(node.Content...) + if err != nil { + return nil, err + } + objects = append(objects, children...) + case yaml.MappingNode: + objects = append(objects, &MapVariant{node: node}) + default: + return nil, fmt.Errorf("unhandled node kind %v", node.Kind) + } + } + return objects, nil +} + +func TypedObjectToMapVariant(v interface{}) (*MapVariant, error) { + // The built-in types only have json tags. We can't simply do ynode.Encode(v), + // since it use the lowercased field name by default if no yaml tag is specified. + // This affects both k8s built-in types (e.g. appsv1.Deployment) and any types + // that depends on built-in types (e.g. metav1.ObjectMeta, corev1.PodTemplate). + // To work around it, we rely on the json tags. We first convert v to + // map[string]interface{} through json and then convert it to ynode. + node, err := func() (*yaml.Node, error) { + j, err := json.Marshal(v) + if err != nil { + return nil, err + } + var m map[string]interface{} + if err = json.Unmarshal(j, &m); err != nil { + return nil, err + } + + node := &yaml.Node{} + if err = node.Encode(m); err != nil { + return nil, err + } + return node, nil + }() + if err != nil { + return nil, fmt.Errorf("unable to convert strong typed object to yaml node: %w", err) + } + + mv := &MapVariant{node: node} + mv.cleanupCreationTimestamp() + err = mv.sortFields() + return mv, err +} + +func TypedObjectToSliceVariant(v interface{}) (*sliceVariant, error) { + // The built-in types only have json tags. We can't simply do ynode.Encode(v), + // since it use the lowercased field name by default if no yaml tag is specified. + // This affects both k8s built-in types (e.g. appsv1.Deployment) and any types + // that depends on built-in types (e.g. metav1.ObjectMeta, corev1.PodTemplate). + // To work around it, we rely on the json tags. We first convert v to + // map[string]interface{} through json and then convert it to ynode. + node, err := func() (*yaml.Node, error) { + j, err := json.Marshal(v) + if err != nil { + return nil, err + } + var l []interface{} + if err = json.Unmarshal(j, &l); err != nil { + return nil, err + } + + node := &yaml.Node{} + if err = node.Encode(l); err != nil { + return nil, err + } + return node, nil + }() + if err != nil { + return nil, fmt.Errorf("unable to convert strong typed object to yaml node: %w", err) + } + + return &sliceVariant{node: node}, nil +} + +func MapVariantToTypedObject(mv *MapVariant, ptr interface{}) error { + if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { + return fmt.Errorf("ptr must be a pointer to an object") + } + + // The built-in types only have json tags. We can't simply do mv.Node().Decode(ptr), + // since it use the lowercased field name by default if no yaml tag is specified. + // This affects both k8s built-in types (e.g. appsv1.Deployment) and any types + // that depends on built-in types (e.g. metav1.ObjectMeta, corev1.PodTemplate). + // To work around it, we rely on the json tags. We first convert mv to json + // and then unmarshal it to ptr. + j, err := yaml.NewRNode(mv.Node()).MarshalJSON() + if err != nil { + return err + } + err = json.Unmarshal(j, ptr) + return err +} diff --git a/functions/go/native-config-adaptor/fn/io.go b/functions/go/native-config-adaptor/fn/io.go new file mode 100644 index 000000000..34d9c9b95 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/io.go @@ -0,0 +1,74 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// byteReadWriter wraps kio.ByteReadWriter +type byteReadWriter struct { + kio.ByteReadWriter +} + +// Read decodes input bytes into a ResourceList +func (rw *byteReadWriter) Read() (*ResourceList, error) { + nodes, err := rw.ByteReadWriter.Read() + if err != nil { + return nil, err + } + var items KubeObjects + for _, n := range nodes { + obj, err := ParseKubeObject([]byte(n.MustString())) + if err != nil { + return nil, err + } + items = append(items, obj) + } + obj, err := ParseKubeObject([]byte(rw.ByteReadWriter.FunctionConfig.MustString())) + if err != nil { + return nil, err + } + return &ResourceList{ + Items: items, + FunctionConfig: obj, + }, nil +} + +// Write writes a ResourceList into bytes +func (rw *byteReadWriter) Write(rl *ResourceList) error { + if len(rl.Results) > 0 { + b, err := yaml.Marshal(rl.Results) + if err != nil { + return errors.Wrap(err) + } + y, err := yaml.Parse(string(b)) + if err != nil { + return errors.Wrap(err) + } + rw.Results = y + } + var nodes []*yaml.RNode + for _, item := range rl.Items { + node, err := yaml.Parse(item.String()) + if err != nil { + return err + } + nodes = append(nodes, node) + } + return rw.ByteReadWriter.Write(nodes) +} diff --git a/functions/go/native-config-adaptor/fn/log.go b/functions/go/native-config-adaptor/fn/log.go new file mode 100644 index 000000000..5d86389fd --- /dev/null +++ b/functions/go/native-config-adaptor/fn/log.go @@ -0,0 +1,28 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "os" +) + +func Log(in ...interface{}) { + fmt.Fprintln(os.Stderr, in...) +} + +func Logf(format string, in ...interface{}) { + fmt.Fprintf(os.Stderr, format, in...) +} diff --git a/functions/go/native-config-adaptor/fn/object.go b/functions/go/native-config-adaptor/fn/object.go new file mode 100644 index 000000000..4213c1fc6 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/object.go @@ -0,0 +1,1000 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "math" + "reflect" + "strconv" + "strings" + + v1 "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/api/kptfile/v1" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/fn/internal" + "sigs.k8s.io/kustomize/kyaml/kio/kioutil" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// KubeObject presents a k8s object. +type KubeObject struct { + SubObject +} + +// ParseKubeObject parses input byte slice to a KubeObject. +func ParseKubeObject(in []byte) (*KubeObject, error) { + doc, err := internal.ParseDoc(in) + if err != nil { + return nil, fmt.Errorf("failed to parse input bytes: %w", err) + } + objects, err := doc.Elements() + if err != nil { + return nil, fmt.Errorf("failed to extract objects: %w", err) + } + if len(objects) != 1 { + return nil, fmt.Errorf("expected exactly one object, got %d", len(objects)) + } + rlMap := objects[0] + return asKubeObject(rlMap), nil +} + +// GetOrDie gets the value for a nested field located by fields. A pointer must +// be passed in, and the value will be stored in ptr. If the field doesn't +// exist, the ptr will be set to nil. It will panic if it encounters any error. +func (o *SubObject) GetOrDie(ptr interface{}, fields ...string) { + _, err := o.Get(ptr, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// NestedBool returns the bool value, if the field exist and a potential error. +func (o *SubObject) NestedBool(fields ...string) (bool, bool, error) { + var val bool + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedBoolOrDie returns the bool value at `fields`. An empty string will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedBoolOrDie(fields ...string) bool { + val, _, err := o.NestedBool(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedString returns the string value, if the field exist and a potential error. +func (o *SubObject) NestedString(fields ...string) (string, bool, error) { + var val string + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedStringOrDie returns the string value at fields. An empty string will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedStringOrDie(fields ...string) string { + val, _, err := o.NestedString(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedFloat64 returns the float64 value, if the field exist and a potential error. +func (o *SubObject) NestedFloat64(fields ...string) (float64, bool, error) { + var val float64 + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedFloat64OrDie returns the string value at fields. 0 will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedFloat64OrDie(fields ...string) float64 { + val, _, err := o.NestedFloat64(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedInt64 returns the int64 value, if the field exist and a potential error. +func (o *SubObject) NestedInt64(fields ...string) (int64, bool, error) { + var val int64 + found, err := o.Get(&val, fields...) + return val, found, err +} + +// NestedInt64OrDie returns the string value at fields. An empty string will be +// returned if the field is not found. It will panic if encountering any errors. +func (o *SubObject) NestedInt64OrDie(fields ...string) int64 { + val, _, err := o.NestedInt64(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedSlice accepts a slice of `fields` which represents the path to the slice component and +// return a slice of SubObjects as the first return value; whether the component exists or +// not as the second return value, and errors as the third return value. +func (o *SubObject) NestedSlice(fields ...string) (SliceSubObjects, bool, error) { + var mapVariant *internal.MapVariant + if len(fields) > 1 { + m, found, err := o.obj.GetNestedMap(fields[:len(fields)-1]...) + if err != nil || !found { + return nil, found, err + } + mapVariant = m + } else { + mapVariant = o.obj + } + sliceVal, found, err := mapVariant.GetNestedSlice(fields[len(fields)-1]) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + if !found { + return nil, found, nil + } + objects, err := sliceVal.Elements() + if err != nil { + return nil, found, err + } + var val []*SubObject + for _, obj := range objects { + val = append(val, &SubObject{obj: obj}) + } + return val, true, nil +} + +// NestedSliceOrDie accepts a slice of `fields` which represents the path to the slice component and +// return a slice of SubObjects. +// - It returns nil if the fields does not exist. +// - It panics with errSubObjectFields error if the field is not a slice type. +func (o *SubObject) NestedSliceOrDie(fields ...string) SliceSubObjects { + val, _, err := o.NestedSlice(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedMap returns a map[string]string value of a nested field, false if not found and an error if not a map[string]string type. +func (o *SubObject) NestedStringMap(fields ...string) (map[string]string, bool, error) { + var variant map[string]string + found, err := o.Get(&variant, fields...) + if err != nil || !found { + return nil, found, err + } + return variant, found, err +} + +// NestedStringMapOrDie returns a map[string]string value of a nested field, if the fields does not exist, it returns +// empty map[string]string. It will panic if the fields are not map[string]string type. +func (o *SubObject) NestedStringMapOrDie(fields ...string) map[string]string { + val, _, err := o.NestedStringMap(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// NestedMap returns a map[string]string value of a nested field, false if not found and an error if not a map[string]string type. +func (o *SubObject) NestedStringSlice(fields ...string) ([]string, bool, error) { + var variant []string + found, err := o.Get(&variant, fields...) + if err != nil || !found { + return nil, found, err + } + return variant, found, err +} + +// NestedMapOrDie returns a map[string]string value of a nested field. +// It will panic if the fields are not map[string]string type. +func (o *SubObject) NestedStringSliceOrDie(fields ...string) []string { + val, _, err := o.NestedStringSlice(fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } + return val +} + +// RemoveNestedFieldOrDie removes the field located by fields if found. It will panic if it +// encounters any error. +func (o *SubObject) RemoveNestedFieldOrDie(fields ...string) { + if _, err := o.RemoveNestedField(fields...); err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// RemoveNestedField removes the field located by fields if found. It returns if the field +// is found and a potential error. +func (o *SubObject) RemoveNestedField(fields ...string) (bool, error) { + found, err := func() (bool, error) { + if o == nil { + return false, fmt.Errorf("the object doesn't exist") + } + return o.obj.RemoveNestedField(fields...) + }() + if err != nil { + return found, fmt.Errorf("unable to remove fields %v with error: %w", fields, err) + } + return found, nil +} + +// Get gets the value for a nested field located by fields. A pointer must be +// passed in, and the value will be stored in ptr. ptr can be a concrete type +// (e.g. string, []corev1.Container, []string, corev1.Pod, map[string]string) or +// a yaml.RNode. yaml.RNode should be used if you are dealing with comments that +// is more than what LineComment, HeadComment, SetLineComment and +// SetHeadComment can handle. It returns if the field is found and a +// potential error. +func (o *SubObject) Get(ptr interface{}, fields ...string) (bool, error) { + found, err := func() (bool, error) { + if o == nil { + return false, fmt.Errorf("the object doesn't exist") + } + if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { + return false, fmt.Errorf("ptr must be a pointer to an object") + } + + if rn, ok := ptr.(*yaml.RNode); ok { + val, found, err := o.obj.GetNestedValue(fields...) + if err != nil || !found { + return found, err + } + rn.SetYNode(val.Node()) + return found, err + } + + switch k := reflect.TypeOf(ptr).Elem().Kind(); k { + case reflect.Struct, reflect.Map: + m, found, err := o.obj.GetNestedMap(fields...) + if err != nil || !found { + return found, err + } + err = m.Node().Decode(ptr) + return found, err + case reflect.Slice: + s, found, err := o.obj.GetNestedSlice(fields...) + if err != nil || !found { + return found, err + } + err = s.Node().Decode(ptr) + return found, err + case reflect.String: + s, found, err := o.obj.GetNestedString(fields...) + if err != nil || !found { + return found, err + } + *(ptr.(*string)) = s + return found, nil + case reflect.Int, reflect.Int64: + i, found, err := o.obj.GetNestedInt(fields...) + if err != nil || !found { + return found, err + } + if k == reflect.Int { + *(ptr.(*int)) = i + } else if k == reflect.Int64 { + *(ptr.(*int64)) = int64(i) + } + return found, nil + case reflect.Float64: + f, found, err := o.obj.GetNestedFloat(fields...) + if err != nil || !found { + return found, err + } + *(ptr.(*float64)) = f + return found, nil + case reflect.Bool: + b, found, err := o.obj.GetNestedBool(fields...) + if err != nil || !found { + return found, err + } + *(ptr.(*bool)) = b + return found, nil + default: + return false, fmt.Errorf("unhandled kind %s", k) + } + }() + if err != nil { + return found, fmt.Errorf("unable to get fields %v as %T with error: %w", fields, ptr, err) + } + return found, nil +} + +// SetOrDie sets a nested field located by fields to the value provided as val. +// It will panic if it encounters any error. +func (o *SubObject) SetOrDie(val interface{}, fields ...string) { + if err := o.SetNestedField(val, fields...); err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// onLockedFields locks the SubObject fields which are expected for kpt internal use only. +func (o *SubObject) onLockedFields(val interface{}, fields ...string) error { + if o.hasUpstreamIdentifier(val, fields...) { + return ErrAttemptToTouchUpstreamIdentifier{} + } + return nil +} + +func (o *SubObject) SetSlice(objects SliceSubObjects, field string) error { + return o.SetNestedField(objects, field) +} + +// SetNestedField sets a nested field located by fields to the value provided as val. val +// should not be a yaml.RNode. If you want to deal with yaml.RNode, you should +// use Get method and modify the underlying yaml.Node. +func (o *SubObject) SetNestedField(val interface{}, fields ...string) error { + if err := o.onLockedFields(val, fields...); err != nil { + return err + } + err := func() error { + if val == nil { + return fmt.Errorf("the passed-in object must not be nil") + } + if o == nil { + return fmt.Errorf("the object doesn't exist") + } + if o.obj == nil { + o.obj = internal.NewMap(nil) + } + kind := reflect.ValueOf(val).Kind() + if kind == reflect.Ptr { + kind = reflect.TypeOf(val).Elem().Kind() + } + + switch kind { + case reflect.Struct, reflect.Map: + m, err := internal.TypedObjectToMapVariant(val) + if err != nil { + return err + } + return o.obj.SetNestedMap(m, fields...) + case reflect.Slice: + if valSliceSubObj, ok := val.(SliceSubObjects); ok { + s := internal.NewSliceVariant() + for _, element := range valSliceSubObj { + s.Add(element.obj) + } + return o.obj.SetNestedSlice(s, fields...) + } + s, err := internal.TypedObjectToSliceVariant(val) + if err != nil { + return err + } + return o.obj.SetNestedSlice(s, fields...) + case reflect.String: + var s string + switch val := val.(type) { + case string: + s = val + case *string: + s = *val + } + return o.obj.SetNestedString(s, fields...) + case reflect.Int, reflect.Int64: + var i int + switch val := val.(type) { + case int: + i = val + case *int: + i = *val + case int64: + i = int(val) + case *int64: + i = int(*val) + } + return o.obj.SetNestedInt(i, fields...) + case reflect.Float64: + var f float64 + switch val := val.(type) { + case float64: + f = val + case *float64: + f = *val + } + return o.obj.SetNestedFloat(f, fields...) + case reflect.Bool: + var b bool + switch val := val.(type) { + case bool: + b = val + case *bool: + b = *val + } + return o.obj.SetNestedBool(b, fields...) + default: + return fmt.Errorf("unhandled kind %s", kind) + } + }() + if err != nil { + return fmt.Errorf("unable to set %v at fields %v with error: %w", val, fields, err) + } + return nil +} + +// SetNestedIntOrDie sets the `fields` value to int `value`. It panics if the fields type is not int. +func (o *SubObject) SetNestedIntOrDie(value int, fields ...string) { + err := o.SetNestedInt(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedInt sets the `fields` value to int `value`. It returns error if the fields type is not int. +func (o *SubObject) SetNestedInt(value int, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedBoolOrDie sets the `fields` value to bool `value`. It panics if the fields type is not bool. +func (o *SubObject) SetNestedBoolOrDie(value bool, fields ...string) { + err := o.SetNestedBool(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedBool sets the `fields` value to bool `value`. It returns error if the fields type is not bool. +func (o *SubObject) SetNestedBool(value bool, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedStringOrDie sets the `fields` value to string `value`. It panics if the fields type is not string. +func (o *SubObject) SetNestedStringOrDie(value string, fields ...string) { + err := o.SetNestedString(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedStringOrDie sets the `fields` value to string `value`. It returns error if the fields type is not string. +func (o *SubObject) SetNestedString(value string, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedStringMapOrDie sets the `fields` value to map[string]string `value`. It panics if the fields type is not map[string]string. +func (o *SubObject) SetNestedStringMapOrDie(value map[string]string, fields ...string) { + err := o.SetNestedStringMap(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedStringMap sets the `fields` value to map[string]string `value`. It returns error if the fields type is not map[string]string. +func (o *SubObject) SetNestedStringMap(value map[string]string, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// SetNestedStringSliceOrDie sets the `fields` value to []string `value`. It panics if the fields type is not []string. +func (o *SubObject) SetNestedStringSliceOrDie(value []string, fields ...string) { + err := o.SetNestedStringSlice(value, fields...) + if err != nil { + panic(errSubObjectFields{fields: fields}) + } +} + +// SetNestedStringSlice sets the `fields` value to []string `value`. It returns error if the fields type is not []string. +func (o *SubObject) SetNestedStringSlice(value []string, fields ...string) error { + return o.SetNestedField(value, fields...) +} + +// LineComment returns the line comment, if the target field exist and a +// potential error. +func (o *KubeObject) LineComment(fields ...string) (string, bool, error) { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if !found || err != nil { + return "", found, err + } + return rn.YNode().LineComment, true, nil +} + +// HeadComment returns the head comment, if the target field exist and a +// potential error. +func (o *KubeObject) HeadComment(fields ...string) (string, bool, error) { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if !found || err != nil { + return "", found, err + } + return rn.YNode().HeadComment, true, nil +} + +func (o *KubeObject) SetLineComment(comment string, fields ...string) error { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if err != nil { + return err + } + if !found { + return fmt.Errorf("can't set line comment because the field doesn't exist") + } + rn.YNode().LineComment = comment + return nil +} + +func (o *KubeObject) SetHeadComment(comment string, fields ...string) error { + rn := &yaml.RNode{} + found, err := o.Get(rn, fields...) + if err != nil { + return err + } + if !found { + return fmt.Errorf("can't set head comment because the field doesn't exist") + } + rn.YNode().HeadComment = comment + return nil +} + +// AsOrDie converts a KubeObject to the desired typed object. ptr must +// be a pointer to a typed object. It will panic if it encounters an error. +func (o *SubObject) AsOrDie(ptr interface{}) { + if err := o.As(ptr); err != nil { + panic(errSubObjectFields{fields: nil}) + } +} + +// As converts a KubeObject to the desired typed object. ptr must be +// a pointer to a typed object. +func (o *SubObject) As(ptr interface{}) error { + err := func() error { + if o == nil { + return fmt.Errorf("the object doesn't exist") + } + if ptr == nil || reflect.ValueOf(ptr).Kind() != reflect.Ptr { + return fmt.Errorf("ptr must be a pointer to an object") + } + return internal.MapVariantToTypedObject(o.obj, ptr) + }() + if err != nil { + return fmt.Errorf("unable to convert object to %T with error: %w", ptr, err) + } + return nil +} + +// NewFromTypedObject construct a KubeObject from a typed object (e.g. corev1.Pod) +func NewFromTypedObject(v interface{}) (*KubeObject, error) { + kind := reflect.ValueOf(v).Kind() + if kind == reflect.Ptr { + kind = reflect.TypeOf(v).Elem().Kind() + } + var err error + var m *internal.MapVariant + switch kind { + case reflect.Struct, reflect.Map: + m, err = internal.TypedObjectToMapVariant(v) + case reflect.Slice: + return nil, fmt.Errorf("The typed object should be of a reflect.Struct or reflect.Map, got reflect.Slice") + } + if err != nil { + return nil, err + } + return asKubeObject(m), nil +} + +// String serializes the object in yaml format. +func (o *KubeObject) String() string { + doc := internal.NewDoc([]*yaml.Node{o.obj.Node()}...) + s, _ := doc.ToYAML() + return string(s) +} + +// ShortString provides a human readable information for the KubeObject Identifier in the form of GVKNN. +func (o *KubeObject) ShortString() string { + return fmt.Sprintf("Resource(apiVersion=%v, kind=%v, namespace=%v, name=%v)", + o.GetAPIVersion(), o.GetKind(), o.GetNamespace(), o.GetName()) +} + +// resourceIdentifier returns the resource identifier including apiVersion, kind, +// namespace and name. +func (o *KubeObject) resourceIdentifier() *yaml.ResourceIdentifier { + apiVersion := o.GetAPIVersion() + kind := o.GetKind() + name := o.GetName() + ns := o.GetNamespace() + return &yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: apiVersion, + Kind: kind, + }, + NameMeta: yaml.NameMeta{ + Name: name, + Namespace: ns, + }, + } +} + +// IsGVK compares the given group, version, and kind with KubeObject's apiVersion and Kind. +func (o *KubeObject) IsGVK(group, version, kind string) bool { + oGroup, oVersion := ParseGroupVersion(o.GetAPIVersion()) + if o.GetKind() != "" && kind != "" && o.GetKind() != kind { + return false + } + if oGroup != "" && group != "" && oGroup != group { + return false + } + if oVersion != "" && version != "" && oVersion != version { + return false + } + return true +} + +// IsLocalConfig checks the "config.kubernetes.io/local-config" field to tell +// whether a KRM resource will be skipped by `kpt live apply` or not. +func (o *KubeObject) IsLocalConfig() bool { + isLocalConfig := o.GetAnnotation(KptLocalConfig) + if isLocalConfig == "" || isLocalConfig == "false" { + return false + } + return true +} + +func (o *KubeObject) GetAPIVersion() string { + apiVersion, _, _ := o.obj.GetNestedString("apiVersion") + return apiVersion +} + +func (o *KubeObject) SetAPIVersion(apiVersion string) { + if err := o.obj.SetNestedString(apiVersion, "apiVersion"); err != nil { + panic(fmt.Errorf("cannot set apiVersion '%v': %v", apiVersion, err)) + } +} + +func (o *KubeObject) GetKind() string { + kind, _, _ := o.obj.GetNestedString("kind") + return kind +} + +func (o *KubeObject) SetKind(kind string) { + if err := o.SetNestedField(kind, "kind"); err != nil { + panic(fmt.Errorf("cannot set kind '%v': %v", kind, err)) + } +} + +func (o *KubeObject) GetName() string { + s, _, _ := o.obj.GetNestedString("metadata", "name") + return s +} + +func (o *KubeObject) SetName(name string) { + if err := o.SetNestedField(name, "metadata", "name"); err != nil { + panic(fmt.Errorf("cannot set metadata name '%v': %v", name, err)) + } +} + +func (o *KubeObject) GetNamespace() string { + s, _, _ := o.obj.GetNestedString("metadata", "namespace") + return s +} + +// IsNamespaceScoped tells whether a k8s resource is namespace scoped. If the KubeObject resource is a customized, it +// determines the namespace scope by checking whether `metadata.namespace` is set. +func (o *KubeObject) IsNamespaceScoped() bool { + tm := yaml.TypeMeta{Kind: o.GetKind(), APIVersion: o.GetAPIVersion()} + if nsScoped, ok := internal.PrecomputedIsNamespaceScoped[tm]; ok { + return nsScoped + } + // TODO(yuwenma): parse the resource openapi schema to know its scope status. + return o.HasNamespace() +} + +// IsClusterScoped tells whether a resource is cluster scoped. +func (o *KubeObject) IsClusterScoped() bool { + return !o.IsNamespaceScoped() +} + +func (o *KubeObject) HasNamespace() bool { + _, found, _ := o.obj.GetNestedString("metadata", "namespace") + return found +} + +func (o *KubeObject) SetNamespace(name string) { + if err := o.SetNestedField(name, "metadata", "namespace"); err != nil { + panic(fmt.Errorf("cannot set namespace '%v': %v", name, err)) + } +} + +func (o *KubeObject) SetAnnotation(k, v string) { + // Keep upstream-identifier untouched from users + if k == UpstreamIdentifier { + panic(ErrAttemptToTouchUpstreamIdentifier{}) + } + if err := o.SetNestedField(v, "metadata", "annotations", k); err != nil { + panic(fmt.Errorf("cannot set metadata annotations '%v': %v", k, err)) + } +} + +// GetAnnotations returns all annotations. +func (o *KubeObject) GetAnnotations() map[string]string { + v, _, _ := o.obj.GetNestedStringMap("metadata", "annotations") + return v +} + +// GetAnnotation returns one annotation with key k. +func (o *KubeObject) GetAnnotation(k string) string { + v, _, _ := o.obj.GetNestedString("metadata", "annotations", k) + return v +} + +// HasAnnotations returns whether the KubeObject has all the given annotations. +func (o *KubeObject) HasAnnotations(annotations map[string]string) bool { + kubeObjectLabels := o.GetAnnotations() + for k, v := range annotations { + kubeObjectValue, found := kubeObjectLabels[k] + if !found || kubeObjectValue != v { + return false + } + } + return true +} + +// RemoveAnnotationsIfEmpty removes the annotations field when it has zero annotations. +func (o *KubeObject) RemoveAnnotationsIfEmpty() error { + annotations, found, err := o.obj.GetNestedStringMap("metadata", "annotations") + if err != nil { + return err + } + if found && len(annotations) == 0 { + _, err = o.obj.RemoveNestedField("metadata", "annotations") + return err + } + return nil +} + +func (o *KubeObject) SetLabel(k, v string) { + if err := o.SetNestedField(v, "metadata", "labels", k); err != nil { + panic(fmt.Errorf("cannot set metadata labels '%v': %v", k, err)) + } +} + +// Label returns one label with key k. +func (o *KubeObject) GetLabel(k string) string { + v, _, _ := o.obj.GetNestedString("metadata", "labels", k) + return v +} + +// Labels returns all labels. +func (o *KubeObject) GetLabels() map[string]string { + v, _, _ := o.obj.GetNestedStringMap("metadata", "labels") + return v +} + +// HasLabels returns whether the KubeObject has all the given labels +func (o *KubeObject) HasLabels(labels map[string]string) bool { + kubeObjectLabels := o.GetLabels() + for k, v := range labels { + kubeObjectValue, found := kubeObjectLabels[k] + if !found || kubeObjectValue != v { + return false + } + } + return true +} + +func (o *KubeObject) PathAnnotation() string { + anno := o.GetAnnotation(kioutil.PathAnnotation) + return anno +} + +// IndexAnnotation return -1 if not found. +func (o *KubeObject) IndexAnnotation() int { + anno := o.GetAnnotation(kioutil.IndexAnnotation) + if anno == "" { + return -1 + } + i, _ := strconv.Atoi(anno) + return i +} + +// IdAnnotation return -1 if not found. +func (o *KubeObject) IdAnnotation() int { + anno := o.GetAnnotation(kioutil.IdAnnotation) + + if anno == "" { + return -1 + } + i, _ := strconv.Atoi(anno) + return i +} + +type KubeObjects []*KubeObject + +func (o KubeObjects) Len() int { return len(o) } +func (o KubeObjects) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o KubeObjects) Less(i, j int) bool { + idi := o[i].resourceIdentifier() + idj := o[j].resourceIdentifier() + idStrI := fmt.Sprintf("%s %s %s %s", idi.GetAPIVersion(), idi.GetKind(), idi.GetNamespace(), idi.GetName()) + idStrJ := fmt.Sprintf("%s %s %s %s", idj.GetAPIVersion(), idj.GetKind(), idj.GetNamespace(), idj.GetName()) + return idStrI < idStrJ +} + +func (o KubeObjects) String() string { + var elems []string + for _, obj := range o { + elems = append(elems, strings.TrimSpace(obj.String())) + } + return strings.Join(elems, "\n---\n") +} + +// Where will return the subset of objects in KubeObjects such that f(object) returns 'true'. +func (o KubeObjects) Where(f func(*KubeObject) bool) KubeObjects { + var result KubeObjects + for _, obj := range o { + if f(obj) { + result = append(result, obj) + } + } + return result +} + +// Not returns will return a function that returns the opposite of f(object), i.e. !f(object) +func Not(f func(*KubeObject) bool) func(o *KubeObject) bool { + return func(o *KubeObject) bool { + return !f(o) + } +} + +// WhereNot will return the subset of objects in KubeObjects such that f(object) returns 'false'. +// This is a shortcut for Where(Not(f)). +func (o KubeObjects) WhereNot(f func(o *KubeObject) bool) KubeObjects { + return o.Where(Not(f)) +} + +// IsGVK returns a function that checks if a KubeObject has a certain GVK. +func IsGVK(group, version, kind string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.IsGVK(group, version, kind) + } +} + +// GetRootKptfile returns the root Kptfile. Nested kpt packages can have multiple Kptfile files of the same GVKNN. +func (o KubeObjects) GetRootKptfile() *KubeObject { + kptfiles := o.Where(IsGVK(v1.KptFileGroup, v1.KptFileVersion, v1.KptFileKind)) + if len(kptfiles) == 0 { + return nil + } + minDepths := math.MaxInt32 + var rootKptfile *KubeObject + for _, kf := range kptfiles { + path := kf.GetAnnotation(PathAnnotation) + depths := len(strings.Split(path, "/")) + if depths <= minDepths { + minDepths = depths + rootKptfile = kf + } + } + return rootKptfile +} + +// IsName returns a function that checks if a KubeObject has a certain name. +func IsName(name string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.GetName() == name + } +} + +// IsNamespace returns a function that checks if a KubeObject has a certain namespace. +func IsNamespace(namespace string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.GetNamespace() == namespace + } +} + +// HasLabels returns a function that checks if a KubeObject has all the given labels. +func HasLabels(labels map[string]string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.HasLabels(labels) + } +} + +// HasAnnotations returns a function that checks if a KubeObject has all the given annotations. +func HasAnnotations(annotations map[string]string) func(*KubeObject) bool { + return func(o *KubeObject) bool { + return o.HasAnnotations(annotations) + } +} + +// IsMetaResource returns a function that checks if a KubeObject is a meta resource. For now +// this just includes the Kptfile +func IsMetaResource() func(*KubeObject) bool { + return IsGVK("kpt.dev", "v1", "Kptfile") +} + +func (o *KubeObject) IsEmpty() bool { + return yaml.IsYNodeEmptyMap(o.obj.Node()) +} + +func NewEmptyKubeObject() *KubeObject { + return &KubeObject{SubObject{internal.NewMap(nil)}} +} + +func asKubeObject(obj *internal.MapVariant) *KubeObject { + return &KubeObject{SubObject{obj}} +} + +func (o *KubeObject) node() *internal.MapVariant { + return o.obj +} + +func rnodeToKubeObject(rn *yaml.RNode) *KubeObject { + mapVariant := internal.NewMap(rn.YNode()) + return &KubeObject{SubObject{mapVariant}} +} + +// SubObject represents a map within a KubeObject +type SubObject struct { + obj *internal.MapVariant +} + +func (o *SubObject) UpsertMap(k string) *SubObject { + m := o.obj.UpsertMap(k) + return &SubObject{obj: m} +} + +// GetMap accepts a single key `k` whose value is expected to be a map. It returns +// the map in the form of a SubObject pointer. +// It panic with ErrSubObjectFields error if the field cannot be represented as a SubObject. +func (o *SubObject) GetMap(k string) *SubObject { + var variant yaml.RNode + found, err := o.Get(&variant, k) + if err != nil || !found { + return nil + } + return &SubObject{obj: internal.NewMap(variant.YNode())} +} + +// GetBool accepts a single key `k` whose value is expected to be a boolean. It returns +// the int value of the `k`. It panic with errSubObjectFields error if the +// field is not an integer type. +func (o *SubObject) GetBool(k string) bool { + return o.NestedBoolOrDie(k) +} + +// GetInt accepts a single key `k` whose value is expected to be an integer. It returns +// the int value of the `k`. It panic with errSubObjectFields error if the +// field is not an integer type. +func (o *SubObject) GetInt(k string) int64 { + return o.NestedInt64OrDie(k) +} + +// GetString accepts a single key `k` whose value is expected to be a string. It returns +// the value of the `k`. It panic with errSubObjectFields error if the +// field is not a string type. +func (o *SubObject) GetString(k string) string { + return o.NestedStringOrDie(k) +} + +// GetSlice accepts a single key `k` whose value is expected to be a slice. It returns +// the value as a slice of SubObject. It panic with errSubObjectFields error if the +// field is not a slice type. +func (o *SubObject) GetSlice(k string) SliceSubObjects { + return o.NestedSliceOrDie(k) +} + +type SliceSubObjects []*SubObject + +// MarshalJSON provides the custom encoding format for encode.json. This is used +// when KubeObject `Set` a slice of SubObjects. +func (s *SliceSubObjects) MarshalJSON() ([]byte, error) { + node := &yaml.Node{Kind: yaml.SequenceNode} + for _, subObject := range *s { + node.Content = append(node.Content, subObject.obj.Node()) + } + return yaml.NewRNode(node).MarshalJSON() +} diff --git a/functions/go/native-config-adaptor/fn/origin.go b/functions/go/native-config-adaptor/fn/origin.go new file mode 100644 index 000000000..a84d33d40 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/origin.go @@ -0,0 +1,138 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "reflect" + "regexp" + "strings" +) + +const ( + // upstreamIdentifierRegexPattern provides the rough regex to parse a upstream-identiifier annotation. + // "group" should be a domain name. We accept empty string for kubernetes core v1 resources. + // "kind" should be the resource type with initial in capitals. + // "namespace" should follow RFC 1123 Label Names. We accept "~C~ for cluster-scoped resource or unknown scope resources. + // "name" should follow RFC 1123 Label Names https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names + upstreamIdentifierRegexPattern = `(?P[a-z0-9-.]*)\|(?P[A-Z][a-zA-Z0-9]*)\|(?P[a-z0-9-]{1,63}|~C)\|(?P[a-z0-9-]{1,63})` + upstreamIdentifierFormat = "|||" + regexPatternGroup = "group" + regexPatternKind = "kind" + regexPatterNamespace = "namespace" + regexPatternName = "name" +) + +type ResourceIdentifier struct { + Group string + Version string + Kind string + Name string + Namespace string +} + +func (r *ResourceIdentifier) String() string { + return fmt.Sprintf("%v|%v|%v|%v", r.Group, r.Kind, r.Namespace, r.Name) +} + +// hasUpstreamIdentifier determines whether the args are touching the kpt only annotation "internal.kpt.dev/upstream-identifier" +func (o *SubObject) hasUpstreamIdentifier(val interface{}, fields ...string) bool { + kind := reflect.ValueOf(val).Kind() + if kind == reflect.Ptr { + kind = reflect.TypeOf(val).Elem().Kind() + } + switch kind { + case reflect.String: + if fields[len(fields)-1] == UpstreamIdentifier { + return true + } + case reflect.Map: + if fields[len(fields)-1] == "annotations" { + for _, key := range reflect.ValueOf(val).MapKeys() { + if key.String() == UpstreamIdentifier { + return true + } + } + } + } + return false +} + +func (o *KubeObject) effectiveNamespace() string { + if o.HasNamespace() { + return o.GetNamespace() + } + if o.IsNamespaceScoped() { + return DefaultNamespace + } + return UnknownNamespace +} + +// GetId gets the Group, Kind, Namespace and Name as the ResourceIdentifier. +func (o *KubeObject) GetId() *ResourceIdentifier { + group, _ := ParseGroupVersion(o.GetAPIVersion()) + return &ResourceIdentifier{ + Group: group, + Kind: o.GetKind(), + Namespace: o.effectiveNamespace(), + Name: o.GetName(), + } +} + +func parseUpstreamIdentifier(upstreamId string) *ResourceIdentifier { + upstreamId = strings.TrimSpace(upstreamId) + r := regexp.MustCompile(upstreamIdentifierRegexPattern) + match := r.FindStringSubmatch(upstreamId) + if match == nil { + panic(ErrInternalAnnotation{Message: fmt.Sprintf("annotation %v: %v is in bad format. expect %q", + UpstreamIdentifier, upstreamId, upstreamIdentifierFormat)}) + } + matchGroups := make(map[string]string) + for i, name := range r.SubexpNames() { + if i > 0 && i <= len(match) { + matchGroups[name] = match[i] + } + } + return &ResourceIdentifier{ + Group: matchGroups[regexPatternGroup], + Kind: matchGroups[regexPatternKind], + Namespace: matchGroups[regexPatterNamespace], + Name: matchGroups[regexPatternName], + } +} + +// GetOriginId provides the `ResourceIdentifier` to identify the upstream origin of a KRM resource. +// This origin is generated and maintained by kpt pkg management and is stored in the `internal.kpt.dev/upstream-identiifer` annotation. +// If a resource does not have an upstream origin, we use its current meta resource ID instead. +func (o *KubeObject) GetOriginId() *ResourceIdentifier { + upstreamId := o.GetAnnotation(UpstreamIdentifier) + if upstreamId != "" { + return parseUpstreamIdentifier(upstreamId) + } + return o.GetId() +} + +// HasUpstreamOrigin tells whether a resource is sourced from an upstream package resource. +func (o *KubeObject) HasUpstreamOrigin() bool { + upstreamId := o.GetAnnotation(UpstreamIdentifier) + return upstreamId != "" +} + +// ParseGroupVersion parses a "apiVersion" to get the "group" and "version" values. +func ParseGroupVersion(apiVersion string) (group, version string) { + if i := strings.Index(apiVersion, "/"); i > -1 { + return apiVersion[:i], apiVersion[i+1:] + } + return "", apiVersion +} diff --git a/functions/go/native-config-adaptor/fn/resourcelist.go b/functions/go/native-config-adaptor/fn/resourcelist.go new file mode 100644 index 000000000..0cdaea942 --- /dev/null +++ b/functions/go/native-config-adaptor/fn/resourcelist.go @@ -0,0 +1,324 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package fn + +import ( + "fmt" + "reflect" + "sort" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/fn/internal" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// ResourceList is a Kubernetes list type used as the primary data interchange format +// in the Configuration Functions Specification: +// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md +// This framework facilitates building functions that receive and emit ResourceLists, +// as required by the specification. +type ResourceList struct { + // Items is the ResourceList.items input and output value. + // + // e.g. given the function input: + // + // kind: ResourceList + // items: + // - kind: Deployment + // ... + // - kind: Service + // ... + // + // Items will be a slice containing the Deployment and Service resources + // Mutating functions will alter this field during processing. + // This field is required. + Items KubeObjects `yaml:"items" json:"items"` + + // FunctionConfig is the ResourceList.functionConfig input value. + // + // e.g. given the input: + // + // kind: ResourceList + // functionConfig: + // kind: Example + // spec: + // foo: var + // + // FunctionConfig will contain the RNodes for the Example: + // kind: Example + // spec: + // foo: var + FunctionConfig *KubeObject `yaml:"functionConfig,omitempty" json:"functionConfig,omitempty"` + + // Results is ResourceList.results output value. + // Validating functions can optionally use this field to communicate structured + // validation error data to downstream functions. + Results Results `yaml:"results,omitempty" json:"results,omitempty"` +} + +// CheckResourceDuplication checks the GVKNN of resourceList.items to make sure they are unique. It returns errors if +// found more than one resource having the same GVKNN. +func CheckResourceDuplication(rl *ResourceList) error { + idMap := map[yaml.ResourceIdentifier]struct{}{} + for _, obj := range rl.Items { + id := obj.resourceIdentifier() + if _, ok := idMap[*id]; ok { + return fmt.Errorf("duplicate Resource(apiVersion=%v, kind=%v, Namespace=%v, Name=%v)", + obj.GetAPIVersion(), obj.GetKind(), obj.GetNamespace(), obj.GetName()) + } + idMap[*id] = struct{}{} + } + return nil +} + +// ParseResourceList parses a ResourceList from the input byte array. This function can be used to parse either KRM fn input +// or KRM fn output +func ParseResourceList(in []byte) (*ResourceList, error) { + rl := &ResourceList{} + rlObj, err := ParseKubeObject(in) + if err != nil { + return nil, fmt.Errorf("failed to parse input bytes: %w", err) + } + if rlObj.GetKind() != kio.ResourceListKind { + return nil, fmt.Errorf("input was of unexpected kind %q; expected ResourceList", rlObj.GetKind()) + } + // Parse FunctionConfig. FunctionConfig can be empty, e.g. `kubeval` fn does not require a FunctionConfig. + fc, found, err := rlObj.obj.GetNestedMap("functionConfig") + if err != nil { + return nil, fmt.Errorf("failed when tried to get functionConfig: %w", err) + } + if found { + rl.FunctionConfig = asKubeObject(fc) + } else { + rl.FunctionConfig = NewEmptyKubeObject() + } + + // Parse Items. Items can be empty, e.g. an input ResourceList for a generator function may not have items. + items, found, err := rlObj.obj.GetNestedSlice("items") + if err != nil { + return nil, fmt.Errorf("failed when tried to get items: %w", err) + } + if found { + objectItems, err := items.Elements() + if err != nil { + return nil, fmt.Errorf("failed to extract objects from items: %w", err) + } + for i := range objectItems { + rl.Items = append(rl.Items, asKubeObject(objectItems[i])) + } + } + + // Parse Results. Results can be empty. + res, found, err := rlObj.obj.GetNestedSlice("results") + if err != nil { + return nil, fmt.Errorf("failed when tried to get results: %w", err) + } + if found { + var results Results + err = res.Node().Decode(&results) + if err != nil { + return nil, fmt.Errorf("failed to decode results: %w", err) + } + rl.Results = results + } + return rl, nil +} + +// toYNode converts the ResourceList to the yaml.Node representation. +func (rl *ResourceList) toYNode() (*yaml.Node, error) { + reMap := internal.NewMap(nil) + reObj := &KubeObject{SubObject{reMap}} + reObj.SetAPIVersion(kio.ResourceListAPIVersion) + reObj.SetKind(kio.ResourceListKind) + + if rl.Items != nil && len(rl.Items) > 0 { + itemsSlice := internal.NewSliceVariant() + for i := range rl.Items { + itemsSlice.Add(rl.Items[i].node()) + } + if err := reMap.SetNestedSlice(itemsSlice, "items"); err != nil { + return nil, err + } + } + if !rl.FunctionConfig.IsEmpty() { + if err := reMap.SetNestedMap(rl.FunctionConfig.node(), "functionConfig"); err != nil { + return nil, err + } + } + + if rl.Results != nil && len(rl.Results) > 0 { + resultsSlice := internal.NewSliceVariant() + for _, result := range rl.Results { + mv, err := internal.TypedObjectToMapVariant(result) + if err != nil { + return nil, err + } + resultsSlice.Add(mv) + } + if err := reMap.SetNestedSlice(resultsSlice, "results"); err != nil { + return nil, err + } + } + + return reMap.Node(), nil +} + +// ToYAML converts the ResourceList to yaml. +func (rl *ResourceList) ToYAML() ([]byte, error) { + // Sort the resources first. + rl.Sort() + ynode, err := rl.toYNode() + if err != nil { + return nil, err + } + doc := internal.NewDoc([]*yaml.Node{ynode}...) + return doc.ToYAML() +} + +// Sort sorts the ResourceList.items by apiVersion, kind, namespace and name. +func (rl *ResourceList) Sort() { + sort.Sort(rl.Items) +} + +// UpsertObjectToItems adds an object to ResourceList.items. The input object can +// be a KubeObject or any typed object (e.g. corev1.Pod). +func (rl *ResourceList) UpsertObjectToItems(obj interface{}, checkExistence func(obj, another *KubeObject) bool, replaceIfAlreadyExist bool) error { + if checkExistence == nil { + checkExistence = func(obj, another *KubeObject) bool { + ri1 := obj.resourceIdentifier() + ri2 := another.resourceIdentifier() + return reflect.DeepEqual(ri1, ri2) + } + } + + var ko *KubeObject + switch obj := obj.(type) { + case KubeObject: + ko = &obj + case *KubeObject: + ko = obj + case yaml.RNode: + ko = rnodeToKubeObject(&obj) + case *yaml.RNode: + ko = rnodeToKubeObject(obj) + case yaml.Node: + ko = rnodeToKubeObject(yaml.NewRNode(&obj)) + case *yaml.Node: + ko = rnodeToKubeObject(yaml.NewRNode(obj)) + default: + var err error + ko, err = NewFromTypedObject(obj) + if err != nil { + return err + } + } + + idx := -1 + for i, item := range rl.Items { + if checkExistence(ko, item) { + idx = i + break + } + } + if idx == -1 { + rl.Items = append(rl.Items, ko) + } else if replaceIfAlreadyExist { + rl.Items[idx] = ko + } + return nil +} + +func (rl *ResourceList) LogResult(err error) { + // If the error is not a Results type, we wrap the error as a Result. + if err == nil { + return + } + switch result := err.(type) { + case Results: + rl.Results = append(rl.Results, result...) + case Result: + rl.Results = append(rl.Results, &result) + case *Result: + rl.Results = append(rl.Results, result) + default: + rl.Results = append(rl.Results, ErrorResult(err)) + } +} + +// ResourceListProcessor is implemented by configuration functions built with this framework +// to conform to the Configuration Functions Specification: +// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md +type ResourceListProcessor interface { + Process(rl *ResourceList) (bool, error) +} + +// ResourceListProcessorFunc converts a compatible function to a ResourceListProcessor. +type ResourceListProcessorFunc func(rl *ResourceList) (bool, error) + +func (p ResourceListProcessorFunc) Process(rl *ResourceList) (bool, error) { + return p(rl) +} + +// Chain chains a list of ResourceListProcessor as a single ResourceListProcessor. +func Chain(processors ...ResourceListProcessor) ResourceListProcessor { + return ResourceListProcessorFunc(func(rl *ResourceList) (bool, error) { + success := true + for _, processor := range processors { + s, err := processor.Process(rl) + if !s { + success = false + } + if err != nil { + return false, err + } + } + return success, nil + }) +} + +// ChainFunctions chains a list of ResourceListProcessorFunc as a single +// ResourceListProcessorFunc. +func ChainFunctions(functions ...ResourceListProcessorFunc) ResourceListProcessorFunc { + return func(rl *ResourceList) (bool, error) { + success := true + for _, fn := range functions { + s, err := fn(rl) + if !s { + success = false + } + if err != nil { + return false, err + } + } + return success, nil + } +} + +// ApplyFnBySelector iterates through every object in ResourceList.items, and if +// it satisfies the selector, fn will be applied on it. +func ApplyFnBySelector(rl *ResourceList, selector func(obj *KubeObject) bool, fn func(obj *KubeObject) error) error { + var results Results + for i, obj := range rl.Items { + if !selector(obj) { + continue + } + err := fn(rl.Items[i]) + if err == nil { + continue + } + switch te := err.(type) { + case Results: + results = append(results, te...) + case *Result: + results = append(results, te) + default: + results = append(results, ErrorResult(err)) + } + } + if len(results) > 0 { + rl.Results = results + return results + } + return nil +} diff --git a/functions/go/native-config-adaptor/fn/result.go b/functions/go/native-config-adaptor/fn/result.go new file mode 100644 index 000000000..fa6db622a --- /dev/null +++ b/functions/go/native-config-adaptor/fn/result.go @@ -0,0 +1,299 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "sort" + "strings" + + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +// Context provides a series of functions to add `Result` to `ResourceList.Results`. +type Context struct { + results *Results +} + +func (c *Context) Result(message string, severity Severity) { + *c.results = append(*c.results, &Result{Message: message, Severity: severity}) +} + +// AddErrResultAndDie adds an Error `Result` and terminates the KRM function run. `KubeObject` can be nil. +func (c *Context) ResultErrAndDie(message string, o *KubeObject) { + c.ResultErr(message, o) + panic(errResultEnd{obj: o, message: message}) +} + +// AddErrResult adds an Error `Result` and continues the KRM function run. `KubeObject` can be nil. +func (c *Context) ResultErr(message string, o *KubeObject) { + var r *Result + if o != nil { + r = ConfigObjectResult(message, o, Error) + } else { + r = GeneralResult(message, Error) + } + *c.results = append(*c.results, r) +} + +// AddErrResult adds an Info `Result`. `KubeObject` can be nil. +func (c *Context) ResultInfo(message string, o *KubeObject) { + var r *Result + if o != nil { + r = ConfigObjectResult(message, o, Info) + } else { + r = GeneralResult(message, Info) + } + *c.results = append(*c.results, r) +} + +// AddErrResult adds an Info `Result`. `KubeObject` can be nil. +func (c *Context) ResultWarn(message string, o *KubeObject) { + var r *Result + if o != nil { + r = ConfigObjectResult(message, o, Warning) + } else { + r = GeneralResult(message, Info) + } + *c.results = append(*c.results, r) +} + +// Severity indicates the severity of the Result +type Severity string + +const ( + // Error indicates the result is an error. Will cause the function to exit non-0. + Error Severity = "error" + // Warning indicates the result is a warning + Warning Severity = "warning" + // Info indicates the result is an informative message + Info Severity = "info" +) + +// Result defines a validation result +type Result struct { + // Message is a human readable message. This field is required. + Message string `yaml:"message,omitempty" json:"message,omitempty"` + + // Severity is the severity of this result + Severity Severity `yaml:"severity,omitempty" json:"severity,omitempty"` + + // ResourceRef is a reference to a resource. + // Required fields: apiVersion, kind, name. + ResourceRef *yaml.ResourceIdentifier `yaml:"resourceRef,omitempty" json:"resourceRef,omitempty"` + + // Field is a reference to the field in a resource this result refers to + Field *Field `yaml:"field,omitempty" json:"field,omitempty"` + + // File references a file containing the resource this result refers to + File *File `yaml:"file,omitempty" json:"file,omitempty"` + + // Tags is an unstructured key value map stored with a result that may be set + // by external tools to store and retrieve arbitrary metadata + Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"` +} + +func (i Result) Error() string { + return (i).String() +} + +// String provides a human-readable message for the result item +func (i Result) String() string { + identifier := i.ResourceRef + var idStringList []string + if identifier != nil { + if identifier.APIVersion != "" { + idStringList = append(idStringList, identifier.APIVersion) + } + if identifier.Kind != "" { + idStringList = append(idStringList, identifier.Kind) + } + if identifier.Namespace != "" { + idStringList = append(idStringList, identifier.Namespace) + } + if identifier.Name != "" { + idStringList = append(idStringList, identifier.Name) + } + } + formatString := "[%s]" + severity := i.Severity + // We default Severity to Info when converting a result to a message. + if i.Severity == "" { + severity = Info + } + list := []interface{}{severity} + if len(idStringList) > 0 { + formatString += " %s" + list = append(list, strings.Join(idStringList, "/")) + } + if i.Field != nil { + formatString += " %s" + list = append(list, i.Field.Path) + } + formatString += ": %s" + list = append(list, i.Message) + return fmt.Sprintf(formatString, list...) +} + +// File references a file containing a resource +type File struct { + // Path is relative path to the file containing the resource. + // This field is required. + Path string `yaml:"path,omitempty" json:"path,omitempty"` + + // Index is the index into the file containing the resource + // (i.e. if there are multiple resources in a single file) + Index int `yaml:"index,omitempty" json:"index,omitempty"` +} + +// Field references a field in a resource +type Field struct { + // Path is the field path. This field is required. + Path string `yaml:"path,omitempty" json:"path,omitempty"` + + // CurrentValue is the current field value + CurrentValue interface{} `yaml:"currentValue,omitempty" json:"currentValue,omitempty"` + + // ProposedValue is the proposed value of the field to fix an issue. + ProposedValue interface{} `yaml:"proposedValue,omitempty" json:"proposedValue,omitempty"` +} + +type Results []*Result + +// Error enables Results to be returned as an error +func (e Results) Error() string { + var msgs []string + for _, i := range e { + msgs = append(msgs, i.String()) + } + return strings.Join(msgs, "\n\n") +} + +// ExitCode provides the exit code based on the result's severity +func (e Results) ExitCode() int { + for _, i := range e { + if i.Severity == Error { + return 1 + } + } + return 0 +} + +// Sort performs an in place stable sort of Results +func (e Results) Sort() { + sort.SliceStable(e, func(i, j int) bool { + if fileLess(e, i, j) != 0 { + return fileLess(e, i, j) < 0 + } + if severityLess(e, i, j) != 0 { + return severityLess(e, i, j) < 0 + } + return resultToString(*e[i]) < resultToString(*e[j]) + }) +} + +func severityLess(items Results, i, j int) int { + severityToNumber := map[Severity]int{ + Error: 0, + Warning: 1, + Info: 2, + } + + severityLevelI, found := severityToNumber[items[i].Severity] + if !found { + severityLevelI = 3 + } + severityLevelJ, found := severityToNumber[items[j].Severity] + if !found { + severityLevelJ = 3 + } + return severityLevelI - severityLevelJ +} + +func fileLess(items Results, i, j int) int { + var fileI, fileJ File + if items[i].File == nil { + fileI = File{} + } else { + fileI = *items[i].File + } + if items[j].File == nil { + fileJ = File{} + } else { + fileJ = *items[j].File + } + if fileI.Path != fileJ.Path { + if fileI.Path < fileJ.Path { + return -1 + } + return 1 + } + return fileI.Index - fileJ.Index +} + +func resultToString(item Result) string { + return fmt.Sprintf("resource-ref:%s,field:%s,message:%s", + item.ResourceRef, item.Field, item.Message) +} + +func ErrorConfigFileResult(err error, path string) *Result { + return ConfigFileResult(err.Error(), path, Error) +} + +func ConfigFileResult(msg, path string, severity Severity) *Result { + return &Result{ + Message: msg, + Severity: severity, + File: &File{ + Path: path, + }, + } +} + +func ErrorResult(err error) *Result { + return GeneralResult(err.Error(), Error) +} + +func GeneralResult(msg string, severity Severity) *Result { + return &Result{ + Message: msg, + Severity: severity, + } +} + +func ErrorConfigObjectResult(err error, obj *KubeObject) *Result { + return ConfigObjectResult(err.Error(), obj, Error) +} + +func ConfigObjectResult(msg string, obj *KubeObject, severity Severity) *Result { + return &Result{ + Message: msg, + Severity: severity, + ResourceRef: &yaml.ResourceIdentifier{ + TypeMeta: yaml.TypeMeta{ + APIVersion: obj.GetAPIVersion(), + Kind: obj.GetKind(), + }, + NameMeta: yaml.NameMeta{ + Name: obj.GetName(), + Namespace: obj.GetNamespace(), + }, + }, + File: &File{ + Path: obj.PathAnnotation(), + Index: obj.IndexAnnotation(), + }, + } +} diff --git a/functions/go/native-config-adaptor/fn/run.go b/functions/go/native-config-adaptor/fn/run.go new file mode 100644 index 000000000..28ad4077f --- /dev/null +++ b/functions/go/native-config-adaptor/fn/run.go @@ -0,0 +1,155 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "io" + "io/ioutil" + "os" + + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio" +) + +// AsMain evaluates the ResourceList from STDIN to STDOUT. +// `input` can be +// - a `ResourceListProcessor` which implements `Process` method +// - a function `Runner` which implements `Run` method +func AsMain(input interface{}) error { + err := func() error { + var p ResourceListProcessor + switch input := input.(type) { + case Runner: + p = runnerProcessor{fnRunner: input} + case *Runner: + p = runnerProcessor{fnRunner: *input} + case Generator: + p = generatorProcessor{fnGenerator: input} + case *Generator: + p = generatorProcessor{fnGenerator: *input} + case ResourceListProcessorFunc: + p = input + default: + return fmt.Errorf("unknown input type %T", input) + } + in, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return fmt.Errorf("unable to read from stdin: %v", err) + } + out, err := Run(p, in) + // If there is an error, we don't return the error immediately. + // We write out to stdout before returning any error. + _, outErr := os.Stdout.Write(out) + if outErr != nil { + return outErr + } + return err + }() + if err != nil { + Logf("failed to evaluate function: %v", err) + } + return err +} + +// Run evaluates the function. input must be a resourceList in yaml format. An +// updated resourceList will be returned. +func Run(p ResourceListProcessor, input []byte) (out []byte, err error) { + rl, err := ParseResourceList(input) + if err != nil { + return nil, err + } + defer func() { + // if we run into a panic, we still need to log the error to Results, + // and return the ResourceList and error. + v := recover() + if v != nil { + switch t := v.(type) { + case errKubeObjectFields: + err = &t + case *errKubeObjectFields: + err = t + case errSubObjectFields: + err = &t + case *errSubObjectFields: + err = t + case errResultEnd: + err = &t + case *errResultEnd: + err = t + case ErrAttemptToTouchUpstreamIdentifier: + err = &t + case *ErrAttemptToTouchUpstreamIdentifier: + err = t + case ErrInternalAnnotation: + err = &t + case *ErrInternalAnnotation: + err = t + default: + panic(v) + } + rl.LogResult(err) + out, _ = rl.ToYAML() + } + }() + success, fnErr := p.Process(rl) + out, yamlErr := rl.ToYAML() + if yamlErr != nil { + return out, yamlErr + } + if fnErr != nil { + return out, fnErr + } + if !success { + return out, fmt.Errorf("error: function failure") + } + return out, nil +} + +func Execute(p ResourceListProcessor, r io.Reader, w io.Writer) error { + rw := &byteReadWriter{ + kio.ByteReadWriter{ + Reader: r, + Writer: w, + // We should not set the id annotation in the function, since we should not + // overwrite what the orchestrator set. + OmitReaderAnnotations: true, + // We should not remove the id annotations in the function, since the + // orchestrator (e.g. kpt) may need them. + KeepReaderAnnotations: true, + }, + } + return execute(p, rw) +} + +func execute(p ResourceListProcessor, rw *byteReadWriter) error { + // Read the input + rl, err := rw.Read() + if err != nil { + return errors.WrapPrefixf(err, "failed to read ResourceList input") + } + success, fnErr := p.Process(rl) + // Write the output + if err := rw.Write(rl); err != nil { + return errors.WrapPrefixf(err, "failed to write ResourceList output") + } + if fnErr != nil { + return fnErr + } + if !success { + return fmt.Errorf("error: function failure") + } + return nil +} diff --git a/functions/go/native-config-adaptor/fn/runnerProcessor.go b/functions/go/native-config-adaptor/fn/runnerProcessor.go new file mode 100644 index 000000000..14dc77b4f --- /dev/null +++ b/functions/go/native-config-adaptor/fn/runnerProcessor.go @@ -0,0 +1,52 @@ +// Copyright 2022 Google LLC +// +// 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 fn + +import ( + "fmt" + "reflect" +) + +type runnerProcessor struct { + fnRunner Runner +} + +func (r runnerProcessor) Process(rl *ResourceList) (bool, error) { + ctx := &Context{results: &rl.Results} + r.config(ctx, rl.FunctionConfig) + r.fnRunner.Run(ctx, rl.FunctionConfig, rl.Items) + return true, nil +} + +func (r *runnerProcessor) config(ctx *Context, o *KubeObject) { + fnName := reflect.ValueOf(r.fnRunner).Elem().Type().Name() + switch true { + case o.IsEmpty(): + ctx.Result("`FunctionConfig` is not given", Info) + case o.IsGVK("", "v1", "ConfigMap"): + data := o.NestedStringMapOrDie("data") + fnRunnerElem := reflect.ValueOf(r.fnRunner).Elem() + for i := 0; i < fnRunnerElem.NumField(); i++ { + if fnRunnerElem.Field(i).Kind() == reflect.Map { + fnRunnerElem.Field(i).Set(reflect.ValueOf(data)) + break + } + } + case o.IsGVK("fn.kpt.dev", "v1alpha1", fnName): + o.AsOrDie(r.fnRunner) + default: + ctx.ResultErrAndDie(fmt.Sprintf("unknown FunctionConfig `%v`, expect %v", o.GetKind(), fnName), o) + } +} diff --git a/functions/go/native-config-adaptor/generator/adaptor.go b/functions/go/native-config-adaptor/generator/adaptor.go new file mode 100644 index 000000000..7a542b3f5 --- /dev/null +++ b/functions/go/native-config-adaptor/generator/adaptor.go @@ -0,0 +1,195 @@ +// Copyright 2022 Google LLC +// +// 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 generator + +import ( + "path/filepath" + "reflect" + "strings" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/fn" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/parsers" +) + +const NativeConfigOpBackToNative = "writeBackToNativeConfig" + +var SupportedFromNative = map[string]func(object *fn.KubeObject, content string, ShouldFlatten bool) error{ + "IniFile": parsers.FromIniFile, +} + +var SupportedToNative = map[string]func(object *fn.KubeObject, fileRef, filename string, IsFlatten bool) (*fn.KubeObject, error){ + "IniFile": parsers.ToIniFile, +} + +var _ fn.Generator = &NativeConfigAdaptor{} + +func NewNativeConfigAdaptor(config *fn.KubeObject) *NativeConfigAdaptor { + var cfg NativeConfigAdaptor + config.As(&cfg) + return &cfg +} + +func NewFromSource(source *NativeConfigSource) (*fn.KubeObject, error) { + nca := &NativeConfigAdaptor{ + + Spec: &NativeConfigSpec{ + Source: []*NativeConfigSource{ + source, + }, + }, + } + object, err := fn.NewFromTypedObject(nca) + if err != nil { + return nil, err + } + object.SetKind(reflect.TypeOf(NativeConfigAdaptor{}).Name()) + object.SetAPIVersion(fn.KptFunctionApiVersion) + return object, nil +} + +func (t *NativeConfigAdaptor) Generate(ctx *fn.Context, fnConfig *fn.KubeObject, items fn.KubeObjects) fn.KubeObjects { + var generatedObjects []*fn.KubeObject + for _, source := range t.Spec.Source { + if source.LocalFileRef != "" { + localfileObjects := items.Where(func(o *fn.KubeObject) bool { + switch true { + case o.GetKind() != fn.NonKrmKind: + return false + case o.GetAPIVersion() != fn.KptFunctionApiVersion: + return false + case o.GetName() != source.LocalFileRef: + return false + default: + return true + } + }) + sotObjects := items.Where(func(o *fn.KubeObject) bool { + return o.GetAnnotation(fn.GeneratorBuiltinIdentifier) != "" + }) + + switch source.Operation { + case NativeConfigOpBackToNative: + for _, canonicalObject := range sotObjects { + nonKrmObject := t.WriteFromCanonicalToNative(ctx, canonicalObject, source) + existingNonKrmObjects := items.Where(func(o *fn.KubeObject) bool { + return o.GetId() == nonKrmObject.GetId() + }) + if len(existingNonKrmObjects) > 0 { + content := nonKrmObject.GetMap("spec").NestedStringMapOrDie("content") + existingNonKrmObjects[0].GetMap("spec").SetNestedStringMap(content, "content") + } else { + generatedObjects = append(generatedObjects, nonKrmObject) + } + rawObject := StoreRawDataInConfigMap(fnConfig.GetName(), + filepath.Base(source.LocalFile), + nonKrmObject.NestedStringOrDie("spec", "content")) + existingrawObjects := items.Where(func(o *fn.KubeObject) bool { + return o.GetId() == rawObject.GetId() + }) + if len(existingrawObjects) > 0 { + existingrawObjects[0].SetNestedStringMapOrDie(rawObject.NestedStringMapOrDie("data"), "data") + } else { + generatedObjects = append(generatedObjects, rawObject) + } + } + default: + for _, object := range localfileObjects { + content := object.NestedStringOrDie("spec", "content") + localFileName := object.NestedStringOrDie("spec", "filename") + newObject := t.ParseFromNativeToCanonical(ctx, object.GetName(), content, source) + rawObject := StoreRawDataInConfigMap(object.GetName(), localFileName, content) + generatedObjects = append(generatedObjects, newObject, rawObject) + } + } + } + } + return generatedObjects +} + +func StoreRawDataInConfigMap(name, filename, content string) *fn.KubeObject { + object := fn.NewEmptyKubeObject() + object.SetKind("ConfigMap") + object.SetAPIVersion("v1") + object.SetName(name) + data := map[string]string{ + filename: content, + } + object.SetNestedStringMap(data, "data") + return object +} + +func (t *NativeConfigAdaptor) WriteFromCanonicalToNative(ctx *fn.Context, canonicalObject *fn.KubeObject, source *NativeConfigSource) *fn.KubeObject { + toFn, ok := SupportedToNative[source.Format] + if !ok { + ctx.ResultErrAndDie("unknown parsing type "+source.Format, nil) + return nil + } + nonKrmObject, err := toFn(canonicalObject, source.LocalFileRef, source.LocalFile, source.AsConfigMap) + if err != nil { + ctx.ResultErrAndDie(err.Error(), canonicalObject) + } + return nonKrmObject +} + +func (t *NativeConfigAdaptor) ParseFromNativeToCanonical(ctx *fn.Context, name, content string, source *NativeConfigSource) *fn.KubeObject { + fromFn, ok := SupportedFromNative[source.Format] + if !ok { + ctx.ResultErrAndDie("unknown parsing type "+source.Format, nil) + return nil + } + object := NewCanonicalObject(name, source) + if err := fromFn(object, content, source.AsConfigMap); err != nil { + ctx.ResultErrAndDie(err.Error(), nil) + } + return object +} + +func NewCanonicalObject(name string, source *NativeConfigSource) *fn.KubeObject { + lenseNameSlitted := strings.Split(source.Format, "_") + camelcaseLense := "" + for _, segment := range lenseNameSlitted { + camelcaseLense += strings.ToUpper(string(segment[0])) + segment[1:] + } + + object := fn.NewEmptyKubeObject() + + if source.AsConfigMap { + object.SetKind("ConfigMap") + object.SetAPIVersion("v1") + object.SetName(name + "-internal") + object.SetAnnotation(fn.KptLocalConfig, "true") + } else { + object.SetKind(camelcaseLense) + object.SetAPIVersion("config.kpt.dev/v1alpha1") + object.SetName(name + "-internal") + object.SetAnnotation(fn.KptLocalConfig, "true") + } + return object +} + +type NativeConfigAdaptor struct { + Spec *NativeConfigSpec `json:"spec,omitempty" yaml:"spec,omitempty"` +} + +type NativeConfigSpec struct { + Source []*NativeConfigSource `json:"source,omitempty" yaml:"source,omitempty"` +} + +type NativeConfigSource struct { + LocalFileRef string `json:"localFileRef,omitempty" yaml:"localFileRef,omitempty"` + LocalFile string `json:"localFile,omitempty" yaml:"localFile,omitempty"` + Format string `json:"format,omitempty" yaml:"format,omitempty"` + Operation string `json:"operation,omitempty" yaml:"operation,omitempty"` + AsConfigMap bool `json:"asConfigMap,omitempty" yaml:"asConfigMap,omitempty"` +} diff --git a/functions/go/native-config-adaptor/go.mod b/functions/go/native-config-adaptor/go.mod new file mode 100644 index 000000000..b3c59c6f2 --- /dev/null +++ b/functions/go/native-config-adaptor/go.mod @@ -0,0 +1,31 @@ +module github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor + +go 1.17 + +require ( + gopkg.in/yaml.v3 v3.0.1 + honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 + k8s.io/klog/v2 v2.70.1 + sigs.k8s.io/kustomize/kyaml v0.13.7 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/stretchr/testify v1.8.0 // indirect + github.com/xlab/treeprint v1.1.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect +) diff --git a/functions/go/native-config-adaptor/go.sum b/functions/go/native-config-adaptor/go.sum new file mode 100644 index 000000000..18ea79fd9 --- /dev/null +++ b/functions/go/native-config-adaptor/go.sum @@ -0,0 +1,724 @@ +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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +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/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/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/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +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-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +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/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +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/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/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/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +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/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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +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 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/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +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= +github.com/yuin/goldmark v1.4.1/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.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +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/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +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-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-20200520004742-59133d7f0dd7/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/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +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-20180909124046-d0be0721c37e/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-20190904154756-749cb33beabd/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-20191120155948-bd437916bb0e/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-20210112080510-489259a85091/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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-20200505023115-26f46d2f7ef8/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-20201224043029-2b0845dc783e/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/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +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-20201019141844-1ed22bb0c154/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/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +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/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.3.0/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E= +honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE= +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-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs= +k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +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.7 h1:/EZ/nPaLUzeJKF/BuJ4QCuMVJWiEVoI8iftOHY3g3tk= +sigs.k8s.io/kustomize/kyaml v0.13.7/go.mod h1:6K+IUOuir3Y7nucPRAjw9yth04KSWBnP5pqUTGwj/qU= +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/native-config-adaptor/kpt-test/native-config-adaptor-fn-config.yaml b/functions/go/native-config-adaptor/kpt-test/native-config-adaptor-fn-config.yaml new file mode 100644 index 000000000..898807eb2 --- /dev/null +++ b/functions/go/native-config-adaptor/kpt-test/native-config-adaptor-fn-config.yaml @@ -0,0 +1,12 @@ +spec: + source: + - asConfigMap: true + format: IniFile + localFile: my.cnf + localFileRef: mycnf-ini-file + operation: writeBackToNativeConfig +kind: NativeConfigAdaptor +apiVersion: fn.kpt.dev/v1alpha1 +metadata: + name: mariadb + namespace: example \ No newline at end of file diff --git a/functions/go/native-config-adaptor/kpt-test/resource.yaml b/functions/go/native-config-adaptor/kpt-test/resource.yaml new file mode 100644 index 000000000..718c0708b --- /dev/null +++ b/functions/go/native-config-adaptor/kpt-test/resource.yaml @@ -0,0 +1,232 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: 'apps|StatefulSet|example|mariadb' + config.kubernetes.io/path: 'statefulset-mariadb.yaml' + internal.config.kubernetes.io/path: 'statefulset-mariadb.yaml' +spec: + replicas: 1 + revisionHistoryLimit: 10 + serviceName: mariadb + updateStrategy: + type: RollingUpdate + template: + spec: + securityContext: + fsGroup: 1001 + containers: + - name: mariadb + image: docker.io/bitnami/mariadb:10.6.7-debian-10-r62 + imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "true" + - name: MARIADB_USER + value: bn_ghost + - name: MARIADB_DATABASE + value: bitnami_ghost + - name: ALLOW_EMPTY_PASSWORD + value: "true" + ports: + - name: mysql + containerPort: 3306 + resources: + limits: {} + requests: {} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.ini + subPath: my.ini + volumes: + - name: config + configMap: + name: mariadb-92256k9879 + metadata: + labels: + app.kubernetes.io/name: mariadb + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/name: mariadb + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + selector: + matchLabels: + app.kubernetes.io/name: mariadb +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3308 # a different change from upstream\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=16M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: + name: mycnf-ini-file + annotations: + internal.config.kubernetes.io/path: 'builtinnonkrm_mycnf-ini-file.yaml' + config.kubernetes.io/path: 'builtinnonkrm_mycnf-ini-file.yaml' +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' + config.kubernetes.io/path: 'configmap-generator.yaml' + internal.config.kubernetes.io/path: 'configmap-generator.yaml' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig +--- +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user2 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" + config.kubernetes.io/path: 'Kptfile' + internal.config.kubernetes.io/path: 'Kptfile' +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 58a76ec42b1e2fd3b9a63b651c778bb3a1a2d5f5 +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-kt25k6kbc2 + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.config.kubernetes.io/path: 'configmap_mariadb-kt25k6kbc2.yaml' + config.kubernetes.io/path: 'configmap_mariadb-kt25k6kbc2.yaml' +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3308 # a different change from upstream + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-internal-7bc2mh52gc + namespace: example + annotations: + internal.config.kubernetes.io/index: "0" + internal.config.kubernetes.io/path: ConfigMap_mariadb-internal-7bc2mh52gc.yaml + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + config.kubernetes.io/path: 'ConfigMap_mariadb-internal-7bc2mh52gc.yaml' + config.kubernetes.io/index: '0' + internal.kpt.dev/generated-builtin-merged: "true" +data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 16M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3308" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' + config.kubernetes.io/path: 'service-mariadb.yaml' + internal.config.kubernetes.io/path: 'service-mariadb.yaml' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + selector: + app.kubernetes.io/name: mariadb \ No newline at end of file diff --git a/functions/go/native-config-adaptor/kpt-test/result/builtinnonkrm_mycnf-ini-file.yaml b/functions/go/native-config-adaptor/kpt-test/result/builtinnonkrm_mycnf-ini-file.yaml new file mode 100644 index 000000000..9eb9a1762 --- /dev/null +++ b/functions/go/native-config-adaptor/kpt-test/result/builtinnonkrm_mycnf-ini-file.yaml @@ -0,0 +1,5 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {filename: my.cnf, content: "[mysqld]\nlog-error = /opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time = 10.0\nplugin_dir = /opt/bitnami/mariadb/plugin\nslow_query_log = 0\nsocket = /opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir = /opt/bitnami/mariadb/tmp\nbasedir = /opt/bitnami/mariadb\nmax_allowed_packet = 16M\npid-file = /opt/bitnami/mariadb/tmp/mysqld.pid\nport = 3308\nslow_query_log_file = /opt/bitnami/mariadb/logs/mysqld.log\nbind-address = *\ncharacter-set-server = UTF8\ncollation-server = utf8_general_ci\n\n[manager]\npid-file = /opt/bitnami/mariadb/tmp/mysqld.pid\nsocket = /opt/bitnami/mariadb/tmp/mysql.sock\nport = 3306\n\n[client]\ndefault-character-set = UTF8\nplugin_dir = /opt/bitnami/mariadb/plugin\nport = 3306\nsocket = /opt/bitnami/mariadb/tmp/mysql.sock\n"} +metadata: + name: mycnf-ini-file diff --git a/functions/go/native-config-adaptor/kpt-test/result/configmap_mariadb.yaml b/functions/go/native-config-adaptor/kpt-test/result/configmap_mariadb.yaml new file mode 100644 index 000000000..3ac5403a5 --- /dev/null +++ b/functions/go/native-config-adaptor/kpt-test/result/configmap_mariadb.yaml @@ -0,0 +1,32 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb +data: + my.cnf: | + [mysqld] + log-error = /opt/bitnami/mariadb/logs/mysqld.log + long_query_time = 10.0 + plugin_dir = /opt/bitnami/mariadb/plugin + slow_query_log = 0 + socket = /opt/bitnami/mariadb/tmp/mysql.sock + tmpdir = /opt/bitnami/mariadb/tmp + basedir = /opt/bitnami/mariadb + max_allowed_packet = 16M + pid-file = /opt/bitnami/mariadb/tmp/mysqld.pid + port = 3308 + slow_query_log_file = /opt/bitnami/mariadb/logs/mysqld.log + bind-address = * + character-set-server = UTF8 + collation-server = utf8_general_ci + + [manager] + pid-file = /opt/bitnami/mariadb/tmp/mysqld.pid + socket = /opt/bitnami/mariadb/tmp/mysql.sock + port = 3306 + + [client] + default-character-set = UTF8 + plugin_dir = /opt/bitnami/mariadb/plugin + port = 3306 + socket = /opt/bitnami/mariadb/tmp/mysql.sock diff --git a/functions/go/native-config-adaptor/kpt-test/result/native-config-adaptor-fn-config.yaml b/functions/go/native-config-adaptor/kpt-test/result/native-config-adaptor-fn-config.yaml new file mode 100644 index 000000000..d1f9d5f58 --- /dev/null +++ b/functions/go/native-config-adaptor/kpt-test/result/native-config-adaptor-fn-config.yaml @@ -0,0 +1,12 @@ +spec: + source: + - asConfigMap: true + format: IniFile + localFile: my.cnf + localFileRef: mycnf-ini-file + operation: writeBackToNativeConfig +kind: NativeConfigAdaptor +apiVersion: fn.kpt.dev/v1alpha1 +metadata: + name: mariadb + namespace: example diff --git a/functions/go/native-config-adaptor/kpt-test/result/resource.yaml b/functions/go/native-config-adaptor/kpt-test/result/resource.yaml new file mode 100644 index 000000000..e7b901161 --- /dev/null +++ b/functions/go/native-config-adaptor/kpt-test/result/resource.yaml @@ -0,0 +1,215 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: 'apps|StatefulSet|example|mariadb' +spec: + replicas: 1 + revisionHistoryLimit: 10 + serviceName: mariadb + updateStrategy: + type: RollingUpdate + template: + spec: + securityContext: + fsGroup: 1001 + containers: + - name: mariadb + image: docker.io/bitnami/mariadb:10.6.7-debian-10-r62 + imagePullPolicy: IfNotPresent + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "true" + - name: MARIADB_USER + value: bn_ghost + - name: MARIADB_DATABASE + value: bitnami_ghost + - name: ALLOW_EMPTY_PASSWORD + value: "true" + ports: + - name: mysql + containerPort: 3306 + resources: + limits: {} + requests: {} + volumeMounts: + - name: data + mountPath: /bitnami/mariadb + - name: config + mountPath: /opt/bitnami/mariadb/conf/my.ini + subPath: my.ini + volumes: + - name: config + configMap: + name: mariadb-92256k9879 + metadata: + labels: + app.kubernetes.io/name: mariadb + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/name: mariadb + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + selector: + matchLabels: + app.kubernetes.io/name: mariadb +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: BuiltInNonKrm +spec: {content: "[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/opt/bitnami/mariadb\nplugin_dir=/opt/bitnami/mariadb/plugin\nport=3308 # a different change from upstream\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ntmpdir=/opt/bitnami/mariadb/tmp\nmax_allowed_packet=16M\nbind-address=*\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid\nlog-error=/opt/bitnami/mariadb/logs/mysqld.log\ncharacter-set-server=UTF8\ncollation-server=utf8_general_ci\nslow_query_log=0\nslow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log\nlong_query_time=10.0\n\n[client]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/opt/bitnami/mariadb/plugin\n\n[manager]\nport=3306\nsocket=/opt/bitnami/mariadb/tmp/mysql.sock\npid-file=/opt/bitnami/mariadb/tmp/mysqld.pid", filename: my.cnf} +metadata: + name: mycnf-ini-file +--- +apiVersion: fn.kpt.dev/v1alpha1 +kind: ConfigMapGenerator +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + annotations: + config.kubernetes.io/local-config: "true" + internal.kpt.dev/upstream-identifier: 'fn.kpt.dev|ConfigMapGenerator|example|mariadb' +spec: + source: + - format: IniFile # optional + localFileRef: mycnf-ini-file + asConfigMap: true + operation: writeBackToNativeConfig +--- +apiVersion: kpt.dev/v1 +kind: Kptfile +metadata: + name: mariadb-user2 + labels: + app.kubernetes.io/name: mariadb + annotations: + config.kubernetes.io/local-config: "true" +upstream: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + updateStrategy: resource-merge +upstreamLock: + type: git + git: + repo: http://github.com/yuwenma/kpt-samples + directory: /ghost/mariadb + ref: poc-e2e-user1 + commit: 58a76ec42b1e2fd3b9a63b651c778bb3a1a2d5f5 +info: + description: The MariaDB which provides the storage for Ghost. +pipeline: + mutators: + - image: gcr.io/kpt-fn/set-namespace:v0.3.4 + configPath: package-context.yaml +pkgAutoRun: + inclNonKrmFiles: + - name: mycnf-ini-file + path: my.cnf + pipeline: + - image: gcr.io/kpt-fn-demo/configmap-generator:yuwen-v0.1 + configPath: configmap-generator.yaml +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-kt25k6kbc2 + annotations: + internal.kpt.dev/generator: fn.kpt.dev|ConfigMapGenerator|example|mariadb +data: + my.cnf: |- + [mysqld] + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mariadb + plugin_dir=/opt/bitnami/mariadb/plugin + port=3308 # a different change from upstream + socket=/opt/bitnami/mariadb/tmp/mysql.sock + tmpdir=/opt/bitnami/mariadb/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid + log-error=/opt/bitnami/mariadb/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mariadb/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mariadb/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mariadb/tmp/mysql.sock + pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid +immutable: true +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: mariadb-internal-7bc2mh52gc + namespace: example + annotations: + internal.kpt.dev/generator-builtin-only: fn.kpt.dev|ConfigMapGenerator|example|mariadb + internal.kpt.dev/generated-builtin-merged: "true" +data: + cm.client.default-character-set: UTF8 + cm.client.plugin_dir: /opt/bitnami/mariadb/plugin + cm.client.port: "3306" + cm.client.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.manager.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.manager.port: "3306" + cm.manager.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.basedir: /opt/bitnami/mariadb + cm.mysqld.bind-address: '*' + cm.mysqld.character-set-server: UTF8 + cm.mysqld.collation-server: utf8_general_ci + cm.mysqld.log-error: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.long_query_time: "10.0" + cm.mysqld.max_allowed_packet: 16M + cm.mysqld.pid-file: /opt/bitnami/mariadb/tmp/mysqld.pid + cm.mysqld.plugin_dir: /opt/bitnami/mariadb/plugin + cm.mysqld.port: "3308" + cm.mysqld.slow_query_log: "0" + cm.mysqld.slow_query_log_file: /opt/bitnami/mariadb/logs/mysqld.log + cm.mysqld.socket: /opt/bitnami/mariadb/tmp/mysql.sock + cm.mysqld.tmpdir: /opt/bitnami/mariadb/tmp +--- +# Source: ghost/charts/mariadb/templates/primary/svc.yaml +apiVersion: v1 +kind: Service +metadata: # kpt-merge: example/mariadb + name: mariadb + namespace: example + labels: + app.kubernetes.io/name: mariadb + annotations: + internal.kpt.dev/upstream-identifier: '|Service|example|mariadb' +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: mysql + port: 3306 + protocol: TCP + targetPort: mysql + selector: + app.kubernetes.io/name: mariadb diff --git a/functions/go/native-config-adaptor/main.go b/functions/go/native-config-adaptor/main.go new file mode 100644 index 000000000..f6ec5fb16 --- /dev/null +++ b/functions/go/native-config-adaptor/main.go @@ -0,0 +1,27 @@ +// Copyright 2022 Google LLC +// +// 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 main + +import ( + "os" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/fn" + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/generator" +) + +func main() { + if err := fn.AsMain(&generator.NativeConfigAdaptor{}); err != nil { + os.Exit(1) + } +} diff --git a/functions/go/native-config-adaptor/native-config.yaml b/functions/go/native-config-adaptor/native-config.yaml new file mode 100644 index 000000000..b4903cf2b --- /dev/null +++ b/functions/go/native-config-adaptor/native-config.yaml @@ -0,0 +1,11 @@ +apiVersion: fn.kpt.dev/v1alpha1 +kind: NativeConfigAdaptor +metadata: + name: builtin +spec: + operation: writeBackToNativeConfig # default to generate canonical KRM object from Native Config + source: + - format: IniFile # optional + localFile: /etc/my.cnf + asConfigMap: true + schema: {} # Only support string map now. Should be OpenApi schema to tell the canonical KRM resource format diff --git a/functions/go/native-config-adaptor/parsers/inifile.go b/functions/go/native-config-adaptor/parsers/inifile.go new file mode 100644 index 000000000..a6e6d0411 --- /dev/null +++ b/functions/go/native-config-adaptor/parsers/inifile.go @@ -0,0 +1,77 @@ +package parsers + +import ( + "bytes" + "fmt" + "strings" + + "github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/native-config-adaptor/fn" + "gopkg.in/ini.v1" +) + +const ( + ConfigMapPrefix = "cm." +) + +func ToIniFile(canonicalObject *fn.KubeObject, fileRef, fileName string, IsFlatten bool) (*fn.KubeObject, error) { + if !IsFlatten { + return nil, fmt.Errorf("not implemented") + } + cfg := ini.Empty(ini.LoadOptions{ + SkipUnrecognizableLines: true, + AllowBooleanKeys: true, + }) + for key, value := range canonicalObject.NestedStringMapOrDie("data") { + prefixSectionKey := strings.Split(key, ".") + var createSectionErr error + section, err := cfg.GetSection(prefixSectionKey[1]) + if err != nil { + section, createSectionErr = cfg.NewSection(prefixSectionKey[1]) + if createSectionErr != nil { + return nil, createSectionErr + } + } + section.NewKey(prefixSectionKey[2], value) + } + out := &bytes.Buffer{} + ini.PrettyFormat = false + cfg.WriteTo(out) + newNonKrmObject := fn.NewNonKrmResource() + p, e := fn.NewFromTypedObject(newNonKrmObject) + p.SetName(fileRef) + p.SetNestedStringOrDie(fileName, "spec", "filename") + p.SetNestedStringOrDie(out.String(), "spec", "content") + return p, e +} + +// Read IniFile from content, write to Object +func FromIniFile(object *fn.KubeObject, content string, ShouldFlatten bool) error { + cfg, err := ini.LoadSources(ini.LoadOptions{SkipUnrecognizableLines: true}, []byte(content)) + if err != nil { + return err + } + if ShouldFlatten { + data := map[string]string{} + for _, section := range cfg.Sections() { + for _, key := range section.Keys() { + cmKey := ConfigMapPrefix + section.Name() + "." + key.Name() + data[cmKey] = key.Value() + } + } + if err = object.SetNestedStringMap(data, "data"); err != nil { + return err + } + return nil + } + + spec := object.UpsertMap("spec") + for _, section := range cfg.Sections() { + subObject := spec.UpsertMap(section.Name()) + for _, key := range section.Keys() { + if err = subObject.SetNestedString(key.Value(), key.Name()); err != nil { + return err + } + } + } + return nil +}