From 11ccaeca35095e30fa346a8f956ed92d7b1b6bf5 Mon Sep 17 00:00:00 2001 From: droot Date: Mon, 12 Dec 2022 21:37:10 +0000 Subject: [PATCH 01/38] rollouts: added top level directory --- rollouts/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 rollouts/README.md diff --git a/rollouts/README.md b/rollouts/README.md new file mode 100644 index 0000000000..71160aea6b --- /dev/null +++ b/rollouts/README.md @@ -0,0 +1,5 @@ +# Rollouts + +Rollouts is a component for deploying KRM configuration to multiple kubernetes clusters. + +More details coming soon. \ No newline at end of file From e7b4b02028a53d4b03662a8076ebb8e08409a173 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Mon, 12 Dec 2022 16:56:46 -0800 Subject: [PATCH 02/38] rollouts: scaffolded the project using kubebuilder (#3689) --- rollouts/.dockerignore | 4 + rollouts/.gitignore | 26 + rollouts/Dockerfile | 33 + rollouts/Makefile | 157 ++++ rollouts/PROJECT | 16 + rollouts/api/v1alpha1/groupversion_info.go | 36 + rollouts/api/v1alpha1/rollout_types.go | 64 ++ .../api/v1alpha1/zz_generated.deepcopy.go | 115 +++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 49 ++ rollouts/config/crd/kustomization.yaml | 21 + rollouts/config/crd/kustomizeconfig.yaml | 19 + .../crd/patches/cainjection_in_rollouts.yaml | 7 + .../crd/patches/webhook_in_rollouts.yaml | 16 + rollouts/config/default/kustomization.yaml | 72 ++ .../default/manager_auth_proxy_patch.yaml | 55 ++ .../config/default/manager_config_patch.yaml | 10 + rollouts/config/manager/kustomization.yaml | 2 + rollouts/config/manager/manager.yaml | 102 +++ rollouts/config/prometheus/kustomization.yaml | 2 + rollouts/config/prometheus/monitor.yaml | 26 + .../rbac/auth_proxy_client_clusterrole.yaml | 16 + rollouts/config/rbac/auth_proxy_role.yaml | 24 + .../config/rbac/auth_proxy_role_binding.yaml | 19 + rollouts/config/rbac/auth_proxy_service.yaml | 21 + rollouts/config/rbac/kustomization.yaml | 18 + .../config/rbac/leader_election_role.yaml | 44 + .../rbac/leader_election_role_binding.yaml | 19 + rollouts/config/rbac/role.yaml | 33 + rollouts/config/rbac/role_binding.yaml | 19 + rollouts/config/rbac/rollout_editor_role.yaml | 31 + rollouts/config/rbac/rollout_viewer_role.yaml | 27 + rollouts/config/rbac/service_account.yaml | 12 + .../samples/gitops_v1alpha1_rollout.yaml | 12 + rollouts/controllers/rollout_controller.go | 62 ++ rollouts/controllers/suite_test.go | 80 ++ rollouts/go.mod | 81 ++ rollouts/go.sum | 796 ++++++++++++++++++ rollouts/hack/boilerplate.go.txt | 15 + rollouts/main.go | 115 +++ 39 files changed, 2276 insertions(+) create mode 100644 rollouts/.dockerignore create mode 100644 rollouts/.gitignore create mode 100644 rollouts/Dockerfile create mode 100644 rollouts/Makefile create mode 100644 rollouts/PROJECT create mode 100644 rollouts/api/v1alpha1/groupversion_info.go create mode 100644 rollouts/api/v1alpha1/rollout_types.go create mode 100644 rollouts/api/v1alpha1/zz_generated.deepcopy.go create mode 100644 rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml create mode 100644 rollouts/config/crd/kustomization.yaml create mode 100644 rollouts/config/crd/kustomizeconfig.yaml create mode 100644 rollouts/config/crd/patches/cainjection_in_rollouts.yaml create mode 100644 rollouts/config/crd/patches/webhook_in_rollouts.yaml create mode 100644 rollouts/config/default/kustomization.yaml create mode 100644 rollouts/config/default/manager_auth_proxy_patch.yaml create mode 100644 rollouts/config/default/manager_config_patch.yaml create mode 100644 rollouts/config/manager/kustomization.yaml create mode 100644 rollouts/config/manager/manager.yaml create mode 100644 rollouts/config/prometheus/kustomization.yaml create mode 100644 rollouts/config/prometheus/monitor.yaml create mode 100644 rollouts/config/rbac/auth_proxy_client_clusterrole.yaml create mode 100644 rollouts/config/rbac/auth_proxy_role.yaml create mode 100644 rollouts/config/rbac/auth_proxy_role_binding.yaml create mode 100644 rollouts/config/rbac/auth_proxy_service.yaml create mode 100644 rollouts/config/rbac/kustomization.yaml create mode 100644 rollouts/config/rbac/leader_election_role.yaml create mode 100644 rollouts/config/rbac/leader_election_role_binding.yaml create mode 100644 rollouts/config/rbac/role.yaml create mode 100644 rollouts/config/rbac/role_binding.yaml create mode 100644 rollouts/config/rbac/rollout_editor_role.yaml create mode 100644 rollouts/config/rbac/rollout_viewer_role.yaml create mode 100644 rollouts/config/rbac/service_account.yaml create mode 100644 rollouts/config/samples/gitops_v1alpha1_rollout.yaml create mode 100644 rollouts/controllers/rollout_controller.go create mode 100644 rollouts/controllers/suite_test.go create mode 100644 rollouts/go.mod create mode 100644 rollouts/go.sum create mode 100644 rollouts/hack/boilerplate.go.txt create mode 100644 rollouts/main.go diff --git a/rollouts/.dockerignore b/rollouts/.dockerignore new file mode 100644 index 0000000000..0f046820f1 --- /dev/null +++ b/rollouts/.dockerignore @@ -0,0 +1,4 @@ +# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file +# Ignore build and test binaries. +bin/ +testbin/ diff --git a/rollouts/.gitignore b/rollouts/.gitignore new file mode 100644 index 0000000000..e917e5cefe --- /dev/null +++ b/rollouts/.gitignore @@ -0,0 +1,26 @@ + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin +testbin/* +Dockerfile.cross + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Kubernetes Generated files - skip generated files, except for vendored files + +!vendor/**/zz_generated.* + +# editor and IDE paraphernalia +.idea +*.swp +*.swo +*~ diff --git a/rollouts/Dockerfile b/rollouts/Dockerfile new file mode 100644 index 0000000000..8f9cca18eb --- /dev/null +++ b/rollouts/Dockerfile @@ -0,0 +1,33 @@ +# Build the manager binary +FROM golang:1.19 as builder +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY main.go main.go +COPY api/ api/ +COPY controllers/ controllers/ + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/rollouts/Makefile b/rollouts/Makefile new file mode 100644 index 0000000000..9116f85220 --- /dev/null +++ b/rollouts/Makefile @@ -0,0 +1,157 @@ + +# Image URL to use all building/pushing image targets +IMG ?= controller:latest +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.25.0 + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out + +##@ Build + +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager main.go + +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./main.go + +# If you wish built the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: test ## Build docker image with the manager. + docker build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + docker push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ +# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) +# To properly provided solutions that supports more than one platform you should use this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: test ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - docker buildx create --name project-v3-builder + docker buildx use project-v3-builder + - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - docker buildx rm project-v3-builder + rm Dockerfile.cross + +##@ Deployment + +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl apply -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | kubectl apply -f - + +.PHONY: undeploy +undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Build Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest + +## Tool Versions +KUSTOMIZE_VERSION ?= v3.8.7 +CONTROLLER_TOOLS_VERSION ?= v0.10.0 + +KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +$(KUSTOMIZE): $(LOCALBIN) + @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ + echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ + rm -rf $(LOCALBIN)/kustomize; \ + fi + test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); } + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +$(CONTROLLER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +$(ENVTEST): $(LOCALBIN) + test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest diff --git a/rollouts/PROJECT b/rollouts/PROJECT new file mode 100644 index 0000000000..cc6c81fa2d --- /dev/null +++ b/rollouts/PROJECT @@ -0,0 +1,16 @@ +domain: kpt.dev +layout: +- go.kubebuilder.io/v3 +projectName: rollouts +repo: github.com/GoogleContainerTools/kpt +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kpt.dev + group: gitops + kind: Rollout + path: github.com/GoogleContainerTools/kpt/api/v1alpha1 + version: v1alpha1 +version: "3" diff --git a/rollouts/api/v1alpha1/groupversion_info.go b/rollouts/api/v1alpha1/groupversion_info.go new file mode 100644 index 0000000000..7fe4e678e6 --- /dev/null +++ b/rollouts/api/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2022. + +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 v1alpha1 contains API Schema definitions for the gitops v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=gitops.kpt.dev +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "gitops.kpt.dev", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go new file mode 100644 index 0000000000..78ab336d38 --- /dev/null +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -0,0 +1,64 @@ +/* +Copyright 2022. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// RolloutSpec defines the desired state of Rollout +type RolloutSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Test is an dummy test field of Rollout. + Test string `json:"test,omitempty"` +} + +// RolloutStatus defines the observed state of Rollout +type RolloutStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Rollout is the Schema for the rollouts API +type Rollout struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RolloutSpec `json:"spec,omitempty"` + Status RolloutStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// RolloutList contains a list of Rollout +type RolloutList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Rollout `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Rollout{}, &RolloutList{}) +} diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..f1669ec124 --- /dev/null +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,115 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022. + +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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Rollout) DeepCopyInto(out *Rollout) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rollout. +func (in *Rollout) DeepCopy() *Rollout { + if in == nil { + return nil + } + out := new(Rollout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Rollout) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutList) DeepCopyInto(out *RolloutList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Rollout, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutList. +func (in *RolloutList) DeepCopy() *RolloutList { + if in == nil { + return nil + } + out := new(RolloutList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RolloutList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutSpec. +func (in *RolloutSpec) DeepCopy() *RolloutSpec { + if in == nil { + return nil + } + out := new(RolloutSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutStatus) DeepCopyInto(out *RolloutStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStatus. +func (in *RolloutStatus) DeepCopy() *RolloutStatus { + if in == nil { + return nil + } + out := new(RolloutStatus) + in.DeepCopyInto(out) + return out +} diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml new file mode 100644 index 0000000000..195482c4fd --- /dev/null +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: rollouts.gitops.kpt.dev +spec: + group: gitops.kpt.dev + names: + kind: Rollout + listKind: RolloutList + plural: rollouts + singular: rollout + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Rollout is the Schema for the rollouts API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RolloutSpec defines the desired state of Rollout + properties: + test: + description: Test is an dummy test field of Rollout. + type: string + type: object + status: + description: RolloutStatus defines the observed state of Rollout + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/rollouts/config/crd/kustomization.yaml b/rollouts/config/crd/kustomization.yaml new file mode 100644 index 0000000000..373deb77e3 --- /dev/null +++ b/rollouts/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/gitops.kpt.dev_rollouts.yaml +#+kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_rollouts.yaml +#+kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_rollouts.yaml +#+kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/rollouts/config/crd/kustomizeconfig.yaml b/rollouts/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000000..ec5c150a9d --- /dev/null +++ b/rollouts/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/rollouts/config/crd/patches/cainjection_in_rollouts.yaml b/rollouts/config/crd/patches/cainjection_in_rollouts.yaml new file mode 100644 index 0000000000..e9f9334543 --- /dev/null +++ b/rollouts/config/crd/patches/cainjection_in_rollouts.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: rollouts.gitops.kpt.dev diff --git a/rollouts/config/crd/patches/webhook_in_rollouts.yaml b/rollouts/config/crd/patches/webhook_in_rollouts.yaml new file mode 100644 index 0000000000..b62de0cd34 --- /dev/null +++ b/rollouts/config/crd/patches/webhook_in_rollouts.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: rollouts.gitops.kpt.dev +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/rollouts/config/default/kustomization.yaml b/rollouts/config/default/kustomization.yaml new file mode 100644 index 0000000000..6ca96fdb3c --- /dev/null +++ b/rollouts/config/default/kustomization.yaml @@ -0,0 +1,72 @@ +# Adds namespace to all resources. +namespace: rollouts-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: rollouts- + +# Labels to add to all resources and selectors. +#commonLabels: +# someName: someValue + +bases: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: +# Protect the /metrics endpoint by putting it behind auth. +# If you want your controller-manager to expose the /metrics +# endpoint w/o any authn/z, please comment the following line. +- manager_auth_proxy_patch.yaml + + + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# the following config is for teaching kustomize how to do var substitution +vars: +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldref: +# fieldpath: metadata.namespace +#- name: CERTIFICATE_NAME +# objref: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +#- name: SERVICE_NAMESPACE # namespace of the service +# objref: +# kind: Service +# version: v1 +# name: webhook-service +# fieldref: +# fieldpath: metadata.namespace +#- name: SERVICE_NAME +# objref: +# kind: Service +# version: v1 +# name: webhook-service diff --git a/rollouts/config/default/manager_auth_proxy_patch.yaml b/rollouts/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 0000000000..b751266167 --- /dev/null +++ b/rollouts/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,55 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - arm64 + - ppc64le + - s390x + - key: kubernetes.io/os + operator: In + values: + - linux + containers: + - name: kube-rbac-proxy + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=0" + ports: + - containerPort: 8443 + protocol: TCP + name: https + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + - name: manager + args: + - "--health-probe-bind-address=:8081" + - "--metrics-bind-address=127.0.0.1:8080" + - "--leader-elect" diff --git a/rollouts/config/default/manager_config_patch.yaml b/rollouts/config/default/manager_config_patch.yaml new file mode 100644 index 0000000000..f6f5891692 --- /dev/null +++ b/rollouts/config/default/manager_config_patch.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager diff --git a/rollouts/config/manager/kustomization.yaml b/rollouts/config/manager/kustomization.yaml new file mode 100644 index 0000000000..5c5f0b84cb --- /dev/null +++ b/rollouts/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/rollouts/config/manager/manager.yaml b/rollouts/config/manager/manager.yaml new file mode 100644 index 0000000000..2554cda432 --- /dev/null +++ b/rollouts/config/manager/manager.yaml @@ -0,0 +1,102 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: namespace + app.kubernetes.io/instance: system + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager + app.kubernetes.io/name: deployment + app.kubernetes.io/instance: controller-manager + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: controller-manager + spec: + # TODO(user): Uncomment the following code to configure the nodeAffinity expression + # according to the platforms which are supported by your solution. + # It is considered best practice to support multiple architectures. You can + # build your manager image using the makefile target docker-buildx. + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + # - arm64 + # - ppc64le + # - s390x + # - key: kubernetes.io/os + # operator: In + # values: + # - linux + securityContext: + runAsNonRoot: true + # TODO(user): For common cases that do not require escalating privileges + # it is recommended to ensure that all your Pods/Containers are restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + # Please uncomment the following code if your project does NOT have to work on old Kubernetes + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). + # seccompProfile: + # type: RuntimeDefault + containers: + - command: + - /manager + args: + - --leader-elect + image: controller:latest + name: manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + serviceAccountName: controller-manager + terminationGracePeriodSeconds: 10 diff --git a/rollouts/config/prometheus/kustomization.yaml b/rollouts/config/prometheus/kustomization.yaml new file mode 100644 index 0000000000..ed137168a1 --- /dev/null +++ b/rollouts/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/rollouts/config/prometheus/monitor.yaml b/rollouts/config/prometheus/monitor.yaml new file mode 100644 index 0000000000..abce79f2d3 --- /dev/null +++ b/rollouts/config/prometheus/monitor.yaml @@ -0,0 +1,26 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: servicemonitor + app.kubernetes.io/instance: controller-manager-metrics-monitor + app.kubernetes.io/component: metrics + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + insecureSkipVerify: true + selector: + matchLabels: + control-plane: controller-manager diff --git a/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml b/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 0000000000..268fa507a0 --- /dev/null +++ b/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: metrics-reader + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/rollouts/config/rbac/auth_proxy_role.yaml b/rollouts/config/rbac/auth_proxy_role.yaml new file mode 100644 index 0000000000..8cf6919b05 --- /dev/null +++ b/rollouts/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,24 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: proxy-role + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/rollouts/config/rbac/auth_proxy_role_binding.yaml b/rollouts/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 0000000000..b46b2453e7 --- /dev/null +++ b/rollouts/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/instance: proxy-rolebinding + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/rollouts/config/rbac/auth_proxy_service.yaml b/rollouts/config/rbac/auth_proxy_service.yaml new file mode 100644 index 0000000000..9d18943e71 --- /dev/null +++ b/rollouts/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: service + app.kubernetes.io/instance: controller-manager-metrics-service + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + selector: + control-plane: controller-manager diff --git a/rollouts/config/rbac/kustomization.yaml b/rollouts/config/rbac/kustomization.yaml new file mode 100644 index 0000000000..731832a6ac --- /dev/null +++ b/rollouts/config/rbac/kustomization.yaml @@ -0,0 +1,18 @@ +resources: +# All RBAC will be applied under this service account in +# the deployment namespace. You may comment out this resource +# if your manager will use a service account that exists at +# runtime. Be sure to update RoleBinding and ClusterRoleBinding +# subjects if changing service account names. +- service_account.yaml +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml diff --git a/rollouts/config/rbac/leader_election_role.yaml b/rollouts/config/rbac/leader_election_role.yaml new file mode 100644 index 0000000000..d168e0ef45 --- /dev/null +++ b/rollouts/config/rbac/leader_election_role.yaml @@ -0,0 +1,44 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: role + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/rollouts/config/rbac/leader_election_role_binding.yaml b/rollouts/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 0000000000..0cfb90c2f7 --- /dev/null +++ b/rollouts/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: rolebinding + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/rollouts/config/rbac/role.yaml b/rollouts/config/rbac/role.yaml new file mode 100644 index 0000000000..0dce5eb9f6 --- /dev/null +++ b/rollouts/config/rbac/role.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts/finalizers + verbs: + - update +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts/status + verbs: + - get + - patch + - update diff --git a/rollouts/config/rbac/role_binding.yaml b/rollouts/config/rbac/role_binding.yaml new file mode 100644 index 0000000000..d960daecbb --- /dev/null +++ b/rollouts/config/rbac/role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/rollouts/config/rbac/rollout_editor_role.yaml b/rollouts/config/rbac/rollout_editor_role.yaml new file mode 100644 index 0000000000..3506f1e2f0 --- /dev/null +++ b/rollouts/config/rbac/rollout_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit rollouts. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: rollout-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: rollout-editor-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts/status + verbs: + - get diff --git a/rollouts/config/rbac/rollout_viewer_role.yaml b/rollouts/config/rbac/rollout_viewer_role.yaml new file mode 100644 index 0000000000..a65f279566 --- /dev/null +++ b/rollouts/config/rbac/rollout_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view rollouts. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: rollout-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: rollout-viewer-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts + verbs: + - get + - list + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - rollouts/status + verbs: + - get diff --git a/rollouts/config/rbac/service_account.yaml b/rollouts/config/rbac/service_account.yaml new file mode 100644 index 0000000000..ebade20fa3 --- /dev/null +++ b/rollouts/config/rbac/service_account.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/instance: controller-manager + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: controller-manager + namespace: system diff --git a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml new file mode 100644 index 0000000000..385ea7ecc7 --- /dev/null +++ b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml @@ -0,0 +1,12 @@ +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + labels: + app.kubernetes.io/name: rollout + app.kubernetes.io/instance: rollout-sample + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: rollouts + name: rollout-sample +spec: + # TODO(user): Add fields here diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go new file mode 100644 index 0000000000..3c58743741 --- /dev/null +++ b/rollouts/controllers/rollout_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2022. + +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 controllers + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/api/v1alpha1" +) + +// RolloutReconciler reconciles a Rollout object +type RolloutReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=rollouts,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=rollouts/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=rollouts/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Rollout object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile +func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&gitopsv1alpha1.Rollout{}). + Complete(r) +} diff --git a/rollouts/controllers/suite_test.go b/rollouts/controllers/suite_test.go new file mode 100644 index 0000000000..2c432cb9be --- /dev/null +++ b/rollouts/controllers/suite_test.go @@ -0,0 +1,80 @@ +/* +Copyright 2022. + +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 controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/api/v1alpha1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = gitopsv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/rollouts/go.mod b/rollouts/go.mod new file mode 100644 index 0000000000..f855b760e7 --- /dev/null +++ b/rollouts/go.mod @@ -0,0 +1,81 @@ +module github.com/GoogleContainerTools/kpt + +go 1.19 + +require ( + github.com/onsi/ginkgo/v2 v2.1.4 + github.com/onsi/gomega v1.19.0 + k8s.io/apimachinery v0.25.0 + k8s.io/client-go v0.25.0 + sigs.k8s.io/controller-runtime v0.13.1 +) + +require ( + cloud.google.com/go v0.97.0 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/zapr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + google.golang.org/appengine v1.6.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.1 // indirect + k8s.io/api v0.25.0 // indirect + k8s.io/apiextensions-apiserver v0.25.0 // indirect + k8s.io/component-base v0.25.0 // indirect + k8s.io/klog/v2 v2.70.1 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/rollouts/go.sum b/rollouts/go.sum new file mode 100644 index 0000000000..3a4aa4557d --- /dev/null +++ b/rollouts/go.sum @@ -0,0 +1,796 @@ +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 v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +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/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/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/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/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +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/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/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-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +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.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +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/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +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/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +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-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/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 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +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.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +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/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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/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.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.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-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/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +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-20181114220301-adae6a3d119a/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20200106162015-b016eb3dc98e/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-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/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-20210124154548-22da62e12c0c/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-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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +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/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858/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-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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/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= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +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.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +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 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +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-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/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.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +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.5/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-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.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= +k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= +k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= +k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= +k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= +k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= +k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +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-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/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/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= +sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/rollouts/hack/boilerplate.go.txt b/rollouts/hack/boilerplate.go.txt new file mode 100644 index 0000000000..29c55ecda3 --- /dev/null +++ b/rollouts/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright 2022. + +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. +*/ \ No newline at end of file diff --git a/rollouts/main.go b/rollouts/main.go new file mode 100644 index 0000000000..09ad257a6b --- /dev/null +++ b/rollouts/main.go @@ -0,0 +1,115 @@ +/* +Copyright 2022. + +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 ( + "flag" + "os" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/controllers" + //+kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(gitopsv1alpha1.AddToScheme(scheme)) + //+kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: metricsAddr, + Port: 9443, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "23e227a5.kpt.dev", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + if err = (&controllers.RolloutReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Rollout") + os.Exit(1) + } + //+kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} From 3d05ffd606bf49d9cae2d77e4f2443d6472ce849 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Fri, 16 Dec 2022 17:36:35 -0800 Subject: [PATCH 03/38] rollouts: added cluster discovery and selection (#3696) --- .gitignore | 2 + rollouts/Dockerfile | 14 + rollouts/Makefile | 19 ++ rollouts/api/v1alpha1/rollout_types.go | 18 +- .../api/v1alpha1/zz_generated.deepcopy.go | 24 +- .../crd/bases/gitops.kpt.dev_rollouts.yaml | 64 ++++- rollouts/config/crd/kustomization.yaml | 14 + rollouts/config/crd/kustomizeconfig.yaml | 14 + .../crd/patches/cainjection_in_rollouts.yaml | 14 + .../crd/patches/webhook_in_rollouts.yaml | 14 + rollouts/config/default/kustomization.yaml | 14 + .../default/manager_auth_proxy_patch.yaml | 14 + .../config/default/manager_config_patch.yaml | 14 + rollouts/config/manager/kustomization.yaml | 20 ++ rollouts/config/manager/manager.yaml | 14 + rollouts/config/prometheus/kustomization.yaml | 14 + rollouts/config/prometheus/monitor.yaml | 14 + .../rbac/auth_proxy_client_clusterrole.yaml | 14 + rollouts/config/rbac/auth_proxy_role.yaml | 14 + .../config/rbac/auth_proxy_role_binding.yaml | 14 + rollouts/config/rbac/auth_proxy_service.yaml | 14 + rollouts/config/rbac/kustomization.yaml | 14 + .../config/rbac/leader_election_role.yaml | 14 + .../rbac/leader_election_role_binding.yaml | 14 + rollouts/config/rbac/role_binding.yaml | 14 + rollouts/config/rbac/rollout_editor_role.yaml | 14 + rollouts/config/rbac/rollout_viewer_role.yaml | 14 + rollouts/config/rbac/service_account.yaml | 14 + .../samples/gitops_v1alpha1_rollout.yaml | 21 +- rollouts/controllers/rollout_controller.go | 90 ++++++- rollouts/controllers/suite_test.go | 2 +- rollouts/go.mod | 57 ++-- rollouts/go.sum | 246 ++++++------------ rollouts/main.go | 4 +- rollouts/pkg/clusterstore/clusterstore.go | 150 +++++++++++ rollouts/pkg/clusterstore/tokens.go | 203 +++++++++++++++ .../gcptokensource/gcptokensource.go | 81 ++++++ .../ksaimpersonation.go | 87 +++++++ .../ksatokensource/ksatokensource.go | 130 +++++++++ .../tokenexchange/membership/membership.go | 67 +++++ 40 files changed, 1382 insertions(+), 211 deletions(-) create mode 100644 rollouts/pkg/clusterstore/clusterstore.go create mode 100644 rollouts/pkg/clusterstore/tokens.go create mode 100644 rollouts/pkg/tokenexchange/gcptokensource/gcptokensource.go create mode 100644 rollouts/pkg/tokenexchange/ksaimpersonationtokensource/ksaimpersonation.go create mode 100644 rollouts/pkg/tokenexchange/ksatokensource/ksatokensource.go create mode 100644 rollouts/pkg/tokenexchange/membership/membership.go diff --git a/.gitignore b/.gitignore index 531215ec08..1fb99f772e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +go.work +go.work.sum .DS_Store .idea/ *.iml diff --git a/rollouts/Dockerfile b/rollouts/Dockerfile index 8f9cca18eb..2b3d768732 100644 --- a/rollouts/Dockerfile +++ b/rollouts/Dockerfile @@ -1,3 +1,17 @@ +# 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. + # Build the manager binary FROM golang:1.19 as builder ARG TARGETOS diff --git a/rollouts/Makefile b/rollouts/Makefile index 9116f85220..2cc041386e 100644 --- a/rollouts/Makefile +++ b/rollouts/Makefile @@ -1,3 +1,17 @@ +# 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. + # Image URL to use all building/pushing image targets IMG ?= controller:latest @@ -115,6 +129,11 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/default | kubectl apply -f - +.PHONY: dry-run +dry-run: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > bin/all.yaml + .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 78ab336d38..29ffccc7e8 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -25,13 +25,25 @@ import ( // RolloutSpec defines the desired state of Rollout type RolloutSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - // Test is an dummy test field of Rollout. - Test string `json:"test,omitempty"` + // Description is a user friendly description of this Rollout. + Description string `json:"description,omitempty"` + + // Targets specifies the clusters that will receive the KRM config packages. + Targets ClusterTargetSelector `json:"targets,omitempty"` + + // PackageSourceType identifies how the KRM config unit will be sourced. + PackageSourceType PackageSourceType `json:"packageSourceType,omitempty"` } +type ClusterTargetSelector struct { + Selector *metav1.LabelSelector `json:"selector,omitempty"` +} + +// +kubebuilder:validation:Enum=gitRepo;gitDir +type PackageSourceType string + // RolloutStatus defines the observed state of Rollout type RolloutStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index f1669ec124..a5a6ef7bc2 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -22,15 +22,36 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTargetSelector) DeepCopyInto(out *ClusterTargetSelector) { + *out = *in + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTargetSelector. +func (in *ClusterTargetSelector) DeepCopy() *ClusterTargetSelector { + if in == nil { + return nil + } + out := new(ClusterTargetSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Rollout) DeepCopyInto(out *Rollout) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status } @@ -87,6 +108,7 @@ func (in *RolloutList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { *out = *in + in.Targets.DeepCopyInto(&out.Targets) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutSpec. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 195482c4fd..22cad1f72d 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -35,9 +35,69 @@ spec: spec: description: RolloutSpec defines the desired state of Rollout properties: - test: - description: Test is an dummy test field of Rollout. + description: + description: Description is a user friendly description of this Rollout. type: string + packageSourceType: + description: PackageSourceType identifies how the KRM config unit + will be sourced. + enum: + - gitRepo + - gitDir + type: string + targets: + description: Targets specifies the clusters that will receive the + KRM config packages. + properties: + selector: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An + empty label selector matches all objects. A null label selector + matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If + the operator is In or NotIn, the values array must + be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A + single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is "key", + the operator is "In", and the values array contains only + "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object type: object status: description: RolloutStatus defines the observed state of Rollout diff --git a/rollouts/config/crd/kustomization.yaml b/rollouts/config/crd/kustomization.yaml index 373deb77e3..bd414eeb42 100644 --- a/rollouts/config/crd/kustomization.yaml +++ b/rollouts/config/crd/kustomization.yaml @@ -1,3 +1,17 @@ +# 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. + # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default diff --git a/rollouts/config/crd/kustomizeconfig.yaml b/rollouts/config/crd/kustomizeconfig.yaml index ec5c150a9d..6eff1bf1c4 100644 --- a/rollouts/config/crd/kustomizeconfig.yaml +++ b/rollouts/config/crd/kustomizeconfig.yaml @@ -1,3 +1,17 @@ +# 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. + # This file is for teaching kustomize how to substitute name and namespace reference in CRD nameReference: - kind: Service diff --git a/rollouts/config/crd/patches/cainjection_in_rollouts.yaml b/rollouts/config/crd/patches/cainjection_in_rollouts.yaml index e9f9334543..45064190eb 100644 --- a/rollouts/config/crd/patches/cainjection_in_rollouts.yaml +++ b/rollouts/config/crd/patches/cainjection_in_rollouts.yaml @@ -1,3 +1,17 @@ +# 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. + # The following patch adds a directive for certmanager to inject CA into the CRD apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/rollouts/config/crd/patches/webhook_in_rollouts.yaml b/rollouts/config/crd/patches/webhook_in_rollouts.yaml index b62de0cd34..8df995f5b1 100644 --- a/rollouts/config/crd/patches/webhook_in_rollouts.yaml +++ b/rollouts/config/crd/patches/webhook_in_rollouts.yaml @@ -1,3 +1,17 @@ +# 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. + # The following patch enables a conversion webhook for the CRD apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/rollouts/config/default/kustomization.yaml b/rollouts/config/default/kustomization.yaml index 6ca96fdb3c..8e3174823f 100644 --- a/rollouts/config/default/kustomization.yaml +++ b/rollouts/config/default/kustomization.yaml @@ -1,3 +1,17 @@ +# 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. + # Adds namespace to all resources. namespace: rollouts-system diff --git a/rollouts/config/default/manager_auth_proxy_patch.yaml b/rollouts/config/default/manager_auth_proxy_patch.yaml index b751266167..6cf4a30a8f 100644 --- a/rollouts/config/default/manager_auth_proxy_patch.yaml +++ b/rollouts/config/default/manager_auth_proxy_patch.yaml @@ -1,3 +1,17 @@ +# 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. + # This patch inject a sidecar container which is a HTTP proxy for the # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. apiVersion: apps/v1 diff --git a/rollouts/config/default/manager_config_patch.yaml b/rollouts/config/default/manager_config_patch.yaml index f6f5891692..f07552ed3e 100644 --- a/rollouts/config/default/manager_config_patch.yaml +++ b/rollouts/config/default/manager_config_patch.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/rollouts/config/manager/kustomization.yaml b/rollouts/config/manager/kustomization.yaml index 5c5f0b84cb..4e0ae586c9 100644 --- a/rollouts/config/manager/kustomization.yaml +++ b/rollouts/config/manager/kustomization.yaml @@ -1,2 +1,22 @@ +# 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. + resources: - manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: controller + newTag: latest diff --git a/rollouts/config/manager/manager.yaml b/rollouts/config/manager/manager.yaml index 2554cda432..38945b15c8 100644 --- a/rollouts/config/manager/manager.yaml +++ b/rollouts/config/manager/manager.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: v1 kind: Namespace metadata: diff --git a/rollouts/config/prometheus/kustomization.yaml b/rollouts/config/prometheus/kustomization.yaml index ed137168a1..26122b33eb 100644 --- a/rollouts/config/prometheus/kustomization.yaml +++ b/rollouts/config/prometheus/kustomization.yaml @@ -1,2 +1,16 @@ +# 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. + resources: - monitor.yaml diff --git a/rollouts/config/prometheus/monitor.yaml b/rollouts/config/prometheus/monitor.yaml index abce79f2d3..1bbac5de89 100644 --- a/rollouts/config/prometheus/monitor.yaml +++ b/rollouts/config/prometheus/monitor.yaml @@ -1,3 +1,17 @@ +# 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. + # Prometheus Monitor Service (Metrics) apiVersion: monitoring.coreos.com/v1 diff --git a/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml b/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml index 268fa507a0..3892367780 100644 --- a/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml +++ b/rollouts/config/rbac/auth_proxy_client_clusterrole.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/rollouts/config/rbac/auth_proxy_role.yaml b/rollouts/config/rbac/auth_proxy_role.yaml index 8cf6919b05..cc9cd5d61b 100644 --- a/rollouts/config/rbac/auth_proxy_role.yaml +++ b/rollouts/config/rbac/auth_proxy_role.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/rollouts/config/rbac/auth_proxy_role_binding.yaml b/rollouts/config/rbac/auth_proxy_role_binding.yaml index b46b2453e7..862d6bd10a 100644 --- a/rollouts/config/rbac/auth_proxy_role_binding.yaml +++ b/rollouts/config/rbac/auth_proxy_role_binding.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: diff --git a/rollouts/config/rbac/auth_proxy_service.yaml b/rollouts/config/rbac/auth_proxy_service.yaml index 9d18943e71..7b368eb291 100644 --- a/rollouts/config/rbac/auth_proxy_service.yaml +++ b/rollouts/config/rbac/auth_proxy_service.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: v1 kind: Service metadata: diff --git a/rollouts/config/rbac/kustomization.yaml b/rollouts/config/rbac/kustomization.yaml index 731832a6ac..5171454a23 100644 --- a/rollouts/config/rbac/kustomization.yaml +++ b/rollouts/config/rbac/kustomization.yaml @@ -1,3 +1,17 @@ +# 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. + resources: # All RBAC will be applied under this service account in # the deployment namespace. You may comment out this resource diff --git a/rollouts/config/rbac/leader_election_role.yaml b/rollouts/config/rbac/leader_election_role.yaml index d168e0ef45..1a008d5c09 100644 --- a/rollouts/config/rbac/leader_election_role.yaml +++ b/rollouts/config/rbac/leader_election_role.yaml @@ -1,3 +1,17 @@ +# 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. + # permissions to do leader election. apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/rollouts/config/rbac/leader_election_role_binding.yaml b/rollouts/config/rbac/leader_election_role_binding.yaml index 0cfb90c2f7..8413ac6da1 100644 --- a/rollouts/config/rbac/leader_election_role_binding.yaml +++ b/rollouts/config/rbac/leader_election_role_binding.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: diff --git a/rollouts/config/rbac/role_binding.yaml b/rollouts/config/rbac/role_binding.yaml index d960daecbb..afc6db2b62 100644 --- a/rollouts/config/rbac/role_binding.yaml +++ b/rollouts/config/rbac/role_binding.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: diff --git a/rollouts/config/rbac/rollout_editor_role.yaml b/rollouts/config/rbac/rollout_editor_role.yaml index 3506f1e2f0..1a6da05f72 100644 --- a/rollouts/config/rbac/rollout_editor_role.yaml +++ b/rollouts/config/rbac/rollout_editor_role.yaml @@ -1,3 +1,17 @@ +# 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. + # permissions for end users to edit rollouts. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/rollouts/config/rbac/rollout_viewer_role.yaml b/rollouts/config/rbac/rollout_viewer_role.yaml index a65f279566..fdba6c7290 100644 --- a/rollouts/config/rbac/rollout_viewer_role.yaml +++ b/rollouts/config/rbac/rollout_viewer_role.yaml @@ -1,3 +1,17 @@ +# 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. + # permissions for end users to view rollouts. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/rollouts/config/rbac/service_account.yaml b/rollouts/config/rbac/service_account.yaml index ebade20fa3..316b3d03ce 100644 --- a/rollouts/config/rbac/service_account.yaml +++ b/rollouts/config/rbac/service_account.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: v1 kind: ServiceAccount metadata: diff --git a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml index 385ea7ecc7..9363d09268 100644 --- a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml +++ b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml @@ -1,3 +1,17 @@ +# 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. + apiVersion: gitops.kpt.dev/v1alpha1 kind: Rollout metadata: @@ -9,4 +23,9 @@ metadata: app.kubernetes.io/created-by: rollouts name: rollout-sample spec: - # TODO(user): Add fields here + packageSourceType: gitRepo + description: "this is first rollout :)" + targets: + selector: + matchExpressions: + - {key: location/island, operator: In, values: [oahu, maui]} diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 3c58743741..a8a150bdfc 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -18,18 +18,54 @@ package controllers import ( "context" + "flag" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/api/v1alpha1" + gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/clusterstore" ) +var ( + rootSyncNamespace = "config-management-system" + rootSyncGVK = schema.GroupVersionKind{ + Group: "configsync.gke.io", + Version: "v1beta1", + Kind: "RootSync", + } + rootSyncGVR = schema.GroupVersionResource{ + Group: "configsync.gke.io", + Version: "v1beta1", + Resource: "rootsyncs", + } + containerClusterKind = "ContainerCluster" + containerClusterApiVersion = "container.cnrm.cloud.google.com/v1beta1" + + configControllerKind = "ConfigControllerInstance" + configControllerApiVersion = "configcontroller.cnrm.cloud.google.com/v1beta1" +) + +type Options struct { +} + +func (o *Options) InitDefaults() { +} + +func (o *Options) BindFlags(prefix string, flags *flag.FlagSet) { +} + // RolloutReconciler reconciles a Rollout object type RolloutReconciler struct { client.Client + + store *clusterstore.ClusterStore + Scheme *runtime.Scheme } @@ -46,16 +82,62 @@ type RolloutReconciler struct { // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile +// +// Dumb reconciliation of Rollout API includes the following: +// Fetch the READY kcc clusters. +// For each kcc cluster, fetch RootSync objects in each of the KCC clusters. func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) + var rollout gitopsv1alpha1.Rollout + + if err := r.Get(ctx, req.NamespacedName, &rollout); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + logger := log.FromContext(ctx) + + logger.Info("reconciling", "key", req.NamespacedName) - // TODO(user): your logic here + gkeClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) + if err != nil { + return ctrl.Result{}, err + } - return ctrl.Result{}, nil + r.store.PrintClusterInfos(ctx, gkeClusters) + + for _, gkeCluster := range gkeClusters.Items { + cl, err := r.store.GetClusterClient(ctx, &gkeCluster) + if err != nil { + return ctrl.Result{}, err + } + r.testClusterClient(ctx, cl) + } + return ctrl.Result{}, err +} + +func (r *RolloutReconciler) testClusterClient(ctx context.Context, cl client.Client) error { + logger := log.FromContext(ctx) + podList := &v1.PodList{} + err := cl.List(context.Background(), podList, client.InNamespace("kube-system")) + if err != nil { + return err + } + logger.Info("found podlist", "number of pods", len(podList.Items)) + return nil } // SetupWithManager sets up the controller with the Manager. func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.Client = mgr.GetClient() + gkeclusterapis.AddToScheme(mgr.GetScheme()) + + // setup the clusterstore + r.store = &clusterstore.ClusterStore{ + Config: mgr.GetConfig(), + Client: r.Client, + } + if err := r.store.Init(); err != nil { + return err + } return ctrl.NewControllerManagedBy(mgr). For(&gitopsv1alpha1.Rollout{}). Complete(r) diff --git a/rollouts/controllers/suite_test.go b/rollouts/controllers/suite_test.go index 2c432cb9be..9ca6384b19 100644 --- a/rollouts/controllers/suite_test.go +++ b/rollouts/controllers/suite_test.go @@ -30,7 +30,7 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/api/v1alpha1" + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" //+kubebuilder:scaffold:imports ) diff --git a/rollouts/go.mod b/rollouts/go.mod index f855b760e7..fb3910db01 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -1,25 +1,32 @@ -module github.com/GoogleContainerTools/kpt +module github.com/GoogleContainerTools/kpt/rollouts go 1.19 require ( - github.com/onsi/ginkgo/v2 v2.1.4 - github.com/onsi/gomega v1.19.0 - k8s.io/apimachinery v0.25.0 + cloud.google.com/go/iam v0.7.0 + github.com/GoogleCloudPlatform/k8s-config-connector v1.98.0 + github.com/golang/protobuf v1.5.2 + github.com/onsi/ginkgo/v2 v2.1.6 + github.com/onsi/gomega v1.20.1 + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 + google.golang.org/api v0.103.0 + google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c + k8s.io/api v0.25.0 + k8s.io/apimachinery v0.25.4 k8s.io/client-go v0.25.0 + k8s.io/klog/v2 v2.80.1 sigs.k8s.io/controller-runtime v0.13.1 ) require ( - cloud.google.com/go v0.97.0 // indirect + cloud.google.com/go/compute v1.12.1 // indirect + cloud.google.com/go/compute/metadata v0.2.1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -29,20 +36,21 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // 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-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.8 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.2 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -53,28 +61,27 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/spf13/pflag v1.0.5 // indirect + go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/grpc v1.50.1 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.25.0 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect - k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index 3a4aa4557d..b39e69ffe5 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -13,27 +13,22 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV 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 v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= 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/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= 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/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= 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= @@ -62,11 +57,9 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 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/GoogleCloudPlatform/k8s-config-connector v1.98.0 h1:miM3EQkFWEN19vz15jKaWFDgGo945dfSQd6Zt9ItnYI= +github.com/GoogleCloudPlatform/k8s-config-connector v1.98.0/go.mod h1:eBXtpWdABpt8RDX851XtCjjxpAWPksldqnFcE23P4cA= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -79,6 +72,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -89,7 +83,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -102,15 +95,14 @@ github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry 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/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -123,7 +115,6 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= @@ -133,11 +124,11 @@ github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa 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.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -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 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -158,8 +149,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt 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/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -175,14 +164,12 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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/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= @@ -190,20 +177,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 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= @@ -211,24 +194,20 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf 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-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -257,8 +236,9 @@ 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 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= 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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -277,10 +257,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -315,19 +295,26 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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/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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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/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= @@ -338,8 +325,8 @@ 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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -359,8 +346,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U 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/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -383,8 +370,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl 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= @@ -393,8 +378,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB 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= @@ -410,7 +393,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL 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= @@ -426,35 +408,22 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R 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-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -497,46 +466,30 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/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-20210124154548-22da62e12c0c/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-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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= @@ -584,22 +537,13 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY 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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/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-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -618,18 +562,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M 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.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= 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= @@ -668,32 +602,9 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc 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-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c h1:S34D59DS2GWOEwWNt4fYmTcFrtlOgukG2k9WsomZ7tg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= 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= @@ -706,20 +617,12 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa 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.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 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= @@ -733,8 +636,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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= +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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -770,19 +673,18 @@ k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= +k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 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= diff --git a/rollouts/main.go b/rollouts/main.go index 09ad257a6b..c87e6b3228 100644 --- a/rollouts/main.go +++ b/rollouts/main.go @@ -31,8 +31,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" - gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/api/v1alpha1" - "github.com/GoogleContainerTools/kpt/controllers" + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/controllers" //+kubebuilder:scaffold:imports ) diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go new file mode 100644 index 0000000000..fa7a3e4238 --- /dev/null +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -0,0 +1,150 @@ +// 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 clusterstore + +import ( + "context" + "encoding/base64" + "fmt" + + "golang.org/x/oauth2" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" +) + +// ClusterStore represents a store of kubernetes cluster. +type ClusterStore struct { + // Config/Client points to the config + // pointing to where the rollout controller is running + Config *rest.Config + client.Client + WorkloadIdentityHelper +} + +func (cs *ClusterStore) Init() error { + if err := cs.WorkloadIdentityHelper.Init(cs.Config); err != nil { + return err + } + return nil +} + +func (cs *ClusterStore) ListClusters(ctx context.Context, selector *metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { + gkeClusters := &gkeclusterapis.ContainerClusterList{} + + var opts []client.ListOption + if selector != nil { + selector, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return nil, err + } + opts = append(opts, client.MatchingLabelsSelector{Selector: selector}) + } + + // TODO: make it configurable ? + opts = append(opts, client.InNamespace("config-control")) + if err := cs.List(ctx, gkeClusters, opts...); err != nil { + return nil, err + } + + return gkeClusters, nil +} + +func (cs *ClusterStore) PrintClusterInfos(ctx context.Context, clusters *gkeclusterapis.ContainerClusterList) { + logger := log.FromContext(ctx) + for _, gkeCluster := range clusters.Items { + logger.Info("gke clusters", "namespace", gkeCluster.Namespace, "name", gkeCluster.Name) + for _, cond := range gkeCluster.Status.Conditions { + logger.Info("gke cluster", "name", gkeCluster.Name, "condition", cond) + } + } +} + +func (cs *ClusterStore) GetClusterClient(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (client.Client, error) { + clusterClientConfig, err := cs.getRESTConfig(ctx, cluster) + if err != nil { + return nil, err + } + cl, err := client.New(clusterClientConfig, client.Options{}) + if err != nil { + return nil, err + } + return cl, nil +} + +func (cs *ClusterStore) getRESTConfig(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (*rest.Config, error) { + logger := log.FromContext(ctx) + restConfig := &rest.Config{} + clusterCaCertificate := cluster.Spec.MasterAuth.ClusterCaCertificate + if clusterCaCertificate == nil || *clusterCaCertificate == "" { + return nil, fmt.Errorf("cluster CA certificate data is missing") + } + caData, err := base64.StdEncoding.DecodeString(*clusterCaCertificate) + if err != nil { + return nil, fmt.Errorf("error decoding ca certificate: %w", err) + } + restConfig.CAData = caData + if cluster.Status.Endpoint == "" { + return nil, fmt.Errorf("cluster master endpoint field is empty") + } + restConfig.Host = "https://" + cluster.Status.Endpoint + logger.Info("Host endpoint is", "endpoint", restConfig.Host) + tokenSource, err := cs.getConfigConnectorContextTokenSource(ctx, cluster.GetNamespace()) + if err != nil { + return nil, err + } + token, err := tokenSource.Token() + if err != nil { + return nil, fmt.Errorf("error getting token: %w", err) + } + restConfig.BearerToken = token.AccessToken + return restConfig, nil +} + +// getConfigConnectorContextTokenSource gets and returns the ConfigConnectorContext for the given namespace. +func (cs *ClusterStore) getConfigConnectorContextTokenSource(ctx context.Context, ns string) (oauth2.TokenSource, error) { + // TODO: migrate to it's own Go type and use client.Client instance for it + gvr := schema.GroupVersionResource{ + Group: "core.cnrm.cloud.google.com", + Version: "v1beta1", + Resource: "configconnectorcontexts", + } + + cr, err := cs.dynamicClient.Resource(gvr).Namespace(ns).Get(ctx, "configconnectorcontext.core.cnrm.cloud.google.com", metav1.GetOptions{}) + if err != nil { + return nil, err + } + + googleServiceAccount, _, err := unstructured.NestedString(cr.Object, "spec", "googleServiceAccount") + if err != nil { + return nil, fmt.Errorf("error reading spec.googleServiceAccount from ConfigConnectorContext in %q: %w", ns, err) + } + + if googleServiceAccount == "" { + return nil, fmt.Errorf("could not find spec.googleServiceAccount from ConfigConnectorContext in %q: %w", ns, err) + } + + kubeServiceAccount := types.NamespacedName{ + Namespace: "cnrm-system", + Name: "cnrm-controller-manager-" + ns, + } + return cs.WorkloadIdentityHelper.GetGcloudAccessTokenSource(ctx, kubeServiceAccount, googleServiceAccount) +} diff --git a/rollouts/pkg/clusterstore/tokens.go b/rollouts/pkg/clusterstore/tokens.go new file mode 100644 index 0000000000..9abf4cd4fa --- /dev/null +++ b/rollouts/pkg/clusterstore/tokens.go @@ -0,0 +1,203 @@ +// 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 clusterstore + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "strings" + "sync" + + "github.com/GoogleContainerTools/kpt/rollouts/pkg/tokenexchange/gcptokensource" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/tokenexchange/ksaimpersonationtokensource" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/tokenexchange/ksatokensource" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/tokenexchange/membership" + "golang.org/x/oauth2" + "google.golang.org/api/option" + "google.golang.org/api/sts/v1" + stsv1 "google.golang.org/api/sts/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" +) + +// WorkloadIdentityHelper is a helper class that does the exchanges needed for workload identity. +type WorkloadIdentityHelper struct { + // stsClient holds a client for querying STS + stsClient *stsv1.Service + + // corev1Client is used for kubernetes impersonation + corev1Client corev1client.CoreV1Interface + + // dynamicClient is used to check for the membership resource + dynamicClient dynamic.Interface + + restConfig *rest.Config + + mutex sync.Mutex + tokenCache map[tokenCacheKey]oauth2.TokenSource +} + +type tokenCacheKey struct { + kubeServiceAccount types.NamespacedName + gcpServiceAccount string +} + +// Init should be called before using a WorkloadIdentityHelper +func (r *WorkloadIdentityHelper) Init(restConfig *rest.Config) error { + r.restConfig = restConfig + + // If we want to debug RBAC/Token Exchange locally... + // restConfigImpersonate := *restConfig + // restConfigImpersonate.Impersonate.UserName = "system:serviceaccount:configcontroller-system:rootsyncset-impersonate" + // restConfig = &restConfigImpersonate + + corev1Client, err := corev1client.NewForConfig(restConfig) + if err != nil { + return fmt.Errorf("error building corev1 client: %w", err) + } + r.corev1Client = corev1Client + + dynamicClient, err := dynamic.NewForConfig(restConfig) + if err != nil { + return fmt.Errorf("error building dynamic client: %w", err) + } + r.dynamicClient = dynamicClient + + // option.WithoutAuthentication because we don't want to use our credentials for the exchange + // STS actually gives an error: googleapi: "Error 400: Request contains an invalid argument., badRequest" + stsClient, err := sts.NewService(context.Background(), option.WithoutAuthentication()) + if err != nil { + return fmt.Errorf("error building sts client: %w", err) + } + r.stsClient = stsClient + + r.tokenCache = make(map[tokenCacheKey]oauth2.TokenSource) + + return nil +} + +// GetGcloudAccessTokenSource does the exchange to get a token for the specified GCP ServiceAccount. +func (r *WorkloadIdentityHelper) GetGcloudAccessTokenSource(ctx context.Context, kubeServiceAccount types.NamespacedName, gcpServiceAccount string) (oauth2.TokenSource, error) { + key := tokenCacheKey{ + kubeServiceAccount: kubeServiceAccount, + gcpServiceAccount: gcpServiceAccount, + } + + r.mutex.Lock() + cachedTokenSource := r.tokenCache[key] + r.mutex.Unlock() + + if cachedTokenSource != nil { + return cachedTokenSource, nil + } + + var workloadIdentityPool string + var identityProvider string + + membershipConfig, err := membership.Get(ctx, r.dynamicClient) + if err != nil { + if apierrors.IsNotFound(err) { + // TODO: Cache this? + workloadIdentityPool, identityProvider, err = r.findWorkloadIdentityPool(ctx, kubeServiceAccount) + if err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("error fetching membership: %w", err) + } + } else { + workloadIdentityPool = membershipConfig.Spec.WorkloadIdentityPool + identityProvider = membershipConfig.Spec.IdentityProvider + } + + impersonated := ksaimpersonationtokensource.New(r.corev1Client, kubeServiceAccount, []string{workloadIdentityPool}) + + ksaToken := ksatokensource.New(r.stsClient, impersonated, workloadIdentityPool, identityProvider) + + var scopes []string + gcpTokenSource := gcptokensource.New(gcpServiceAccount, scopes, ksaToken) + + tokenSource := oauth2.ReuseTokenSource(nil, gcpTokenSource) + + r.mutex.Lock() + r.tokenCache[key] = tokenSource + r.mutex.Unlock() + + return tokenSource, nil +} + +func (r *WorkloadIdentityHelper) findWorkloadIdentityPool(ctx context.Context, kubeServiceAccount types.NamespacedName) (string, string, error) { + accessToken := "" + + // First, see if we have a valid token mounted locally in our pod + { + const tokenFilePath = "/var/run/secrets/kubernetes.io/serviceaccount/token" + + tokenBytes, err := ioutil.ReadFile(tokenFilePath) + if err != nil { + if os.IsNotExist(err) { + klog.V(2).Infof("token file not found at %q", tokenFilePath) + } else { + klog.Warningf("error reading token file from %q: %v", tokenFilePath, err) + } + } else { + klog.Infof("found token at %q", tokenFilePath) + accessToken = string(tokenBytes) + } + } + + // We could also query the kube apiserver at /.well-known/openid-configuration + // kubectl get --raw /.well-known/openid-configuration + // {"issuer":"https://container.googleapis.com/v1/projects/example-project-id/locations/us-central1/clusters/krmapihost-control","jwks_uri":"https://172.16.0.130:443/openid/v1/jwks","response_types_supported":["id_token"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"]} + + if accessToken == "" { + // We get a token for our own service account, so we can extract the issuer + klog.Infof("token not found at well-known path, requesting token from apiserver") + impersonated := ksaimpersonationtokensource.New(r.corev1Client, kubeServiceAccount, nil /* unspecified/default audience */) + + token, err := impersonated.Token() + if err != nil { + return "", "", fmt.Errorf("failed to get kube token for %s: %w", kubeServiceAccount, err) + } else { + accessToken = token.AccessToken + } + } + + issuer, err := ksatokensource.ExtractIssuer(accessToken) + if err != nil { + return "", "", err + } + + if strings.HasPrefix(issuer, "https://container.googleapis.com/") { + path := strings.TrimPrefix(issuer, "https://container.googleapis.com/") + tokens := strings.Split(path, "/") + for i := 0; i+1 < len(tokens); i++ { + if tokens[i] == "projects" { + workloadIdentityPool := tokens[i+1] + ".svc.id.goog" + klog.Infof("inferred workloadIdentityPool as %q", workloadIdentityPool) + return workloadIdentityPool, issuer, nil + } + } + return "", "", fmt.Errorf("could not extract project from issue %q", issuer) + } else { + return "", "", fmt.Errorf("unknown issuer %q", issuer) + } +} diff --git a/rollouts/pkg/tokenexchange/gcptokensource/gcptokensource.go b/rollouts/pkg/tokenexchange/gcptokensource/gcptokensource.go new file mode 100644 index 0000000000..80de9b8cbe --- /dev/null +++ b/rollouts/pkg/tokenexchange/gcptokensource/gcptokensource.go @@ -0,0 +1,81 @@ +// 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 gcptokensource + +import ( + "context" + "fmt" + "time" + + iamv1 "cloud.google.com/go/iam/credentials/apiv1" + "github.com/golang/protobuf/ptypes" + "golang.org/x/oauth2" + "google.golang.org/api/option" + iampb "google.golang.org/genproto/googleapis/iam/credentials/v1" + "k8s.io/klog/v2" +) + +// New returns an oauth2.TokenSource that exchanges tokens from ts for tokens +// that authenticate as GCP Service Accounts. +func New(gcpServiceAccount string, scopes []string, tokenSource oauth2.TokenSource) oauth2.TokenSource { + // The cloud-platform scope is always required for the token exchange. + scopes = append(scopes, "https://www.googleapis.com/auth/cloud-platform") + return &gcpTokenSource{ + gcpServiceAccount: gcpServiceAccount, + scopes: scopes, + tokenSource: tokenSource, + } +} + +// gcpTokenSource produces tokens that authenticate as GCP ServiceAccounts. +type gcpTokenSource struct { + gcpServiceAccount string + scopes []string + tokenSource oauth2.TokenSource +} + +// ensure gcpTokenSource implements oauth2.TokenSource +var _ oauth2.TokenSource = &gcpTokenSource{} + +// Token exchanges the input token for a GCP SA token. +func (ts *gcpTokenSource) Token() (*oauth2.Token, error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // use the provided token source to make the request + c, err := iamv1.NewIamCredentialsClient(ctx, option.WithTokenSource(ts.tokenSource)) + if err != nil { + return nil, fmt.Errorf("failed to construct IAM client: %w", err) + } + resp, err := c.GenerateAccessToken(ctx, + &iampb.GenerateAccessTokenRequest{ + Name: "projects/-/serviceAccounts/" + ts.gcpServiceAccount, + Scope: ts.scopes, + }) + if err != nil { + return nil, fmt.Errorf("token exchange for GCP serviceaccount %q failed: %w", ts.gcpServiceAccount, err) + } + + klog.Infof("got GCP token for %v", ts.gcpServiceAccount) + + expiry, err := ptypes.Timestamp(resp.ExpireTime) + if err != nil { + return nil, fmt.Errorf("failed to parse expire time on returned token: %w", err) + } + return &oauth2.Token{ + AccessToken: resp.AccessToken, + Expiry: expiry, + }, nil +} diff --git a/rollouts/pkg/tokenexchange/ksaimpersonationtokensource/ksaimpersonation.go b/rollouts/pkg/tokenexchange/ksaimpersonationtokensource/ksaimpersonation.go new file mode 100644 index 0000000000..a012d5d099 --- /dev/null +++ b/rollouts/pkg/tokenexchange/ksaimpersonationtokensource/ksaimpersonation.go @@ -0,0 +1,87 @@ +// 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 ksaimpersonationtokensource + +import ( + "context" + "fmt" + "time" + + "golang.org/x/oauth2" + authenticationv1 "k8s.io/api/authentication/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/klog/v2" +) + +// New returns an oauth2.TokenSource that exchanges the KSA token at ksaTokenPath +// for a GCP access token. +func New(corev1Client corev1client.CoreV1Interface, serviceAccount types.NamespacedName, audiences []string) oauth2.TokenSource { + return &ksaImpersonationTokenSource{ + corev1Client: corev1Client, + serviceAccount: serviceAccount, + audiences: audiences, + } +} + +// ksaImpersonationTokenSource implements oauth2.TokenSource for exchanging KSA tokens for +// GCP tokens. It can be wrapped in a ReuseTokenSource to cache tokens until +// expiry. +type ksaImpersonationTokenSource struct { + corev1Client corev1client.CoreV1Interface + + // serviceAccount is the name of the serviceAccount to impersonate + serviceAccount types.NamespacedName + + // audiences is the set of audiences to request + audiences []string +} + +// ksaTokenSource implements oauth2.TokenSource +var _ oauth2.TokenSource = &ksaImpersonationTokenSource{} + +// Token exchanges a KSA token for a GCP access token, returning the GCP token. +func (ts *ksaImpersonationTokenSource) Token() (*oauth2.Token, error) { + tokenRequest := &authenticationv1.TokenRequest{ + Spec: authenticationv1.TokenRequestSpec{ + Audiences: ts.audiences, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + klog.V(2).Infof("getting token for kubernetes serviceaccount %v", ts.serviceAccount) + response, err := ts.corev1Client.ServiceAccounts(ts.serviceAccount.Namespace).CreateToken(ctx, ts.serviceAccount.Name, tokenRequest, metav1.CreateOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to CreateToken for %s: %w", ts.serviceAccount, err) + } + + exchangeTime := time.Now() + + serviceAccountToken := &oauth2.Token{ + AccessToken: response.Status.Token, + TokenType: "Bearer", + } + + if response.Spec.ExpirationSeconds != nil { + serviceAccountToken.Expiry = exchangeTime.Add(time.Duration(*response.Spec.ExpirationSeconds) * time.Second) + } else { + klog.Warningf("service account token did not include expirationSeconds") + serviceAccountToken.Expiry = exchangeTime + } + + return serviceAccountToken, nil +} diff --git a/rollouts/pkg/tokenexchange/ksatokensource/ksatokensource.go b/rollouts/pkg/tokenexchange/ksatokensource/ksatokensource.go new file mode 100644 index 0000000000..c034ddb4d9 --- /dev/null +++ b/rollouts/pkg/tokenexchange/ksatokensource/ksatokensource.go @@ -0,0 +1,130 @@ +// 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 ksatokensource + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "strings" + "time" + + "golang.org/x/oauth2" + stsv1 "google.golang.org/api/sts/v1" +) + +// New returns an oauth2.TokenSource that exchanges the KSA token from ksaToken +// for a GCP access token. +func New(stsService *stsv1.Service, ksaToken oauth2.TokenSource, workloadIdentityPool, identityProvider string) oauth2.TokenSource { + return &ksaTokenSource{ + ksaToken: ksaToken, + workloadIdentityPool: workloadIdentityPool, + identityProvider: identityProvider, + stsService: stsService, + } +} + +// ksaTokenSource implements oauth2.TokenSource for exchanging KSA tokens for +// GCP tokens. It can be wrapped in a ReuseTokenSource to cache tokens until +// expiry. +type ksaTokenSource struct { + // ksaToken is the source of the kubernetes serviceaccount token. + ksaToken oauth2.TokenSource + // workloadIdentityPool is the Workload Identity Pool to use when exchanging the KSA + // token for a GCP token. + workloadIdentityPool string + // identityProvider is the Identity Provider to use when exchanging the KSA + // token for a GCP token. + identityProvider string + + stsService *stsv1.Service +} + +// ksaTokenSource implements oauth2.TokenSource +var _ oauth2.TokenSource = &ksaTokenSource{} + +// Token exchanges a KSA token for a GCP access token, returning the GCP token. +func (ts *ksaTokenSource) Token() (*oauth2.Token, error) { + ksaToken, err := ts.ksaToken.Token() + if err != nil { + return nil, err + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + exchangeTime := time.Now() + workloadIdentityPool := ts.workloadIdentityPool + identityProvider := ts.identityProvider + + audience := fmt.Sprintf("identitynamespace:%s:%s", workloadIdentityPool, identityProvider) + + request := &stsv1.GoogleIdentityStsV1ExchangeTokenRequest{ + GrantType: "urn:ietf:params:oauth:grant-type:token-exchange", + SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt", + SubjectToken: ksaToken.AccessToken, + RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", + Audience: audience, + Scope: "https://www.googleapis.com/auth/iam", + } + + response, err := ts.stsService.V1.Token(request).Context(ctx).Do() + if err != nil { + return nil, fmt.Errorf("failed to get federated token from STS: %w", err) + } + + token := &oauth2.Token{ + AccessToken: response.AccessToken, + TokenType: response.TokenType, + Expiry: exchangeTime.Add(time.Duration(response.ExpiresIn) * time.Second), + } + return token, nil + +} + +// ExtractIsssuer will extract the issuer field from the provided JWT token +func ExtractIssuer(jwtToken string) (string, error) { + return ExtractJWTString(jwtToken, "iss") +} + +// ExtractJWTString extracts the named field from the provided JWT token. +func ExtractJWTString(jwtToken string, key string) (string, error) { + tokens := strings.Split(jwtToken, ".") + if len(tokens) != 3 { + // Don't log the token as it may be sensitive + return "", fmt.Errorf("error getting identity provider from JWT (unexpected number of tokens)") + } + b, err := base64.RawURLEncoding.DecodeString(tokens[1]) + if err != nil { + // Don't log the token as it may be sensitive + return "", fmt.Errorf("error getting identity provider from JWT (cannot decode base64)") + } + m := make(map[string]interface{}) + if err := json.Unmarshal(b, &m); err != nil { + // Don't log the token as it may be sensitive + return "", fmt.Errorf("error getting identity provider from JWT (cannot decode json)") + } + val := m[key] + if val == nil { + // Don't log the token as it may be sensitive + return "", fmt.Errorf("error getting identity provider from JWT (key %q not found)", key) + } + s, ok := val.(string) + if !ok { + // Don't log the token as it may be sensitive + return "", fmt.Errorf("error getting identity provider from JWT (key %q was not string)", key) + } + return s, nil +} diff --git a/rollouts/pkg/tokenexchange/membership/membership.go b/rollouts/pkg/tokenexchange/membership/membership.go new file mode 100644 index 0000000000..7c919b65da --- /dev/null +++ b/rollouts/pkg/tokenexchange/membership/membership.go @@ -0,0 +1,67 @@ +// 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 membership + +import ( + "context" + "encoding/json" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +var ( + gvr = schema.GroupVersionResource{ + Group: "hub.gke.io", + Version: "v1", + Resource: "memberships", + } +) + +// Membership is the object created by hub when a cluster is registered to hub. +type Membership struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec MembershipSpec `json:"spec,omitempty"` +} + +type MembershipSpec struct { + Owner MembershipOwner `json:"owner,omitempty"` + WorkloadIdentityPool string `json:"workload_identity_pool,omitempty"` + IdentityProvider string `json:"identity_provider,omitempty"` +} + +type MembershipOwner struct { + ID string `json:"id,omitempty"` +} + +// Get gets and returns the Membership named "membership" from the cluster. +func Get(ctx context.Context, client dynamic.Interface) (*Membership, error) { + cr, err := client.Resource(gvr).Get(ctx, "membership", metav1.GetOptions{}) + if err != nil { + return nil, err + } + // round-trip through JSON is a convenient way to get at structured content + b, err := cr.MarshalJSON() + if err != nil { + return nil, err + } + membership := &Membership{} + if err := json.Unmarshal(b, membership); err != nil { + return nil, err + } + return membership, nil +} From f03c104adc027304b7d1fd3ac730da40dcf8b447 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 21 Dec 2022 11:42:28 -0800 Subject: [PATCH 04/38] Rollouts package discovery (#3697) --- rollouts/api/v1alpha1/rollout_types.go | 35 +++++- .../api/v1alpha1/zz_generated.deepcopy.go | 64 ++++++++++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 52 ++++++-- .../samples/gitops_v1alpha1_rollout.yaml | 9 +- rollouts/controllers/rollout_controller.go | 20 +++ rollouts/go.mod | 2 + rollouts/go.sum | 5 + .../pkg/packagediscovery/packagediscovery.go | 118 ++++++++++++++++++ .../packagediscovery/packagediscovery_test.go | 44 +++++++ 9 files changed, 337 insertions(+), 12 deletions(-) create mode 100644 rollouts/pkg/packagediscovery/packagediscovery.go create mode 100644 rollouts/pkg/packagediscovery/packagediscovery_test.go diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 29ffccc7e8..9cf9992e74 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -30,20 +30,47 @@ type RolloutSpec struct { // Description is a user friendly description of this Rollout. Description string `json:"description,omitempty"` + // Packages source for this Rollout. + Packages PackagesConfig `json:"packages"` + // Targets specifies the clusters that will receive the KRM config packages. Targets ClusterTargetSelector `json:"targets,omitempty"` - - // PackageSourceType identifies how the KRM config unit will be sourced. - PackageSourceType PackageSourceType `json:"packageSourceType,omitempty"` } type ClusterTargetSelector struct { Selector *metav1.LabelSelector `json:"selector,omitempty"` } -// +kubebuilder:validation:Enum=gitRepo;gitDir +// +kubebuilder:validation:Enum=git type PackageSourceType string +// PackagesConfig defines the packages the Rollout should deploy. +type PackagesConfig struct { + SourceType PackageSourceType `json:"sourceType"` + + Git GitSource `json:"git"` +} + +// GitSource defines the packages source in Git. +type GitSource struct { + GitRepoSelector GitSelector `json:"selector"` +} + +// GitSelector defines the selector to apply to Git. +type GitSelector struct { + Org string `json:"org"` + Name string `json:"name"` + Directory string `json:"directory"` + Revision string `json:"revision"` + SecretRef SecretReference `json:"secretRef,omitempty"` +} + +// SecretReference contains the reference to the secret +type SecretReference struct { + // Name represents the secret name + Name string `json:"name,omitempty"` +} + // RolloutStatus defines the observed state of Rollout type RolloutStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index a5a6ef7bc2..0cfee70ff8 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -46,6 +46,54 @@ func (in *ClusterTargetSelector) DeepCopy() *ClusterTargetSelector { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitSelector) DeepCopyInto(out *GitSelector) { + *out = *in + out.SecretRef = in.SecretRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSelector. +func (in *GitSelector) DeepCopy() *GitSelector { + if in == nil { + return nil + } + out := new(GitSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitSource) DeepCopyInto(out *GitSource) { + *out = *in + out.GitRepoSelector = in.GitRepoSelector +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSource. +func (in *GitSource) DeepCopy() *GitSource { + if in == nil { + return nil + } + out := new(GitSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackagesConfig) DeepCopyInto(out *PackagesConfig) { + *out = *in + out.Git = in.Git +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackagesConfig. +func (in *PackagesConfig) DeepCopy() *PackagesConfig { + if in == nil { + return nil + } + out := new(PackagesConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Rollout) DeepCopyInto(out *Rollout) { *out = *in @@ -108,6 +156,7 @@ func (in *RolloutList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { *out = *in + out.Packages = in.Packages in.Targets.DeepCopyInto(&out.Targets) } @@ -135,3 +184,18 @@ func (in *RolloutStatus) DeepCopy() *RolloutStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretReference) DeepCopyInto(out *SecretReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. +func (in *SecretReference) DeepCopy() *SecretReference { + if in == nil { + return nil + } + out := new(SecretReference) + in.DeepCopyInto(out) + return out +} diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 22cad1f72d..adc71dba3f 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -38,13 +38,49 @@ spec: description: description: Description is a user friendly description of this Rollout. type: string - packageSourceType: - description: PackageSourceType identifies how the KRM config unit - will be sourced. - enum: - - gitRepo - - gitDir - type: string + packages: + description: Packages source for this Rollout. + properties: + git: + description: GitSource defines the packages source in Git. + properties: + selector: + description: GitSelector defines the selector to apply to + Git. + properties: + directory: + type: string + name: + type: string + org: + type: string + revision: + type: string + secretRef: + description: SecretReference contains the reference to + the secret + properties: + name: + description: Name represents the secret name + type: string + type: object + required: + - directory + - name + - org + - revision + type: object + required: + - selector + type: object + sourceType: + enum: + - git + type: string + required: + - git + - sourceType + type: object targets: description: Targets specifies the clusters that will receive the KRM config packages. @@ -98,6 +134,8 @@ spec: type: object x-kubernetes-map-type: atomic type: object + required: + - packages type: object status: description: RolloutStatus defines the observed state of Rollout diff --git a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml index 9363d09268..80c7e68026 100644 --- a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml +++ b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml @@ -23,8 +23,15 @@ metadata: app.kubernetes.io/created-by: rollouts name: rollout-sample spec: - packageSourceType: gitRepo description: "this is first rollout :)" + packages: + sourceType: git + git: + selector: + org: GoogleContainerTools + name: kpt-samples + directory: "*" + revision: main targets: selector: matchExpressions: diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index a8a150bdfc..9f4c7471b4 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -30,6 +30,7 @@ import ( gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" "github.com/GoogleContainerTools/kpt/rollouts/pkg/clusterstore" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery" ) var ( @@ -111,6 +112,25 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } r.testClusterClient(ctx, cl) } + + if err := r.Get(ctx, req.NamespacedName, &rollout); err != nil { + logger.Error(err, "unable to fetch Rollout") + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + packageDiscoveryClient := packagediscovery.NewPackageDiscovery(rollout.Spec.Packages, r.Client, req.Namespace) + + discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx) + if err != nil { + logger.Error(err, "package discovery failed") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages) + return ctrl.Result{}, err } diff --git a/rollouts/go.mod b/rollouts/go.mod index fb3910db01..a66e878146 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -6,6 +6,7 @@ require ( cloud.google.com/go/iam v0.7.0 github.com/GoogleCloudPlatform/k8s-config-connector v1.98.0 github.com/golang/protobuf v1.5.2 + github.com/google/go-github/v48 v48.2.0 github.com/onsi/ginkgo/v2 v2.1.6 github.com/onsi/gomega v1.20.1 golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 @@ -43,6 +44,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index b39e69ffe5..713d2ff8df 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -177,11 +177,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v48 v48.2.0 h1:68puzySE6WqUY9KWmpOsDEQfDZsso98rT6pZcz9HqcE= +github.com/google/go-github/v48 v48.2.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/rollouts/pkg/packagediscovery/packagediscovery.go b/rollouts/pkg/packagediscovery/packagediscovery.go new file mode 100644 index 0000000000..f8ccfaccb8 --- /dev/null +++ b/rollouts/pkg/packagediscovery/packagediscovery.go @@ -0,0 +1,118 @@ +// 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 packagediscovery + +import ( + "context" + "fmt" + "net/http" + "path/filepath" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/google/go-github/v48/github" + "golang.org/x/oauth2" + coreapi "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type PackageDiscovery struct { + config gitopsv1alpha1.PackagesConfig + client client.Client + namespace string +} + +type DiscoveredPackage struct { + Org string + Name string + Directory string + Revision string +} + +func NewPackageDiscovery(config gitopsv1alpha1.PackagesConfig, client client.Client, namespace string) *PackageDiscovery { + return &PackageDiscovery{ + config: config, + client: client, + namespace: namespace, + } +} + +func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage, error) { + gitRepoSelector := d.config.Git.GitRepoSelector + gitClient, err := d.getGitHubClient(ctx) + if err != nil { + return nil, fmt.Errorf("unable to create git client: %w", err) + } + + tree, _, err := gitClient.Git.GetTree(ctx, gitRepoSelector.Org, gitRepoSelector.Name, gitRepoSelector.Revision, true) + if err != nil { + return nil, fmt.Errorf("unable to fetch tree from git: %w", err) + } + + allPaths := []string{} + for _, entry := range tree.Entries { + if *entry.Type == "tree" { + allPaths = append(allPaths, *entry.Path) + } + } + + packagesPaths := filterDirectories(gitRepoSelector.Directory, allPaths) + + discoveredPackages := []DiscoveredPackage{} + + for _, path := range packagesPaths { + thisDiscoveredPackage := DiscoveredPackage{Org: gitRepoSelector.Org, Name: gitRepoSelector.Name, Revision: gitRepoSelector.Revision, Directory: path} + discoveredPackages = append(discoveredPackages, thisDiscoveredPackage) + } + + return discoveredPackages, nil +} + +func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, error) { + gitRepoSelector := d.config.Git.GitRepoSelector + + httpClient := &http.Client{} + + if secretName := gitRepoSelector.SecretRef.Name; secretName != "" { + var repositorySecret coreapi.Secret + key := client.ObjectKey{Namespace: d.namespace, Name: secretName} + if err := d.client.Get(ctx, key, &repositorySecret); err != nil { + return nil, fmt.Errorf("cannot retrieve git credentials %s: %v", key, err) + } + + accessToken := string(repositorySecret.Data["password"]) + + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: accessToken}, + ) + + httpClient = oauth2.NewClient(ctx, ts) + } + + gitClient := github.NewClient(httpClient) + + return gitClient, nil +} + +func filterDirectories(pattern string, directories []string) []string { + filteredDirectories := []string{} + + for _, directory := range directories { + if isMatch, _ := filepath.Match(pattern, directory); isMatch { + filteredDirectories = append(filteredDirectories, directory) + } + } + + return filteredDirectories +} diff --git a/rollouts/pkg/packagediscovery/packagediscovery_test.go b/rollouts/pkg/packagediscovery/packagediscovery_test.go new file mode 100644 index 0000000000..9adae5281a --- /dev/null +++ b/rollouts/pkg/packagediscovery/packagediscovery_test.go @@ -0,0 +1,44 @@ +// 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 packagediscovery + +import ( + "reflect" + "testing" +) + +var FilterDirectories = filterDirectories + +func TestFilterDirectories(t *testing.T) { + tests := []struct { + directories []string + pattern string + want []string + }{ + {directories: []string{"dev", "prod", "dev/package-a", "dev/paackage-b", "prod/package-c"}, pattern: "dev/*", want: []string{"dev/package-a", "dev/package-b"}}, + {directories: []string{"package-a", "package-b", "package-a/dev", "package-a/prod", "package-b/dev"}, pattern: "*/dev", want: []string{"package-a/dev", "package-b/dev"}}, + {directories: []string{"package-a", "package-b", "package-a/dev", "package-a/prod", "package-b/dev"}, pattern: "package-*/prod", want: []string{"package-a/prod"}}, + {directories: []string{"package-a", "package-b", "package-a/dev", "package-a/prod", "package-b/dev"}, pattern: "package-a/dev", want: []string{"package-a/dev"}}, + {directories: []string{"parent", "parent/package-a", "parent/package-b", "parent/package-a/dev", "parent/package-a/prod", "parent/package-b/dev"}, pattern: "parent/*-a/dev", want: []string{"parent/package-a/dev"}}, + {directories: []string{"package-a", "package-b", "package-a/crds", "package-a/crs", "package-b/crds"}, pattern: "package-*", want: []string{"package-a", "package-b"}}, + } + + for _, tc := range tests { + got := FilterDirectories(tc.pattern, tc.directories) + if !reflect.DeepEqual(tc.want, got) { + t.Fatalf("expected: %v, got: %v", tc.want, got) + } + } +} From 10c67e8a0750e9d72441152b238eb81459354831 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Wed, 21 Dec 2022 14:24:56 -0800 Subject: [PATCH 05/38] rollouts: added remoterootsync API (#3698) --- rollouts/PROJECT | 13 +- rollouts/api/v1alpha1/remoterootsync_types.go | 92 +++++ rollouts/api/v1alpha1/rollout_types.go | 6 + .../api/v1alpha1/zz_generated.deepcopy.go | 174 +++++++++ rollouts/config/crd/bases/_.yaml | 14 + .../bases/gitops.kpt.dev_remoterootsyncs.yaml | 174 +++++++++ rollouts/config/crd/kustomization.yaml | 3 + .../cainjection_in_remoterootsyncs.yaml | 7 + .../patches/webhook_in_remoterootsyncs.yaml | 16 + .../rbac/remoterootsync_editor_role.yaml | 31 ++ .../rbac/remoterootsync_viewer_role.yaml | 27 ++ rollouts/config/rbac/role.yaml | 26 ++ .../gitops_v1alpha1_remoterootsync.yaml | 22 ++ .../controllers/remoterootsync_controller.go | 341 ++++++++++++++++++ rollouts/controllers/rollout_controller.go | 22 +- rollouts/controllers/status.go | 149 ++++++++ rollouts/controllers/watcher.go | 146 ++++++++ rollouts/go.mod | 1 + rollouts/go.sum | 3 + rollouts/main.go | 7 + rollouts/pkg/clusterstore/clusterstore.go | 26 +- 21 files changed, 1273 insertions(+), 27 deletions(-) create mode 100644 rollouts/api/v1alpha1/remoterootsync_types.go create mode 100644 rollouts/config/crd/bases/_.yaml create mode 100644 rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml create mode 100644 rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml create mode 100644 rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml create mode 100644 rollouts/config/rbac/remoterootsync_editor_role.yaml create mode 100644 rollouts/config/rbac/remoterootsync_viewer_role.yaml create mode 100644 rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml create mode 100644 rollouts/controllers/remoterootsync_controller.go create mode 100644 rollouts/controllers/status.go create mode 100644 rollouts/controllers/watcher.go diff --git a/rollouts/PROJECT b/rollouts/PROJECT index cc6c81fa2d..8015fa091a 100644 --- a/rollouts/PROJECT +++ b/rollouts/PROJECT @@ -2,7 +2,7 @@ domain: kpt.dev layout: - go.kubebuilder.io/v3 projectName: rollouts -repo: github.com/GoogleContainerTools/kpt +repo: github.com/GoogleContainerTools/kpt/rollouts resources: - api: crdVersion: v1 @@ -11,6 +11,15 @@ resources: domain: kpt.dev group: gitops kind: Rollout - path: github.com/GoogleContainerTools/kpt/api/v1alpha1 + path: github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kpt.dev + group: gitops + kind: RemoteRootSync + path: github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1 version: v1alpha1 version: "3" diff --git a/rollouts/api/v1alpha1/remoterootsync_types.go b/rollouts/api/v1alpha1/remoterootsync_types.go new file mode 100644 index 0000000000..ea65dd9c91 --- /dev/null +++ b/rollouts/api/v1alpha1/remoterootsync_types.go @@ -0,0 +1,92 @@ +/* +Copyright 2022. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// RemoteRootSyncSpec defines the desired state of RemoteRootSync +type RemoteRootSyncSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + ClusterRef ClusterRef `json:"clusterRef,omitempty"` + Template *RootSyncInfo `json:"template,omitempty"` +} + +type RootSyncInfo struct { + Spec *RootSyncSpec `json:"spec,omitempty"` +} + +type RootSyncSpec struct { + SourceFormat string `json:"sourceFormat,omitempty"` + Git *GitInfo `json:"git,omitempty"` +} + +type GitInfo struct { + Repo string `json:"repo"` + Branch string `json:"branch,omitempty"` + Revision string `json:"revision,omitempty"` + Dir string `json:"dir,omitempty"` + Period metav1.Duration `json:"period,omitempty"` + Auth string `json:"auth"` + GCPServiceAccountEmail string `json:"gcpServiceAccountEmail,omitempty"` + Proxy string `json:"proxy,omitempty"` + SecretRef SecretReference `json:"secretRef,omitempty"` + NoSSLVerify bool `json:"noSSLVerify,omitempty"` +} + +// RemoteRootSyncStatus defines the observed state of RemoteRootSync +type RemoteRootSyncStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions describes the reconciliation state of the object. + Conditions []metav1.Condition `json:"conditions,omitempty"` + + SyncStatus string `json:"syncStatus,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// RemoteRootSync is the Schema for the remoterootsyncs API +type RemoteRootSync struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RemoteRootSyncSpec `json:"spec,omitempty"` + Status RemoteRootSyncStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// RemoteRootSyncList contains a list of RemoteRootSync +type RemoteRootSyncList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RemoteRootSync `json:"items"` +} + +func init() { + SchemeBuilder.Register(&RemoteRootSync{}, &RemoteRootSyncList{}) +} diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 9cf9992e74..680d663ae0 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -41,6 +41,12 @@ type ClusterTargetSelector struct { Selector *metav1.LabelSelector `json:"selector,omitempty"` } +// ClusterReference contains the identify information +// need to refer a cluster. +type ClusterRef struct { + Name string `json:"name"` +} + // +kubebuilder:validation:Enum=git type PackageSourceType string diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 0cfee70ff8..5905137ab7 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterRef) DeepCopyInto(out *ClusterRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterRef. +func (in *ClusterRef) DeepCopy() *ClusterRef { + if in == nil { + return nil + } + out := new(ClusterRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterTargetSelector) DeepCopyInto(out *ClusterTargetSelector) { *out = *in @@ -46,6 +61,23 @@ func (in *ClusterTargetSelector) DeepCopy() *ClusterTargetSelector { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitInfo) DeepCopyInto(out *GitInfo) { + *out = *in + out.Period = in.Period + out.SecretRef = in.SecretRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitInfo. +func (in *GitInfo) DeepCopy() *GitInfo { + if in == nil { + return nil + } + out := new(GitInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitSelector) DeepCopyInto(out *GitSelector) { *out = *in @@ -94,6 +126,108 @@ func (in *PackagesConfig) DeepCopy() *PackagesConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteRootSync) DeepCopyInto(out *RemoteRootSync) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteRootSync. +func (in *RemoteRootSync) DeepCopy() *RemoteRootSync { + if in == nil { + return nil + } + out := new(RemoteRootSync) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemoteRootSync) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteRootSyncList) DeepCopyInto(out *RemoteRootSyncList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RemoteRootSync, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteRootSyncList. +func (in *RemoteRootSyncList) DeepCopy() *RemoteRootSyncList { + if in == nil { + return nil + } + out := new(RemoteRootSyncList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemoteRootSyncList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteRootSyncSpec) DeepCopyInto(out *RemoteRootSyncSpec) { + *out = *in + out.ClusterRef = in.ClusterRef + if in.Template != nil { + in, out := &in.Template, &out.Template + *out = new(RootSyncInfo) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteRootSyncSpec. +func (in *RemoteRootSyncSpec) DeepCopy() *RemoteRootSyncSpec { + if in == nil { + return nil + } + out := new(RemoteRootSyncSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteRootSyncStatus) DeepCopyInto(out *RemoteRootSyncStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteRootSyncStatus. +func (in *RemoteRootSyncStatus) DeepCopy() *RemoteRootSyncStatus { + if in == nil { + return nil + } + out := new(RemoteRootSyncStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Rollout) DeepCopyInto(out *Rollout) { *out = *in @@ -185,6 +319,46 @@ func (in *RolloutStatus) DeepCopy() *RolloutStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RootSyncInfo) DeepCopyInto(out *RootSyncInfo) { + *out = *in + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(RootSyncSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootSyncInfo. +func (in *RootSyncInfo) DeepCopy() *RootSyncInfo { + if in == nil { + return nil + } + out := new(RootSyncInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RootSyncSpec) DeepCopyInto(out *RootSyncSpec) { + *out = *in + if in.Git != nil { + in, out := &in.Git, &out.Git + *out = new(GitInfo) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootSyncSpec. +func (in *RootSyncSpec) DeepCopy() *RootSyncSpec { + if in == nil { + return nil + } + out := new(RootSyncSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretReference) DeepCopyInto(out *SecretReference) { *out = *in diff --git a/rollouts/config/crd/bases/_.yaml b/rollouts/config/crd/bases/_.yaml new file mode 100644 index 0000000000..25f6032cf9 --- /dev/null +++ b/rollouts/config/crd/bases/_.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null +spec: + group: "" + names: + kind: "" + plural: "" + scope: "" + versions: null diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml new file mode 100644 index 0000000000..2a8f382fcd --- /dev/null +++ b/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml @@ -0,0 +1,174 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: remoterootsyncs.gitops.kpt.dev +spec: + group: gitops.kpt.dev + names: + kind: RemoteRootSync + listKind: RemoteRootSyncList + plural: remoterootsyncs + singular: remoterootsync + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: RemoteRootSync is the Schema for the remoterootsyncs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RemoteRootSyncSpec defines the desired state of RemoteRootSync + properties: + clusterRef: + description: ClusterReference contains the identify information need + to refer a cluster. + properties: + name: + type: string + required: + - name + type: object + template: + properties: + spec: + properties: + git: + properties: + auth: + type: string + branch: + type: string + dir: + type: string + gcpServiceAccountEmail: + type: string + noSSLVerify: + type: boolean + period: + type: string + proxy: + type: string + repo: + type: string + revision: + type: string + secretRef: + description: SecretReference contains the reference to + the secret + properties: + name: + description: Name represents the secret name + type: string + type: object + required: + - auth + - repo + type: object + sourceFormat: + type: string + type: object + type: object + type: object + status: + description: RemoteRootSyncStatus defines the observed state of RemoteRootSync + properties: + conditions: + description: Conditions describes the reconciliation state of the + object. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + format: int64 + type: integer + syncStatus: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/rollouts/config/crd/kustomization.yaml b/rollouts/config/crd/kustomization.yaml index bd414eeb42..03018b6199 100644 --- a/rollouts/config/crd/kustomization.yaml +++ b/rollouts/config/crd/kustomization.yaml @@ -17,17 +17,20 @@ # It should be run by config/default resources: - bases/gitops.kpt.dev_rollouts.yaml +- bases/gitops.kpt.dev_remoterootsyncs.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_rollouts.yaml +#- patches/webhook_in_remoterootsyncs.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_rollouts.yaml +#- patches/cainjection_in_remoterootsyncs.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml b/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml new file mode 100644 index 0000000000..72be600847 --- /dev/null +++ b/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: remoterootsyncs.gitops.kpt.dev diff --git a/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml b/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml new file mode 100644 index 0000000000..5aa8a7b329 --- /dev/null +++ b/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: remoterootsyncs.gitops.kpt.dev +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/rollouts/config/rbac/remoterootsync_editor_role.yaml b/rollouts/config/rbac/remoterootsync_editor_role.yaml new file mode 100644 index 0000000000..04f3fede88 --- /dev/null +++ b/rollouts/config/rbac/remoterootsync_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit remoterootsyncs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: remoterootsync-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: remoterootsync-editor-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs/status + verbs: + - get diff --git a/rollouts/config/rbac/remoterootsync_viewer_role.yaml b/rollouts/config/rbac/remoterootsync_viewer_role.yaml new file mode 100644 index 0000000000..a18e939194 --- /dev/null +++ b/rollouts/config/rbac/remoterootsync_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view remoterootsyncs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: remoterootsync-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: remoterootsync-viewer-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs + verbs: + - get + - list + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs/status + verbs: + - get diff --git a/rollouts/config/rbac/role.yaml b/rollouts/config/rbac/role.yaml index 0dce5eb9f6..94a5b34df8 100644 --- a/rollouts/config/rbac/role.yaml +++ b/rollouts/config/rbac/role.yaml @@ -5,6 +5,32 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs/finalizers + verbs: + - update +- apiGroups: + - gitops.kpt.dev + resources: + - remoterootsyncs/status + verbs: + - get + - patch + - update - apiGroups: - gitops.kpt.dev resources: diff --git a/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml b/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml new file mode 100644 index 0000000000..1622ae4532 --- /dev/null +++ b/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml @@ -0,0 +1,22 @@ +apiVersion: gitops.kpt.dev/v1alpha1 +kind: RemoteRootSync +metadata: + labels: + app.kubernetes.io/name: remoterootsync + app.kubernetes.io/instance: remoterootsync-sample + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: rollouts + name: remoterootsync-sample +spec: + # TODO(user): Add fields here + clusterRef: + name: dev-1 + template: + spec: + sourceFormat: unstructured + git: + repo: https://github.com/droot/oahu.git + branch: main + revision: main + auth: none diff --git a/rollouts/controllers/remoterootsync_controller.go b/rollouts/controllers/remoterootsync_controller.go new file mode 100644 index 0000000000..f20eb26eb8 --- /dev/null +++ b/rollouts/controllers/remoterootsync_controller.go @@ -0,0 +1,341 @@ +/* +Copyright 2022. + +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 controllers + +import ( + "context" + "encoding/json" + "fmt" + "sync" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "k8s.io/klog" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/clusterstore" +) + +var ( + rootSyncNamespace = "config-management-system" + rootSyncGVK = schema.GroupVersionKind{ + Group: "configsync.gke.io", + Version: "v1beta1", + Kind: "RootSync", + } + rootSyncGVR = schema.GroupVersionResource{ + Group: "configsync.gke.io", + Version: "v1beta1", + Resource: "rootsyncs", + } + + remoteRootSyncNameLabel = "gitops.kpt.dev/remoterootsync-name" + remoteRootSyncNamespaceLabel = "gitops.kpt.dev/remoterootsync-namespace" +) + +// RemoteRootSyncReconciler reconciles a RemoteRootSync object +type RemoteRootSyncReconciler struct { + client.Client + Scheme *runtime.Scheme + + store *clusterstore.ClusterStore + + // channel is where watchers put events to trigger new reconcilations based + // on watch events from target clusters. + channel chan event.GenericEvent + + mutex sync.Mutex + + watchers map[gitopsv1alpha1.ClusterRef]*watcher +} + +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=remoterootsyncs,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=remoterootsyncs/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=remoterootsyncs/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the RemoteRootSync object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile +func (r *RemoteRootSyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + logger.Info("reconciling", "key", req.NamespacedName) + + var remoterootsync gitopsv1alpha1.RemoteRootSync + if err := r.Get(ctx, req.NamespacedName, &remoterootsync); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + myFinalizerName := "remoterootsync.gitops.kpt.dev/finalizer" + if remoterootsync.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, + // then lets add the finalizer and update the object. This is equivalent + // registering our finalizer. + if !controllerutil.ContainsFinalizer(&remoterootsync, myFinalizerName) { + controllerutil.AddFinalizer(&remoterootsync, myFinalizerName) + if err := r.Update(ctx, &remoterootsync); err != nil { + return ctrl.Result{}, fmt.Errorf("error adding finalizer: %w", err) + } + } + } else { + // The object is being deleted + if controllerutil.ContainsFinalizer(&remoterootsync, myFinalizerName) { + // our finalizer is present, so lets handle any external dependency + if err := r.deleteExternalResources(ctx, &remoterootsync); err != nil { + // if fail to delete the external dependency here, return with error + // so that it can be retried + return ctrl.Result{}, fmt.Errorf("have problem to delete external resource: %w", err) + } + // Make sure we stop any watches that are no longer needed. + r.pruneWatches(req.NamespacedName, &remoterootsync.Spec.ClusterRef) + // remove our finalizer from the list and update it. + controllerutil.RemoveFinalizer(&remoterootsync, myFinalizerName) + if err := r.Update(ctx, &remoterootsync); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to update %s after delete finalizer: %w", req.Name, err) + } + } + // Stop reconciliation as the item is being deleted + return ctrl.Result{}, nil + } + + cr := &remoterootsync.Spec.ClusterRef + + gkeCluster, err := r.store.GetCluster(ctx, cr.Name) + if err != nil { + return ctrl.Result{}, err + } + + _, dynCl, err := r.store.GetClusterClient(ctx, gkeCluster) + if err != nil { + return ctrl.Result{}, err + } + r.setupWatches(ctx, dynCl, remoterootsync.Name, remoterootsync.Namespace, remoterootsync.Spec.ClusterRef) + + if err := r.patchRootSync(ctx, dynCl, req.Name, &remoterootsync); err != nil { + return ctrl.Result{}, err + } + + syncStatus, err := checkSyncStatus(ctx, dynCl, req.Name) + if err != nil { + return ctrl.Result{}, err + } + + if err := r.updateStatus(ctx, &remoterootsync, syncStatus); err != nil { + klog.Errorf("failed to update status: %v", err) + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +func (r *RemoteRootSyncReconciler) updateStatus(ctx context.Context, rrs *gitopsv1alpha1.RemoteRootSync, syncStatus string) error { + logger := log.FromContext(ctx) + + // Don't update if there are no changes. + if rrs.Status.SyncStatus == syncStatus && rrs.Generation == rrs.Status.ObservedGeneration { + return nil + } + logger.Info("updating the status") + rrs.Status.SyncStatus = syncStatus + rrs.Status.ObservedGeneration = rrs.Generation + return r.Client.Status().Update(ctx, rrs) +} + +// patchRootSync patches the RootSync in the remote clusters targeted by +// the clusterRefs based on the latest revision of the template in the RemoteRootSync. +func (r *RemoteRootSyncReconciler) patchRootSync(ctx context.Context, client dynamic.Interface, name string, rrs *gitopsv1alpha1.RemoteRootSync) error { + newRootSync, err := BuildObjectsToApply(rrs) + if err != nil { + return err + } + data, err := json.Marshal(newRootSync) + if err != nil { + return fmt.Errorf("failed to encode root sync to JSON: %w", err) + } + _, err = client.Resource(rootSyncGVR).Namespace(rootSyncNamespace).Patch(ctx, name, types.ApplyPatchType, data, metav1.PatchOptions{FieldManager: name}) + if err != nil { + return fmt.Errorf("failed to patch RootSync: %w", err) + } + klog.Infof("Create/Update resource %s as", name) + return nil +} + +// setupWatches makes sure we have the necessary watches running against +// the remote clusters we care about. +func (r *RemoteRootSyncReconciler) setupWatches(ctx context.Context, client dynamic.Interface, rrsName, ns string, clusterRef gitopsv1alpha1.ClusterRef) { + r.mutex.Lock() + defer r.mutex.Unlock() + nn := types.NamespacedName{ + Namespace: ns, + Name: rrsName, + } + + // If we already have a watch running, make sure we have the current RootSyncSet + // listed in the liens map. + if w, found := r.watchers[clusterRef]; found { + w.liens[nn] = struct{}{} + return + } + + // Since we don't currently have a watch running, create a new watcher + // and add it to the map of watchers. + watcherCtx, cancelFunc := context.WithCancel(context.Background()) + w := &watcher{ + clusterRef: clusterRef, + ctx: watcherCtx, + cancelFunc: cancelFunc, + client: client, + channel: r.channel, + liens: map[types.NamespacedName]struct{}{ + nn: {}, + }, + } + klog.Infof("Creating watcher for %v", clusterRef) + go w.watch() + r.watchers[clusterRef] = w +} + +// pruneWatches removes the current RootSyncSet from the liens map of all watchers +// that it no longer needs. If any of the watchers are no longer used by any RootSyncSets, +// they are shut down. +func (r *RemoteRootSyncReconciler) pruneWatches(rrsnn types.NamespacedName, clusterRef *gitopsv1alpha1.ClusterRef) { + r.mutex.Lock() + defer r.mutex.Unlock() + klog.Infof("Pruning watches for %s which has %v clusterRef", rrsnn.String(), clusterRef) + + // Look through all watchers to check if it used to be needed by the RootSyncSet + // but is no longer. + w, found := r.watchers[*clusterRef] + if !found { + return + } + + // Delete the current RootSyncSet from the list of liens (it it exists) + delete(w.liens, rrsnn) + // If no other RootSyncSets need the watch, stop it and remove the watcher from the map. + if len(w.liens) == 0 { + w.cancelFunc() + delete(r.watchers, *clusterRef) + } +} + +// BuildObjectsToApply config root sync +func BuildObjectsToApply(remoterootsync *gitopsv1alpha1.RemoteRootSync) (*unstructured.Unstructured, error) { + newRootSync, err := runtime.DefaultUnstructuredConverter.ToUnstructured(remoterootsync.Spec.Template) + if err != nil { + return nil, err + } + u := unstructured.Unstructured{Object: newRootSync} + u.SetGroupVersionKind(rootSyncGVK) + u.SetName(remoterootsync.Name) + u.SetNamespace(rootSyncNamespace) + u.SetLabels(map[string]string{ + remoteRootSyncNameLabel: remoterootsync.Name, + remoteRootSyncNamespaceLabel: remoterootsync.Namespace, + }) + if err != nil { + return nil, fmt.Errorf("failed to convert to unstructured type: %w", err) + } + return &u, nil +} + +func (r *RemoteRootSyncReconciler) deleteExternalResources(ctx context.Context, remoterootsync *gitopsv1alpha1.RemoteRootSync) error { + + gkeCluster, err := r.store.GetCluster(ctx, remoterootsync.Spec.ClusterRef.Name) + if err != nil { + return err + } + + _, dynCl, err := r.store.GetClusterClient(ctx, gkeCluster) + if err != nil { + return err + } + + klog.Infof("deleting external resource %s ...", remoterootsync.Name) + err = dynCl.Resource(rootSyncGVR).Namespace("config-management-system").Delete(ctx, remoterootsync.Name, metav1.DeleteOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return err + } + klog.Infof("external resource %s delete Done!", remoterootsync.Name) + return err +} + +// SetupWithManager sets up the controller with the Manager. +func (r *RemoteRootSyncReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.channel = make(chan event.GenericEvent, 10) + r.watchers = make(map[gitopsv1alpha1.ClusterRef]*watcher) + r.Client = mgr.GetClient() + gkeclusterapis.AddToScheme(mgr.GetScheme()) + + if err := gitopsv1alpha1.AddToScheme(mgr.GetScheme()); err != nil { + return err + } + // setup the clusterstore + r.store = &clusterstore.ClusterStore{ + Config: mgr.GetConfig(), + Client: r.Client, + } + if err := r.store.Init(); err != nil { + return err + } + return ctrl.NewControllerManagedBy(mgr). + For(&gitopsv1alpha1.RemoteRootSync{}). + Watches( + &source.Channel{Source: r.channel}, + handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { + var rrsName string + var rrsNamespace string + if o.GetLabels() != nil { + rrsName = o.GetLabels()[remoteRootSyncNameLabel] + rrsNamespace = o.GetLabels()[remoteRootSyncNamespaceLabel] + } + if rrsName == "" || rrsNamespace == "" { + return []reconcile.Request{} + } + klog.Infof("Resource %s contains a label for %s", o.GetName(), rrsName) + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{ + Namespace: rrsNamespace, + Name: rrsName, + }, + }, + } + }), + ). + Complete(r) +} diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 9f4c7471b4..0ba4e71e97 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -22,7 +22,6 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -33,25 +32,6 @@ import ( "github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery" ) -var ( - rootSyncNamespace = "config-management-system" - rootSyncGVK = schema.GroupVersionKind{ - Group: "configsync.gke.io", - Version: "v1beta1", - Kind: "RootSync", - } - rootSyncGVR = schema.GroupVersionResource{ - Group: "configsync.gke.io", - Version: "v1beta1", - Resource: "rootsyncs", - } - containerClusterKind = "ContainerCluster" - containerClusterApiVersion = "container.cnrm.cloud.google.com/v1beta1" - - configControllerKind = "ConfigControllerInstance" - configControllerApiVersion = "configcontroller.cnrm.cloud.google.com/v1beta1" -) - type Options struct { } @@ -106,7 +86,7 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct r.store.PrintClusterInfos(ctx, gkeClusters) for _, gkeCluster := range gkeClusters.Items { - cl, err := r.store.GetClusterClient(ctx, &gkeCluster) + cl, _, err := r.store.GetClusterClient(ctx, &gkeCluster) if err != nil { return ctrl.Result{}, err } diff --git a/rollouts/controllers/status.go b/rollouts/controllers/status.go new file mode 100644 index 0000000000..f08d5d6149 --- /dev/null +++ b/rollouts/controllers/status.go @@ -0,0 +1,149 @@ +// 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 controllers + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" +) + +// checkSyncStatus fetches the RootSync using the provided client and computes the sync status. The rules +// for computing status here mirrors the one used in the status command in the nomos cli. +func checkSyncStatus(ctx context.Context, client dynamic.Interface, rrsName string) (string, error) { + // TODO: Change this to use the RootSync type instead of Unstructured. + rs, err := client.Resource(rootSyncGVR).Namespace(rootSyncNamespace).Get(ctx, rrsName, metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("failed to get RootSync: %w", err) + } + + generation, _, err := unstructured.NestedInt64(rs.Object, "metadata", "generation") + if err != nil { + return "", fmt.Errorf("failed to read generation from RootSync: %w", err) + } + + observedGeneration, _, err := unstructured.NestedInt64(rs.Object, "status", "observedGeneration") + if err != nil { + return "", fmt.Errorf("failed to read observedGeneration from RootSync: %w", err) + } + + if generation != observedGeneration { + return "Pending", nil + } + + conditions, _, err := unstructured.NestedSlice(rs.Object, "status", "conditions") + if err != nil { + return "", fmt.Errorf("failed to extract conditions from RootSync: %w", err) + } + + val, found, err := getConditionStatus(conditions, "Stalled") + if err != nil { + return "", fmt.Errorf("error fetching condition 'Stalled' from conditions slice: %w", err) + } + if found && val == "True" { + return "Stalled", nil + } + + val, found, err = getConditionStatus(conditions, "Reconciling") + if err != nil { + return "", fmt.Errorf("error fetching condition 'Reconciling' from conditions slice: %w", err) + } + if found && val == "True" { + return "Reconciling", nil + } + + cond, found, err := getCondition(conditions, "Syncing") + if err != nil { + return "", fmt.Errorf("error fetching condition 'Syncing' from conditions slice: %w", err) + } + if !found { + return "Reconciling", nil + } + + errCount, err := extractErrorCount(cond) + if err != nil { + return "", fmt.Errorf("error extracting error count from 'Syncing' condition: %w", err) + } + if errCount > 0 { + return "Error", nil + } + + val, err = extractStringField(cond, "status") + if err != nil { + return "", fmt.Errorf("error extracting status of 'Syncing' condition: %w", err) + } + if val == "True" { + return "Pending", nil + } + + return "Synced", nil +} + +func getConditionStatus(conditions []interface{}, condType string) (string, bool, error) { + cond, found, err := getCondition(conditions, condType) + if err != nil { + return "", false, err + } + if !found { + return "", false, nil + } + s, err := extractStringField(cond, "status") + if err != nil { + return "", false, err + } + return s, true, nil +} + +func getCondition(conditions []interface{}, condType string) (map[string]interface{}, bool, error) { + for i := range conditions { + cond, ok := conditions[i].(map[string]interface{}) + if !ok { + return map[string]interface{}{}, false, fmt.Errorf("failed to extract condition %d from slice", i) + } + t, err := extractStringField(cond, "type") + if err != nil { + return map[string]interface{}{}, false, err + } + + if t != condType { + continue + } + return cond, true, nil + } + return map[string]interface{}{}, false, nil +} + +func extractStringField(condition map[string]interface{}, field string) (string, error) { + t, ok := condition[field] + if !ok { + return "", fmt.Errorf("condition does not have a type field") + } + condVal, ok := t.(string) + if !ok { + return "", fmt.Errorf("value of '%s' condition is not of type 'string'", field) + } + return condVal, nil +} + +func extractErrorCount(cond map[string]interface{}) (int64, error) { + count, found, err := unstructured.NestedInt64(cond, "errorSummary", "totalCount") + if err != nil || !found { + return 0, err + } + return count, nil +} diff --git a/rollouts/controllers/watcher.go b/rollouts/controllers/watcher.go new file mode 100644 index 0000000000..8c49414cbf --- /dev/null +++ b/rollouts/controllers/watcher.go @@ -0,0 +1,146 @@ +// 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 controllers + +import ( + "context" + "time" + + "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/event" +) + +const ( + minReconnectDelay = 1 * time.Second + maxReconnectDelay = 30 * time.Second +) + +type watcher struct { + clusterRef v1alpha1.ClusterRef + + ctx context.Context + + cancelFunc context.CancelFunc + + client dynamic.Interface + + channel chan event.GenericEvent + + liens map[types.NamespacedName]struct{} +} + +func (w watcher) watch() { + clusterRefName := w.clusterRef.Name + var events <-chan watch.Event + var watcher watch.Interface + var bookmark string + defer func() { + if watcher != nil { + watcher.Stop() + } + }() + + reconnect := newBackoffTimer(minReconnectDelay, maxReconnectDelay) + defer reconnect.Stop() + +loop: + for { + select { + case <-reconnect.channel(): + var err error + klog.Infof("Starting watch for %s... ", clusterRefName) + watcher, err = w.client.Resource(rootSyncGVR).Watch(w.ctx, v1.ListOptions{}) + if err != nil { + klog.Errorf("Cannot start watch for %s: %v; will retry", clusterRefName, err) + reconnect.backoff() + } else { + klog.Infof("Watch successfully started for %s.", clusterRefName) + events = watcher.ResultChan() + } + case e, ok := <-events: + if !ok { + klog.Errorf("Watch event stream closed for cluster %s. Will restart watch from bookmark %q", clusterRefName, bookmark) + watcher.Stop() + events = nil + watcher = nil + + // Initiate reconnect + reconnect.reset() + } else if obj, ok := e.Object.(*unstructured.Unstructured); ok { + if e.Type == watch.Bookmark { + bookmark = obj.GetResourceVersion() + klog.Infof("Watch bookmark for %s: %q", clusterRefName, bookmark) + } else { + bookmark = obj.GetResourceVersion() + klog.Infof("Got Watch event for %s: rv: %q", clusterRefName, bookmark) + w.channel <- event.GenericEvent{ + Object: obj, + } + } + } else { + klog.V(5).Infof("Received unexpected watch event Object from %s: %T", e.Object, clusterRefName) + } + case <-w.ctx.Done(): + if w.ctx.Err() != nil { + klog.V(2).Infof("exiting watcher for %s, because context is done: %v", clusterRefName, w.ctx.Err()) + } else { + klog.Infof("Watch background routine exiting for %s; context done", clusterRefName) + } + break loop + } + } +} + +// TODO: This comes from Porch. Find a place to put code that can be shared. +type backoffTimer struct { + min, max, curr time.Duration + timer *time.Timer +} + +func newBackoffTimer(min, max time.Duration) *backoffTimer { + return &backoffTimer{ + min: min, + max: max, + timer: time.NewTimer(min), + } +} + +func (t *backoffTimer) Stop() bool { + return t.timer.Stop() +} + +func (t *backoffTimer) channel() <-chan time.Time { + return t.timer.C +} + +func (t *backoffTimer) reset() bool { + t.curr = t.min + return t.timer.Reset(t.curr) +} + +func (t *backoffTimer) backoff() bool { + curr := t.curr * 2 + if curr > t.max { + curr = t.max + } + t.curr = curr + return t.timer.Reset(curr) +} diff --git a/rollouts/go.mod b/rollouts/go.mod index a66e878146..d951cf31e3 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -82,6 +82,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect + k8s.io/klog v1.0.0 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index 713d2ff8df..d8ba2a8ae1 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -115,6 +115,7 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= @@ -684,6 +685,8 @@ k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= diff --git a/rollouts/main.go b/rollouts/main.go index c87e6b3228..10fe89f4cf 100644 --- a/rollouts/main.go +++ b/rollouts/main.go @@ -96,6 +96,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "Rollout") os.Exit(1) } + if err = (&controllers.RemoteRootSyncReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "RemoteRootSync") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index fa7a3e4238..2f748798c0 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) @@ -78,16 +79,33 @@ func (cs *ClusterStore) PrintClusterInfos(ctx context.Context, clusters *gkeclus } } -func (cs *ClusterStore) GetClusterClient(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (client.Client, error) { +func (cs *ClusterStore) GetCluster(ctx context.Context, name string) (*gkeclusterapis.ContainerCluster, error) { + gkeCluster := gkeclusterapis.ContainerCluster{} + clusterKey := client.ObjectKey{ + Namespace: "config-control", + Name: name, + } + if err := cs.Get(ctx, clusterKey, &gkeCluster); err != nil { + return nil, err + } + + return &gkeCluster, nil +} + +func (cs *ClusterStore) GetClusterClient(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (client.Client, dynamic.Interface, error) { clusterClientConfig, err := cs.getRESTConfig(ctx, cluster) if err != nil { - return nil, err + return nil, nil, err } cl, err := client.New(clusterClientConfig, client.Options{}) if err != nil { - return nil, err + return nil, nil, err + } + dynCl, err := dynamic.NewForConfig(clusterClientConfig) + if err != nil { + return nil, nil, err } - return cl, nil + return cl, dynCl, err } func (cs *ClusterStore) getRESTConfig(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (*rest.Config, error) { From 0d0833603c6323a2f7a412aecd96da9e068a9990 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 21 Dec 2022 18:32:57 -0800 Subject: [PATCH 06/38] rollouts: add package cluster matcher (#3700) --- rollouts/api/v1alpha1/rollout_types.go | 13 +- .../api/v1alpha1/zz_generated.deepcopy.go | 16 +++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 21 +++- .../samples/container_cluster_maui.yaml | 27 ++++ .../samples/container_cluster_oahu.yaml | 27 ++++ .../samples/gitops_v1alpha1_rollout.yaml | 5 +- rollouts/controllers/rollout_controller.go | 16 ++- rollouts/go.mod | 3 + rollouts/go.sum | 5 + .../packageclustermatcher.go | 118 ++++++++++++++++++ .../pkg/packagediscovery/packagediscovery.go | 6 +- 11 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 rollouts/config/samples/container_cluster_maui.yaml create mode 100644 rollouts/config/samples/container_cluster_oahu.yaml create mode 100644 rollouts/pkg/packageclustermatcher/packageclustermatcher.go diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 680d663ae0..071ec82e15 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -35,6 +35,9 @@ type RolloutSpec struct { // Targets specifies the clusters that will receive the KRM config packages. Targets ClusterTargetSelector `json:"targets,omitempty"` + + // PackageToTargetMatcher specifies the clusters that will receive a specific package. + PackageToTargetMatcher PackageToClusterMatcher `json:"packageToTargetMatcher"` } type ClusterTargetSelector struct { @@ -65,7 +68,7 @@ type GitSource struct { // GitSelector defines the selector to apply to Git. type GitSelector struct { Org string `json:"org"` - Name string `json:"name"` + Repo string `json:"repo"` Directory string `json:"directory"` Revision string `json:"revision"` SecretRef SecretReference `json:"secretRef,omitempty"` @@ -77,6 +80,14 @@ type SecretReference struct { Name string `json:"name,omitempty"` } +// +kubebuilder:validation:Enum=CEL +type MatcherType string + +type PackageToClusterMatcher struct { + Type MatcherType `json:"type"` + MatchExpression string `json:"matchExpression"` +} + // RolloutStatus defines the observed state of Rollout type RolloutStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 5905137ab7..a482431c12 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -110,6 +110,21 @@ func (in *GitSource) DeepCopy() *GitSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageToClusterMatcher) DeepCopyInto(out *PackageToClusterMatcher) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageToClusterMatcher. +func (in *PackageToClusterMatcher) DeepCopy() *PackageToClusterMatcher { + if in == nil { + return nil + } + out := new(PackageToClusterMatcher) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PackagesConfig) DeepCopyInto(out *PackagesConfig) { *out = *in @@ -292,6 +307,7 @@ func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { *out = *in out.Packages = in.Packages in.Targets.DeepCopyInto(&out.Targets) + out.PackageToTargetMatcher = in.PackageToTargetMatcher } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutSpec. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index adc71dba3f..31215c7acc 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -38,6 +38,20 @@ spec: description: description: Description is a user friendly description of this Rollout. type: string + packageToTargetMatcher: + description: PackageToTargetMatcher specifies the clusters that will + receive a specific package. + properties: + matchExpression: + type: string + type: + enum: + - CEL + type: string + required: + - matchExpression + - type + type: object packages: description: Packages source for this Rollout. properties: @@ -50,10 +64,10 @@ spec: properties: directory: type: string - name: - type: string org: type: string + repo: + type: string revision: type: string secretRef: @@ -66,8 +80,8 @@ spec: type: object required: - directory - - name - org + - repo - revision type: object required: @@ -135,6 +149,7 @@ spec: x-kubernetes-map-type: atomic type: object required: + - packageToTargetMatcher - packages type: object status: diff --git a/rollouts/config/samples/container_cluster_maui.yaml b/rollouts/config/samples/container_cluster_maui.yaml new file mode 100644 index 0000000000..95088dfe21 --- /dev/null +++ b/rollouts/config/samples/container_cluster_maui.yaml @@ -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. + +apiVersion: container.cnrm.cloud.google.com/v1beta1 +kind: ContainerCluster +metadata: + name: containercluster-maui + namespace: config-control + labels: + location/island: maui +spec: + description: Maui autopilot cluster + enableAutopilot: true + location: us-central1 + releaseChannel: + channel: REGULAR diff --git a/rollouts/config/samples/container_cluster_oahu.yaml b/rollouts/config/samples/container_cluster_oahu.yaml new file mode 100644 index 0000000000..20585ccf5c --- /dev/null +++ b/rollouts/config/samples/container_cluster_oahu.yaml @@ -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. + +apiVersion: container.cnrm.cloud.google.com/v1beta1 +kind: ContainerCluster +metadata: + name: containercluster-oahu + namespace: config-control + labels: + location/island: oahu +spec: + description: Oahu autopilot cluster + enableAutopilot: true + location: us-central1 + releaseChannel: + channel: REGULAR diff --git a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml index 80c7e68026..52023525e1 100644 --- a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml +++ b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml @@ -29,10 +29,13 @@ spec: git: selector: org: GoogleContainerTools - name: kpt-samples + repo: kpt-samples directory: "*" revision: main targets: selector: matchExpressions: - {key: location/island, operator: In, values: [oahu, maui]} + packageToTargetMatcher: + type: CEL + matchExpression: 'true' diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 0ba4e71e97..414430056f 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -29,6 +29,7 @@ import ( gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" "github.com/GoogleContainerTools/kpt/rollouts/pkg/clusterstore" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/packageclustermatcher" "github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery" ) @@ -109,7 +110,20 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, client.IgnoreNotFound(err) } - logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages) + packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(gkeClusters.Items, discoveredPackages) + + allClusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) + + if err != nil { + logger.Error(err, "get cluster packages failed") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + for _, clusterPackages := range allClusterPackages { + clusterName := clusterPackages.Cluster.Name + + logger.Info("cluster packages", "cluster", clusterName, "packagesCount", len(clusterPackages.Packages), "packages", clusterPackages.Packages) + } return ctrl.Result{}, err } diff --git a/rollouts/go.mod b/rollouts/go.mod index d951cf31e3..cb6f72a831 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -6,6 +6,7 @@ require ( cloud.google.com/go/iam v0.7.0 github.com/GoogleCloudPlatform/k8s-config-connector v1.98.0 github.com/golang/protobuf v1.5.2 + github.com/google/cel-go v0.13.0 github.com/google/go-github/v48 v48.2.0 github.com/onsi/ginkgo/v2 v2.1.6 github.com/onsi/gomega v1.20.1 @@ -28,6 +29,7 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -63,6 +65,7 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index d8ba2a8ae1..69139d8e9c 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -66,6 +66,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -169,6 +171,8 @@ 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/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU= +github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s= 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= @@ -303,6 +307,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 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 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/rollouts/pkg/packageclustermatcher/packageclustermatcher.go b/rollouts/pkg/packageclustermatcher/packageclustermatcher.go new file mode 100644 index 0000000000..dc81cb1306 --- /dev/null +++ b/rollouts/pkg/packageclustermatcher/packageclustermatcher.go @@ -0,0 +1,118 @@ +// 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 packageclustermatcher + +import ( + "fmt" + + gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker/decls" +) + +type PackageClusterMatcher struct { + clusters []gkeclusterapis.ContainerCluster + packages []packagediscovery.DiscoveredPackage +} + +type ClusterPackages struct { + Cluster gkeclusterapis.ContainerCluster + Packages []packagediscovery.DiscoveredPackage +} + +func NewPackageClusterMatcher(clusters []gkeclusterapis.ContainerCluster, packages []packagediscovery.DiscoveredPackage) *PackageClusterMatcher { + return &PackageClusterMatcher{ + clusters: clusters, + packages: packages, + } +} + +func (m *PackageClusterMatcher) GetClusterPackages(matcher gitopsv1alpha1.PackageToClusterMatcher) ([]ClusterPackages, error) { + clusters := m.clusters + packages := m.packages + + allClusterPackages := []ClusterPackages{} + + for _, cluster := range clusters { + matchedPackages := []packagediscovery.DiscoveredPackage{} + + celCluster := map[string]interface{}{ + "name": cluster.ObjectMeta.Name, + "labels": cluster.ObjectMeta.Labels, + } + + for _, discoveredPackage := range packages { + celPackage := map[string]interface{}{ + "org": discoveredPackage.Org, + "repo": discoveredPackage.Repo, + "directory": discoveredPackage.Directory, + } + + isMatch, err := isPackageClusterMatch(matcher.MatchExpression, celCluster, celPackage) + if err != nil { + return nil, fmt.Errorf("unable to execute package cluster matcher expression: %w", err) + } + + if isMatch { + matchedPackages = append(matchedPackages, discoveredPackage) + } + } + + clusterPackages := ClusterPackages{ + Cluster: cluster, + Packages: matchedPackages, + } + + allClusterPackages = append(allClusterPackages, clusterPackages) + } + + return allClusterPackages, nil +} + +func isPackageClusterMatch(expr string, cluster, rolloutPackage map[string]interface{}) (bool, error) { + env, err := cel.NewEnv( + cel.Declarations( + decls.NewVar("cluster", decls.Dyn), + decls.NewVar("rolloutPackage", decls.Dyn), + )) + if err != nil { + return false, err + } + + p, issue := env.Parse(expr) + if issue != nil && issue.Err() != nil { + return false, issue.Err() + } + + c, issue := env.Check(p) + if issue != nil && issue.Err() != nil { + return false, issue.Err() + } + + prg, err := env.Program(c) + if err != nil { + return false, err + } + + out, _, err := prg.Eval(map[string]interface{}{ + "cluster": cluster, + "rolloutPackage": rolloutPackage, + }) + + return out.Value().(bool), err +} diff --git a/rollouts/pkg/packagediscovery/packagediscovery.go b/rollouts/pkg/packagediscovery/packagediscovery.go index f8ccfaccb8..fb9d3ab340 100644 --- a/rollouts/pkg/packagediscovery/packagediscovery.go +++ b/rollouts/pkg/packagediscovery/packagediscovery.go @@ -35,7 +35,7 @@ type PackageDiscovery struct { type DiscoveredPackage struct { Org string - Name string + Repo string Directory string Revision string } @@ -55,7 +55,7 @@ func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage return nil, fmt.Errorf("unable to create git client: %w", err) } - tree, _, err := gitClient.Git.GetTree(ctx, gitRepoSelector.Org, gitRepoSelector.Name, gitRepoSelector.Revision, true) + tree, _, err := gitClient.Git.GetTree(ctx, gitRepoSelector.Org, gitRepoSelector.Repo, gitRepoSelector.Revision, true) if err != nil { return nil, fmt.Errorf("unable to fetch tree from git: %w", err) } @@ -72,7 +72,7 @@ func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage discoveredPackages := []DiscoveredPackage{} for _, path := range packagesPaths { - thisDiscoveredPackage := DiscoveredPackage{Org: gitRepoSelector.Org, Name: gitRepoSelector.Name, Revision: gitRepoSelector.Revision, Directory: path} + thisDiscoveredPackage := DiscoveredPackage{Org: gitRepoSelector.Org, Repo: gitRepoSelector.Repo, Revision: gitRepoSelector.Revision, Directory: path} discoveredPackages = append(discoveredPackages, thisDiscoveredPackage) } From f6872780cc6f4b0ffab7fcd79359d05c2786da5d Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Thu, 22 Dec 2022 16:15:30 -0800 Subject: [PATCH 07/38] rollouts: add AllAtOnce strategy (#3703) --- rollouts/api/v1alpha1/rollout_types.go | 44 +++ .../api/v1alpha1/zz_generated.deepcopy.go | 121 ++++++- .../crd/bases/gitops.kpt.dev_rollouts.yaml | 126 +++++++ .../samples/gitops_v1alpha1_rollout.yaml | 18 +- rollouts/controllers/rollout_controller.go | 331 ++++++++++++++++-- 5 files changed, 605 insertions(+), 35 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 071ec82e15..dd068d1006 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -38,6 +38,8 @@ type RolloutSpec struct { // PackageToTargetMatcher specifies the clusters that will receive a specific package. PackageToTargetMatcher PackageToClusterMatcher `json:"packageToTargetMatcher"` + // Strategy specifies the rollout strategy to use for this rollout. + Strategy RolloutStrategy `json:"strategy"` } type ClusterTargetSelector struct { @@ -88,10 +90,52 @@ type PackageToClusterMatcher struct { MatchExpression string `json:"matchExpression"` } +// +kubebuilder:validation:Enum=AllAtOnce;Rolling;Progressive +type StrategyType string + +const ( + AllAtOnce StrategyType = "AllAtOnce" + Rolling StrategyType = "Rolling" + Progressive StrategyType = "Progressive" +) + +type StrategyAllAtOnce struct{} + +type StrategyRolling struct { + MaxUnavailable int64 `json:"maxUnavailable"` +} + +// StrategyProgressive allows staged rollouts +// where the entire rollout will progress through different stages (aka steps, phases or waves). +type StrategyProgressive struct{} + +type RolloutStrategy struct { + Type StrategyType `json:"type"` + AllAtOnce *StrategyAllAtOnce `json:"allAtOnce,omitempty"` + Rolling *StrategyRolling `json:"rolling,omitempty"` + Progressive *StrategyProgressive `json:"progressive,omitempty"` +} + // RolloutStatus defines the observed state of Rollout type RolloutStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions describes the reconciliation state of the object. + Conditions []metav1.Condition `json:"conditions,omitempty"` + + ClusterStatuses []ClusterStatus `json:"clusterStatuses,omitempty"` +} + +type ClusterStatus struct { + Name string `json:"name"` + PackageStatus PackageStatus `json:"packageStatus"` +} + +type PackageStatus struct { + PackageID string `json:"packageId"` + SyncStatus string `json:"syncStatus"` } //+kubebuilder:object:root=true diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index a482431c12..29f4ef85d5 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -41,6 +41,22 @@ func (in *ClusterRef) DeepCopy() *ClusterRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { + *out = *in + out.PackageStatus = in.PackageStatus +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterStatus. +func (in *ClusterStatus) DeepCopy() *ClusterStatus { + if in == nil { + return nil + } + out := new(ClusterStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterTargetSelector) DeepCopyInto(out *ClusterTargetSelector) { *out = *in @@ -110,6 +126,21 @@ func (in *GitSource) DeepCopy() *GitSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageStatus) DeepCopyInto(out *PackageStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageStatus. +func (in *PackageStatus) DeepCopy() *PackageStatus { + if in == nil { + return nil + } + out := new(PackageStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PackageToClusterMatcher) DeepCopyInto(out *PackageToClusterMatcher) { *out = *in @@ -249,7 +280,7 @@ func (in *Rollout) DeepCopyInto(out *Rollout) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rollout. @@ -308,6 +339,7 @@ func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { out.Packages = in.Packages in.Targets.DeepCopyInto(&out.Targets) out.PackageToTargetMatcher = in.PackageToTargetMatcher + in.Strategy.DeepCopyInto(&out.Strategy) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutSpec. @@ -323,6 +355,18 @@ func (in *RolloutSpec) DeepCopy() *RolloutSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RolloutStatus) DeepCopyInto(out *RolloutStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ClusterStatuses != nil { + in, out := &in.ClusterStatuses, &out.ClusterStatuses + *out = make([]ClusterStatus, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStatus. @@ -335,6 +379,36 @@ func (in *RolloutStatus) DeepCopy() *RolloutStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RolloutStrategy) DeepCopyInto(out *RolloutStrategy) { + *out = *in + if in.AllAtOnce != nil { + in, out := &in.AllAtOnce, &out.AllAtOnce + *out = new(StrategyAllAtOnce) + **out = **in + } + if in.Rolling != nil { + in, out := &in.Rolling, &out.Rolling + *out = new(StrategyRolling) + **out = **in + } + if in.Progressive != nil { + in, out := &in.Progressive, &out.Progressive + *out = new(StrategyProgressive) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStrategy. +func (in *RolloutStrategy) DeepCopy() *RolloutStrategy { + if in == nil { + return nil + } + out := new(RolloutStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RootSyncInfo) DeepCopyInto(out *RootSyncInfo) { *out = *in @@ -389,3 +463,48 @@ func (in *SecretReference) DeepCopy() *SecretReference { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyAllAtOnce) DeepCopyInto(out *StrategyAllAtOnce) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyAllAtOnce. +func (in *StrategyAllAtOnce) DeepCopy() *StrategyAllAtOnce { + if in == nil { + return nil + } + out := new(StrategyAllAtOnce) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyProgressive) DeepCopyInto(out *StrategyProgressive) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyProgressive. +func (in *StrategyProgressive) DeepCopy() *StrategyProgressive { + if in == nil { + return nil + } + out := new(StrategyProgressive) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StrategyRolling) DeepCopyInto(out *StrategyRolling) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyRolling. +func (in *StrategyRolling) DeepCopy() *StrategyRolling { + if in == nil { + return nil + } + out := new(StrategyRolling) + in.DeepCopyInto(out) + return out +} diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 31215c7acc..eff2decf17 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -95,6 +95,34 @@ spec: - git - sourceType type: object + strategy: + description: Strategy specifies the rollout strategy to use for this + rollout. + properties: + allAtOnce: + type: object + progressive: + description: StrategyProgressive allows staged rollouts where + the entire rollout will progress through different stages (aka + steps, phases or waves). + type: object + rolling: + properties: + maxUnavailable: + format: int64 + type: integer + required: + - maxUnavailable + type: object + type: + enum: + - AllAtOnce + - Rolling + - Progressive + type: string + required: + - type + type: object targets: description: Targets specifies the clusters that will receive the KRM config packages. @@ -151,9 +179,107 @@ spec: required: - packageToTargetMatcher - packages + - strategy type: object status: description: RolloutStatus defines the observed state of Rollout + properties: + clusterStatuses: + items: + properties: + name: + type: string + packageStatus: + properties: + packageId: + type: string + syncStatus: + type: string + required: + - packageId + - syncStatus + type: object + required: + - name + - packageStatus + type: object + type: array + conditions: + description: Conditions describes the reconciliation state of the + object. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + format: int64 + type: integer type: object type: object served: true diff --git a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml index 52023525e1..b9242e9f7e 100644 --- a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml +++ b/rollouts/config/samples/gitops_v1alpha1_rollout.yaml @@ -28,14 +28,18 @@ spec: sourceType: git git: selector: - org: GoogleContainerTools - repo: kpt-samples - directory: "*" + org: droot + repo: oahu + directory: namespaces revision: main - targets: - selector: - matchExpressions: - - {key: location/island, operator: In, values: [oahu, maui]} packageToTargetMatcher: type: CEL matchExpression: 'true' + targets: + selector: + matchLabels: + env: staging +# matchExpressions: +# - {key: env, operator: In, values: [dev, staging]} + strategy: + type: AllAtOnce diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 414430056f..62e14c156a 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -19,11 +19,16 @@ package controllers import ( "context" "flag" + "fmt" v1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" @@ -42,6 +47,10 @@ func (o *Options) InitDefaults() { func (o *Options) BindFlags(prefix string, flags *flag.FlagSet) { } +const ( + rolloutLabel = "gitops.kpt.dev/rollout-name" +) + // RolloutReconciler reconciles a Rollout object type RolloutReconciler struct { client.Client @@ -69,63 +78,254 @@ type RolloutReconciler struct { // Fetch the READY kcc clusters. // For each kcc cluster, fetch RootSync objects in each of the KCC clusters. func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) var rollout gitopsv1alpha1.Rollout + logger.Info("reconciling", "key", req.NamespacedName) + if err := r.Get(ctx, req.NamespacedName, &rollout); err != nil { + logger.Error(err, "unable to fetch Rollout") + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. return ctrl.Result{}, client.IgnoreNotFound(err) } - logger := log.FromContext(ctx) - - logger.Info("reconciling", "key", req.NamespacedName) + myFinalizerName := "gitops.kpt.dev/rollouts" + if rollout.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, + // then lets add the finalizer and update the object. This is equivalent + // registering our finalizer. + if !controllerutil.ContainsFinalizer(&rollout, myFinalizerName) { + controllerutil.AddFinalizer(&rollout, myFinalizerName) + if err := r.Update(ctx, &rollout); err != nil { + return ctrl.Result{}, fmt.Errorf("error adding finalizer: %w", err) + } + } + } else { + // The object is being deleted + if controllerutil.ContainsFinalizer(&rollout, myFinalizerName) { + // remove our finalizer from the list and update it. + controllerutil.RemoveFinalizer(&rollout, myFinalizerName) + if err := r.Update(ctx, &rollout); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to update %s after delete finalizer: %w", req.Name, err) + } + } + // Stop reconciliation as the item is being deleted + return ctrl.Result{}, nil + } - gkeClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) + err := r.reconcileRollout(ctx, &rollout) if err != nil { return ctrl.Result{}, err } - r.store.PrintClusterInfos(ctx, gkeClusters) + return ctrl.Result{}, nil +} - for _, gkeCluster := range gkeClusters.Items { - cl, _, err := r.store.GetClusterClient(ctx, &gkeCluster) - if err != nil { - return ctrl.Result{}, err - } - r.testClusterClient(ctx, cl) - } +func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout) error { + logger := log.FromContext(ctx) - if err := r.Get(ctx, req.NamespacedName, &rollout); err != nil { - logger.Error(err, "unable to fetch Rollout") - // we'll ignore not-found errors, since they can't be fixed by an immediate - // requeue (we'll need to wait for a new notification), and we can get them - // on deleted requests. - return ctrl.Result{}, client.IgnoreNotFound(err) + clusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) + if err != nil { + return err } + logger.Info("discovered clusters", "count", len(clusters.Items)) - packageDiscoveryClient := packagediscovery.NewPackageDiscovery(rollout.Spec.Packages, r.Client, req.Namespace) + packageDiscoveryClient := packagediscovery.NewPackageDiscovery(rollout.Spec.Packages, r.Client, rollout.Namespace) discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx) if err != nil { - logger.Error(err, "package discovery failed") - return ctrl.Result{}, client.IgnoreNotFound(err) + logger.Error(err, "failed to discover packages") + return client.IgnoreNotFound(err) } + logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages) - packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(gkeClusters.Items, discoveredPackages) + packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(clusters.Items, discoveredPackages) allClusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) - if err != nil { logger.Error(err, "get cluster packages failed") - return ctrl.Result{}, client.IgnoreNotFound(err) + return client.IgnoreNotFound(err) } for _, clusterPackages := range allClusterPackages { clusterName := clusterPackages.Cluster.Name - logger.Info("cluster packages", "cluster", clusterName, "packagesCount", len(clusterPackages.Packages), "packages", clusterPackages.Packages) } - return ctrl.Result{}, err + targets, err := r.computeTargets(ctx, rollout, allClusterPackages) + if err != nil { + return err + } + + clusterStatuses, err := r.rolloutTargets(ctx, rollout, targets) + if err != nil { + return err + } + + if err := r.updateStatus(ctx, rollout, clusterStatuses); err != nil { + return err + } + return nil +} + +func (r *RolloutReconciler) updateStatus(ctx context.Context, rollout *gitopsv1alpha1.Rollout, clusterStatuses []gitopsv1alpha1.ClusterStatus) error { + logger := log.FromContext(ctx) + logger.Info("updating the status", "cluster statuses", len(clusterStatuses)) + rollout.Status.ClusterStatuses = clusterStatuses + rollout.Status.ObservedGeneration = rollout.Generation + return r.Client.Status().Update(ctx, rollout) +} + +/* +so we compute targets where each target consists of (cluster, package) +compute the RRS corresponding to each (cluster, package) pair +For RRS, name has to be function of cluster-id and package-id. +For RRS, make rootSyncTemplate +*/ +func (r *RolloutReconciler) computeTargets(ctx context.Context, + rollout *gitopsv1alpha1.Rollout, + clusterPackages []packageclustermatcher.ClusterPackages) (*Targets, error) { + + RRSkeysToBeDeleted := map[client.ObjectKey]*gitopsv1alpha1.RemoteRootSync{} + // let's take a look at existing remoterootsyncs + existingRRSs, err := r.listRemoteRootSyncs(ctx, rollout.Name, rollout.Namespace) + if err != nil { + return nil, err + } + // initially assume all the keys to be deleted + for _, rrs := range existingRRSs { + RRSkeysToBeDeleted[client.ObjectKeyFromObject(rrs)] = rrs + } + klog.Infof("Found remoterootsyncs: %s", toRemoteRootSyncNames(existingRRSs)) + targets := &Targets{} + // track keys of all the desired remote rootsyncs + for _, clusterPkg := range clusterPackages { + // TODO: figure out multiple packages per cluster story + if len(clusterPkg.Packages) < 1 { + continue + } + cluster := &clusterPkg.Cluster + pkg := &clusterPkg.Packages[0] + rrs := gitopsv1alpha1.RemoteRootSync{} + key := client.ObjectKey{ + Namespace: rollout.Namespace, + Name: fmt.Sprintf("%s-%s", pkgID(pkg), cluster.Name), + } + // since this RRS need to exist, remove it from the deletion list + delete(RRSkeysToBeDeleted, key) + // check if this remoterootsync for this package exists or not ? + err := r.Client.Get(ctx, key, &rrs) + if err != nil { + if apierrors.IsNotFound(err) { // rrs is missing + targets.ToBeCreated = append(targets.ToBeCreated, &clusterPackagePair{ + cluster: cluster, + packageRef: pkg, + }) + } else { + // some other error encountered + return nil, err + } + } else { + // remoterootsync already exists + if pkg.Revision != rrs.Spec.Template.Spec.Git.Revision { + rrs.Spec.Template.Spec.Git.Revision = pkg.Revision + // revision has been updated + targets.ToBeUpdated = append(targets.ToBeUpdated, &rrs) + } else { + targets.Unchanged = append(targets.Unchanged, &rrs) + } + } + } + + for _, rrs := range RRSkeysToBeDeleted { + targets.ToBeDeleted = append(targets.ToBeDeleted, rrs) + } + + return targets, nil +} + +func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, targets *Targets) ([]gitopsv1alpha1.ClusterStatus, error) { + + clusterStatuses := []gitopsv1alpha1.ClusterStatus{} + + if rollout.Spec.Strategy.Type != gitopsv1alpha1.AllAtOnce { + return clusterStatuses, fmt.Errorf("%v strategy not supported yet", rollout.Spec.Strategy.Type) + } + + for _, target := range targets.ToBeCreated { + rootSyncSpec := toRootSyncSpec(target.packageRef) + rrs := newRemoteRootSync(rollout, + gitopsv1alpha1.ClusterRef{Name: target.cluster.Name}, + rootSyncSpec, + pkgID(target.packageRef), + ) + if err := r.Create(ctx, rrs); err != nil { + klog.Warningf("Error creating RemoteRootSync %s: %v", rrs.Name, err) + return nil, err + } + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: rrs.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: rrs.Name, + SyncStatus: rrs.Status.SyncStatus, + }, + }) + } + + for _, target := range targets.ToBeUpdated { + if err := r.Update(ctx, target); err != nil { + klog.Warningf("Error updating RemoteRootSync %s: %v", target.Name, err) + return nil, err + } + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: target.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: target.Name, + SyncStatus: target.Status.SyncStatus, + }, + }) + } + + for _, target := range targets.ToBeDeleted { + if err := r.Delete(ctx, target); err != nil { + klog.Warningf("Error deleting RemoteRootSync %s: %v", target.Name, err) + return nil, err + } + } + + for _, target := range targets.Unchanged { + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: target.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: target.Name, + SyncStatus: target.Status.SyncStatus, + }, + }) + } + + return clusterStatuses, nil +} + +type Targets struct { + ToBeCreated []*clusterPackagePair + ToBeUpdated []*gitopsv1alpha1.RemoteRootSync + ToBeDeleted []*gitopsv1alpha1.RemoteRootSync + Unchanged []*gitopsv1alpha1.RemoteRootSync +} + +type clusterPackagePair struct { + cluster *gkeclusterapis.ContainerCluster + packageRef *packagediscovery.DiscoveredPackage +} + +func toRemoteRootSyncNames(rsss []*gitopsv1alpha1.RemoteRootSync) []string { + var names []string + for _, rss := range rsss { + names = append(names, rss.Name) + } + return names } func (r *RolloutReconciler) testClusterClient(ctx context.Context, cl client.Client) error { @@ -139,10 +339,85 @@ func (r *RolloutReconciler) testClusterClient(ctx context.Context, cl client.Cli return nil } +func (r *RolloutReconciler) listRemoteRootSyncs(ctx context.Context, rsdName, rsdNamespace string) ([]*gitopsv1alpha1.RemoteRootSync, error) { + var list gitopsv1alpha1.RemoteRootSyncList + if err := r.List(ctx, &list, client.MatchingLabels{rolloutLabel: rsdName}, client.InNamespace(rsdNamespace)); err != nil { + return nil, err + } + var remoterootsyncs []*gitopsv1alpha1.RemoteRootSync + for i := range list.Items { + item := &list.Items[i] + remoterootsyncs = append(remoterootsyncs, item) + } + return remoterootsyncs, nil +} + +func isRRSSynced(rss *gitopsv1alpha1.RemoteRootSync) bool { + if rss.Generation != rss.Status.ObservedGeneration { + return false + } + + if rss.Status.SyncStatus == "Synced" { + return true + } + return false +} + +// Given a package identifier and cluster, create a RemoteRootSync object. +func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha1.ClusterRef, rssSpec *gitopsv1alpha1.RootSyncSpec, pkgID string) *gitopsv1alpha1.RemoteRootSync { + t := true + return &gitopsv1alpha1.RemoteRootSync{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%s", pkgID, clusterRef.Name), + Namespace: rollout.Namespace, + Labels: map[string]string{ + rolloutLabel: rollout.Name, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: rollout.APIVersion, + Kind: rollout.Kind, + Name: rollout.Name, + UID: rollout.UID, + Controller: &t, + }, + }, + }, + Spec: gitopsv1alpha1.RemoteRootSyncSpec{ + ClusterRef: clusterRef, + Template: &gitopsv1alpha1.RootSyncInfo{ + Spec: rssSpec, + }, + }, + } +} + +func toRootSyncSpec(dpkg *packagediscovery.DiscoveredPackage) *gitopsv1alpha1.RootSyncSpec { + return &gitopsv1alpha1.RootSyncSpec{ + SourceFormat: "unstructured", + Git: &gitopsv1alpha1.GitInfo{ + Repo: fmt.Sprintf("https://github.com/%s/%s.git", dpkg.Org, dpkg.Repo), + Revision: dpkg.Revision, + Dir: dpkg.Directory, + Branch: "main", + Auth: "none", + }, + } +} + +func pkgID(dpkg *packagediscovery.DiscoveredPackage) string { + return fmt.Sprintf("%s-%s-%s", dpkg.Org, dpkg.Repo, dpkg.Directory) +} + // SetupWithManager sets up the controller with the Manager. func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := gkeclusterapis.AddToScheme(mgr.GetScheme()); err != nil { + return err + } + if err := gitopsv1alpha1.AddToScheme(mgr.GetScheme()); err != nil { + return err + } r.Client = mgr.GetClient() - gkeclusterapis.AddToScheme(mgr.GetScheme()) // setup the clusterstore r.store = &clusterstore.ClusterStore{ @@ -152,7 +427,9 @@ func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { if err := r.store.Init(); err != nil { return err } + // TODO: watch cluster resources as well return ctrl.NewControllerManagedBy(mgr). For(&gitopsv1alpha1.Rollout{}). + Owns(&gitopsv1alpha1.RemoteRootSync{}). Complete(r) } From dc5fd534d3439bb3f22eb289ef0246a5031b653b Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Thu, 22 Dec 2022 17:31:04 -0800 Subject: [PATCH 08/38] rollouts: allow packages to be discovered from multiple repositories (#3702) --- rollouts/api/v1alpha1/rollout_types.go | 2 +- .../api/v1alpha1/zz_generated.deepcopy.go | 2 +- .../samples/container_cluster_lanai.yaml | 29 +++++ .../samples/container_cluster_maui.yaml | 4 +- .../samples/container_cluster_oahu.yaml | 4 +- .../samples/gitops_rollout_kpt_samples.yaml | 37 ++++++ ..._rollout.yaml => gitops_rollout_oahu.yaml} | 12 +- .../samples/gitops_rollout_repo_selector.yaml | 37 ++++++ rollouts/go.mod | 2 +- .../pkg/packagediscovery/packagediscovery.go | 119 +++++++++++++++--- .../packagediscovery/packagediscovery_test.go | 28 ++++- 11 files changed, 238 insertions(+), 38 deletions(-) create mode 100644 rollouts/config/samples/container_cluster_lanai.yaml create mode 100644 rollouts/config/samples/gitops_rollout_kpt_samples.yaml rename rollouts/config/samples/{gitops_v1alpha1_rollout.yaml => gitops_rollout_oahu.yaml} (72%) create mode 100644 rollouts/config/samples/gitops_rollout_repo_selector.yaml diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index dd068d1006..0a37bd1cb8 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -64,7 +64,7 @@ type PackagesConfig struct { // GitSource defines the packages source in Git. type GitSource struct { - GitRepoSelector GitSelector `json:"selector"` + Selector GitSelector `json:"selector"` } // GitSelector defines the selector to apply to Git. diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 29f4ef85d5..e15442bcfb 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -113,7 +113,7 @@ func (in *GitSelector) DeepCopy() *GitSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GitSource) DeepCopyInto(out *GitSource) { *out = *in - out.GitRepoSelector = in.GitRepoSelector + out.Selector = in.Selector } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSource. diff --git a/rollouts/config/samples/container_cluster_lanai.yaml b/rollouts/config/samples/container_cluster_lanai.yaml new file mode 100644 index 0000000000..6c9c4438f7 --- /dev/null +++ b/rollouts/config/samples/container_cluster_lanai.yaml @@ -0,0 +1,29 @@ +# 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. + +apiVersion: container.cnrm.cloud.google.com/v1beta1 +kind: ContainerCluster +metadata: + name: lanai + namespace: config-control + labels: + env: dev + location/island: lanai + location/state: hawaii +spec: + description: Lanai autopilot cluster + enableAutopilot: true + location: us-central1 + releaseChannel: + channel: REGULAR diff --git a/rollouts/config/samples/container_cluster_maui.yaml b/rollouts/config/samples/container_cluster_maui.yaml index 95088dfe21..a1d2d63794 100644 --- a/rollouts/config/samples/container_cluster_maui.yaml +++ b/rollouts/config/samples/container_cluster_maui.yaml @@ -15,10 +15,12 @@ apiVersion: container.cnrm.cloud.google.com/v1beta1 kind: ContainerCluster metadata: - name: containercluster-maui + name: maui namespace: config-control labels: + env: prod location/island: maui + location/state: hawaii spec: description: Maui autopilot cluster enableAutopilot: true diff --git a/rollouts/config/samples/container_cluster_oahu.yaml b/rollouts/config/samples/container_cluster_oahu.yaml index 20585ccf5c..f32a9662da 100644 --- a/rollouts/config/samples/container_cluster_oahu.yaml +++ b/rollouts/config/samples/container_cluster_oahu.yaml @@ -15,10 +15,12 @@ apiVersion: container.cnrm.cloud.google.com/v1beta1 kind: ContainerCluster metadata: - name: containercluster-oahu + name: oahu namespace: config-control labels: + env: staging location/island: oahu + location/state: hawaii spec: description: Oahu autopilot cluster enableAutopilot: true diff --git a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml new file mode 100644 index 0000000000..534136d65f --- /dev/null +++ b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml @@ -0,0 +1,37 @@ +# 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. + +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + name: rollout-kpt-samples +spec: + description: kpt samples rollout + packages: + sourceType: git + git: + selector: + org: GoogleContainerTools + repo: kpt-samples + directory: "*" + revision: main + targets: + selector: + matchExpressions: + - {key: location/island, operator: In, values: [oahu, maui]} + packageToTargetMatcher: + type: CEL + matchExpression: 'true' + strategy: + type: AllAtOnce diff --git a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml b/rollouts/config/samples/gitops_rollout_oahu.yaml similarity index 72% rename from rollouts/config/samples/gitops_v1alpha1_rollout.yaml rename to rollouts/config/samples/gitops_rollout_oahu.yaml index b9242e9f7e..edaaa236eb 100644 --- a/rollouts/config/samples/gitops_v1alpha1_rollout.yaml +++ b/rollouts/config/samples/gitops_rollout_oahu.yaml @@ -15,15 +15,9 @@ apiVersion: gitops.kpt.dev/v1alpha1 kind: Rollout metadata: - labels: - app.kubernetes.io/name: rollout - app.kubernetes.io/instance: rollout-sample - app.kubernetes.io/part-of: rollouts - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: rollouts - name: rollout-sample + name: rollout-oahu spec: - description: "this is first rollout :)" + description: oahu rollout packages: sourceType: git git: @@ -39,7 +33,5 @@ spec: selector: matchLabels: env: staging -# matchExpressions: -# - {key: env, operator: In, values: [dev, staging]} strategy: type: AllAtOnce diff --git a/rollouts/config/samples/gitops_rollout_repo_selector.yaml b/rollouts/config/samples/gitops_rollout_repo_selector.yaml new file mode 100644 index 0000000000..cbcd9bf4c2 --- /dev/null +++ b/rollouts/config/samples/gitops_rollout_repo_selector.yaml @@ -0,0 +1,37 @@ +# 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. + +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + name: rollout-repo-selector +spec: + description: repo selector rollout + packages: + sourceType: git + git: + selector: + org: droot + repo: "*-apps" + directory: "*" + revision: main + targets: + selector: + matchExpressions: + - {key: location/state, operator: In, values: [hawaii]} + packageToTargetMatcher: + type: CEL + matchExpression: rolloutPackage.repo == cluster.labels['location/island'] + '-apps' + strategy: + type: AllAtOnce diff --git a/rollouts/go.mod b/rollouts/go.mod index cb6f72a831..8b402bd906 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -16,6 +16,7 @@ require ( k8s.io/api v0.25.0 k8s.io/apimachinery v0.25.4 k8s.io/client-go v0.25.0 + k8s.io/klog v1.0.0 k8s.io/klog/v2 v2.80.1 sigs.k8s.io/controller-runtime v0.13.1 ) @@ -85,7 +86,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect - k8s.io/klog v1.0.0 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect diff --git a/rollouts/pkg/packagediscovery/packagediscovery.go b/rollouts/pkg/packagediscovery/packagediscovery.go index fb9d3ab340..6dd07494d9 100644 --- a/rollouts/pkg/packagediscovery/packagediscovery.go +++ b/rollouts/pkg/packagediscovery/packagediscovery.go @@ -18,7 +18,8 @@ import ( "context" "fmt" "net/http" - "path/filepath" + "regexp" + "strings" gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" "github.com/google/go-github/v48/github" @@ -49,30 +50,84 @@ func NewPackageDiscovery(config gitopsv1alpha1.PackagesConfig, client client.Cli } func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage, error) { - gitRepoSelector := d.config.Git.GitRepoSelector gitClient, err := d.getGitHubClient(ctx) if err != nil { return nil, fmt.Errorf("unable to create git client: %w", err) } - tree, _, err := gitClient.Git.GetTree(ctx, gitRepoSelector.Org, gitRepoSelector.Repo, gitRepoSelector.Revision, true) + discoveredPackages := []DiscoveredPackage{} + + repositoryNames, err := d.getRepositoryNames(gitClient, ctx) if err != nil { - return nil, fmt.Errorf("unable to fetch tree from git: %w", err) + return nil, fmt.Errorf("unable to get repositories: %w", err) } - allPaths := []string{} - for _, entry := range tree.Entries { - if *entry.Type == "tree" { - allPaths = append(allPaths, *entry.Path) + for _, repositoryName := range repositoryNames { + repoPackages, err := d.getPackagesForRepository(gitClient, ctx, repositoryName) + if err != nil { + return nil, fmt.Errorf("unable to get packages: %w", err) } + + discoveredPackages = append(discoveredPackages, repoPackages...) } - packagesPaths := filterDirectories(gitRepoSelector.Directory, allPaths) + return discoveredPackages, nil +} + +func (d *PackageDiscovery) getRepositoryNames(gitClient *github.Client, ctx context.Context) ([]string, error) { + selector := d.config.Git.Selector + repositoryNames := []string{} + + if isSelectorField(selector.Repo) { + // TOOD: add pagination + listOptions := github.RepositoryListOptions{} + listOptions.PerPage = 150 + + repositories, _, err := gitClient.Repositories.List(ctx, selector.Org, &listOptions) + if err != nil { + return nil, err + } + allRepositoryNames := []string{} + for _, repository := range repositories { + allRepositoryNames = append(allRepositoryNames, *repository.Name) + } + + matchRepositoryNames := filterByPattern(selector.Repo, allRepositoryNames) + + repositoryNames = append(repositoryNames, matchRepositoryNames...) + } else { + repositoryNames = append(repositoryNames, selector.Repo) + } + + return repositoryNames, nil +} + +func (d *PackageDiscovery) getPackagesForRepository(gitClient *github.Client, ctx context.Context, repoName string) ([]DiscoveredPackage, error) { discoveredPackages := []DiscoveredPackage{} + selector := d.config.Git.Selector - for _, path := range packagesPaths { - thisDiscoveredPackage := DiscoveredPackage{Org: gitRepoSelector.Org, Repo: gitRepoSelector.Repo, Revision: gitRepoSelector.Revision, Directory: path} + if isSelectorField(selector.Directory) { + tree, _, err := gitClient.Git.GetTree(ctx, selector.Org, repoName, selector.Revision, true) + if err != nil { + return nil, err + } + + allDirectories := []string{} + for _, entry := range tree.Entries { + if *entry.Type == "tree" { + allDirectories = append(allDirectories, *entry.Path) + } + } + + directories := filterByPattern(selector.Directory, allDirectories) + + for _, directory := range directories { + thisDiscoveredPackage := DiscoveredPackage{Org: selector.Org, Repo: repoName, Revision: selector.Revision, Directory: directory} + discoveredPackages = append(discoveredPackages, thisDiscoveredPackage) + } + } else { + thisDiscoveredPackage := DiscoveredPackage{Org: selector.Org, Repo: repoName, Revision: selector.Revision, Directory: selector.Directory} discoveredPackages = append(discoveredPackages, thisDiscoveredPackage) } @@ -80,11 +135,11 @@ func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage } func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, error) { - gitRepoSelector := d.config.Git.GitRepoSelector + selector := d.config.Git.Selector httpClient := &http.Client{} - if secretName := gitRepoSelector.SecretRef.Name; secretName != "" { + if secretName := selector.SecretRef.Name; secretName != "" { var repositorySecret coreapi.Secret key := client.ObjectKey{Namespace: d.namespace, Name: secretName} if err := d.client.Get(ctx, key, &repositorySecret); err != nil { @@ -105,14 +160,40 @@ func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, return gitClient, nil } -func filterDirectories(pattern string, directories []string) []string { - filteredDirectories := []string{} +func filterByPattern(pattern string, list []string) []string { + matches := []string{} + + regexPattern := getRegexPattern(pattern) + + for _, value := range list { + if isMatch := match(regexPattern, value); isMatch { + matches = append(matches, value) + } + } + + return matches +} + +func getRegexPattern(pattern string) string { + var result strings.Builder - for _, directory := range directories { - if isMatch, _ := filepath.Match(pattern, directory); isMatch { - filteredDirectories = append(filteredDirectories, directory) + result.WriteString("^") + for i, literal := range strings.Split(pattern, "*") { + if i > 0 { + result.WriteString("[^/]+") } + result.WriteString(regexp.QuoteMeta(literal)) } + result.WriteString("$") + + return result.String() +} + +func match(pattern string, value string) bool { + result, _ := regexp.MatchString(pattern, value) + return result +} - return filteredDirectories +func isSelectorField(value string) bool { + return strings.Contains(value, "*") } diff --git a/rollouts/pkg/packagediscovery/packagediscovery_test.go b/rollouts/pkg/packagediscovery/packagediscovery_test.go index 9adae5281a..0d6f49f84b 100644 --- a/rollouts/pkg/packagediscovery/packagediscovery_test.go +++ b/rollouts/pkg/packagediscovery/packagediscovery_test.go @@ -19,15 +19,35 @@ import ( "testing" ) -var FilterDirectories = filterDirectories +var FilterByPattern = filterByPattern -func TestFilterDirectories(t *testing.T) { +func TestFilterByPatternForRepositories(t *testing.T) { + tests := []struct { + repositories []string + pattern string + want []string + }{ + {repositories: []string{"apps", "policies", "namespaces"}, pattern: "*", want: []string{"apps", "policies", "namespaces"}}, + {repositories: []string{"cluster1-apps", "cluster1-policies", "cluster2-apps", "cluster2-policies"}, pattern: "*-apps", want: []string{"cluster1-apps", "cluster2-apps"}}, + {repositories: []string{"cluster1-apps", "cluster1-policies", "cluster2-apps", "cluster2-policies"}, pattern: "cluster1-*", want: []string{"cluster1-apps", "cluster1-policies"}}, + {repositories: []string{"cluster1-apps", "cluster1-policies", "cluster2-apps", "cluster2-policies"}, pattern: "cluster1-apps", want: []string{"cluster1-apps"}}, + } + + for _, tc := range tests { + got := FilterByPattern(tc.pattern, tc.repositories) + if !reflect.DeepEqual(tc.want, got) { + t.Fatalf("expected: %v, got: %v", tc.want, got) + } + } +} + +func TestFilterByPatternForDirectories(t *testing.T) { tests := []struct { directories []string pattern string want []string }{ - {directories: []string{"dev", "prod", "dev/package-a", "dev/paackage-b", "prod/package-c"}, pattern: "dev/*", want: []string{"dev/package-a", "dev/package-b"}}, + {directories: []string{"dev", "prod", "dev/package-a", "dev/package-b", "prod/package-c"}, pattern: "dev/*", want: []string{"dev/package-a", "dev/package-b"}}, {directories: []string{"package-a", "package-b", "package-a/dev", "package-a/prod", "package-b/dev"}, pattern: "*/dev", want: []string{"package-a/dev", "package-b/dev"}}, {directories: []string{"package-a", "package-b", "package-a/dev", "package-a/prod", "package-b/dev"}, pattern: "package-*/prod", want: []string{"package-a/prod"}}, {directories: []string{"package-a", "package-b", "package-a/dev", "package-a/prod", "package-b/dev"}, pattern: "package-a/dev", want: []string{"package-a/dev"}}, @@ -36,7 +56,7 @@ func TestFilterDirectories(t *testing.T) { } for _, tc := range tests { - got := FilterDirectories(tc.pattern, tc.directories) + got := FilterByPattern(tc.pattern, tc.directories) if !reflect.DeepEqual(tc.want, got) { t.Fatalf("expected: %v, got: %v", tc.want, got) } From b0ddddd0ca693fb937ce261c986c82c5c91c8a2e Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 3 Jan 2023 18:28:35 -0500 Subject: [PATCH 09/38] rollouts: rename packages git source to github (#3708) --- rollouts/api/v1alpha1/rollout_types.go | 18 +++++++---- .../api/v1alpha1/zz_generated.deepcopy.go | 32 +++++++++---------- .../crd/bases/gitops.kpt.dev_rollouts.yaml | 12 +++---- .../samples/gitops_rollout_kpt_samples.yaml | 4 +-- .../config/samples/gitops_rollout_oahu.yaml | 4 +-- .../samples/gitops_rollout_repo_selector.yaml | 4 +-- .../pkg/packagediscovery/packagediscovery.go | 32 +++++++++++-------- 7 files changed, 57 insertions(+), 49 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 0a37bd1cb8..ffc80ada6d 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -52,23 +52,27 @@ type ClusterRef struct { Name string `json:"name"` } -// +kubebuilder:validation:Enum=git +const ( + GitHub PackageSourceType = "GitHub" +) + +// +kubebuilder:validation:Enum=GitHub type PackageSourceType string // PackagesConfig defines the packages the Rollout should deploy. type PackagesConfig struct { SourceType PackageSourceType `json:"sourceType"` - Git GitSource `json:"git"` + GitHub GitHubSource `json:"github"` } -// GitSource defines the packages source in Git. -type GitSource struct { - Selector GitSelector `json:"selector"` +// GitHubSource defines the packages source in Git. +type GitHubSource struct { + Selector GitHubSelector `json:"selector"` } -// GitSelector defines the selector to apply to Git. -type GitSelector struct { +// GitHubSelector defines the selector to apply to Git. +type GitHubSelector struct { Org string `json:"org"` Repo string `json:"repo"` Directory string `json:"directory"` diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index e15442bcfb..c8a87f4fcc 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -78,50 +78,50 @@ func (in *ClusterTargetSelector) DeepCopy() *ClusterTargetSelector { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GitInfo) DeepCopyInto(out *GitInfo) { +func (in *GitHubSelector) DeepCopyInto(out *GitHubSelector) { *out = *in - out.Period = in.Period out.SecretRef = in.SecretRef } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitInfo. -func (in *GitInfo) DeepCopy() *GitInfo { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitHubSelector. +func (in *GitHubSelector) DeepCopy() *GitHubSelector { if in == nil { return nil } - out := new(GitInfo) + out := new(GitHubSelector) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GitSelector) DeepCopyInto(out *GitSelector) { +func (in *GitHubSource) DeepCopyInto(out *GitHubSource) { *out = *in - out.SecretRef = in.SecretRef + out.Selector = in.Selector } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSelector. -func (in *GitSelector) DeepCopy() *GitSelector { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitHubSource. +func (in *GitHubSource) DeepCopy() *GitHubSource { if in == nil { return nil } - out := new(GitSelector) + out := new(GitHubSource) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GitSource) DeepCopyInto(out *GitSource) { +func (in *GitInfo) DeepCopyInto(out *GitInfo) { *out = *in - out.Selector = in.Selector + out.Period = in.Period + out.SecretRef = in.SecretRef } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSource. -func (in *GitSource) DeepCopy() *GitSource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitInfo. +func (in *GitInfo) DeepCopy() *GitInfo { if in == nil { return nil } - out := new(GitSource) + out := new(GitInfo) in.DeepCopyInto(out) return out } @@ -159,7 +159,7 @@ func (in *PackageToClusterMatcher) DeepCopy() *PackageToClusterMatcher { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PackagesConfig) DeepCopyInto(out *PackagesConfig) { *out = *in - out.Git = in.Git + out.GitHub = in.GitHub } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackagesConfig. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index eff2decf17..6b73546f8f 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -55,12 +55,12 @@ spec: packages: description: Packages source for this Rollout. properties: - git: - description: GitSource defines the packages source in Git. + github: + description: GitHubSource defines the packages source in Git. properties: selector: - description: GitSelector defines the selector to apply to - Git. + description: GitHubSelector defines the selector to apply + to Git. properties: directory: type: string @@ -89,10 +89,10 @@ spec: type: object sourceType: enum: - - git + - GitHub type: string required: - - git + - github - sourceType type: object strategy: diff --git a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml index 534136d65f..e3e5b9ffff 100644 --- a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml +++ b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml @@ -19,8 +19,8 @@ metadata: spec: description: kpt samples rollout packages: - sourceType: git - git: + sourceType: GitHub + github: selector: org: GoogleContainerTools repo: kpt-samples diff --git a/rollouts/config/samples/gitops_rollout_oahu.yaml b/rollouts/config/samples/gitops_rollout_oahu.yaml index edaaa236eb..cf1789a57f 100644 --- a/rollouts/config/samples/gitops_rollout_oahu.yaml +++ b/rollouts/config/samples/gitops_rollout_oahu.yaml @@ -19,8 +19,8 @@ metadata: spec: description: oahu rollout packages: - sourceType: git - git: + sourceType: GitHub + github: selector: org: droot repo: oahu diff --git a/rollouts/config/samples/gitops_rollout_repo_selector.yaml b/rollouts/config/samples/gitops_rollout_repo_selector.yaml index cbcd9bf4c2..1e33198bb8 100644 --- a/rollouts/config/samples/gitops_rollout_repo_selector.yaml +++ b/rollouts/config/samples/gitops_rollout_repo_selector.yaml @@ -19,8 +19,8 @@ metadata: spec: description: repo selector rollout packages: - sourceType: git - git: + sourceType: GitHub + github: selector: org: droot repo: "*-apps" diff --git a/rollouts/pkg/packagediscovery/packagediscovery.go b/rollouts/pkg/packagediscovery/packagediscovery.go index 6dd07494d9..f9c3ed08ea 100644 --- a/rollouts/pkg/packagediscovery/packagediscovery.go +++ b/rollouts/pkg/packagediscovery/packagediscovery.go @@ -50,20 +50,24 @@ func NewPackageDiscovery(config gitopsv1alpha1.PackagesConfig, client client.Cli } func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage, error) { - gitClient, err := d.getGitHubClient(ctx) + if d.config.SourceType != gitopsv1alpha1.GitHub { + return nil, fmt.Errorf("%v source type not supported yet", d.config.SourceType) + } + + gitHubClient, err := d.getGitHubClient(ctx) if err != nil { - return nil, fmt.Errorf("unable to create git client: %w", err) + return nil, fmt.Errorf("unable to create github client: %w", err) } discoveredPackages := []DiscoveredPackage{} - repositoryNames, err := d.getRepositoryNames(gitClient, ctx) + repositoryNames, err := d.getRepositoryNames(gitHubClient, ctx) if err != nil { return nil, fmt.Errorf("unable to get repositories: %w", err) } for _, repositoryName := range repositoryNames { - repoPackages, err := d.getPackagesForRepository(gitClient, ctx, repositoryName) + repoPackages, err := d.getPackagesForRepository(gitHubClient, ctx, repositoryName) if err != nil { return nil, fmt.Errorf("unable to get packages: %w", err) } @@ -74,8 +78,8 @@ func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage return discoveredPackages, nil } -func (d *PackageDiscovery) getRepositoryNames(gitClient *github.Client, ctx context.Context) ([]string, error) { - selector := d.config.Git.Selector +func (d *PackageDiscovery) getRepositoryNames(gitHubClient *github.Client, ctx context.Context) ([]string, error) { + selector := d.config.GitHub.Selector repositoryNames := []string{} if isSelectorField(selector.Repo) { @@ -83,7 +87,7 @@ func (d *PackageDiscovery) getRepositoryNames(gitClient *github.Client, ctx cont listOptions := github.RepositoryListOptions{} listOptions.PerPage = 150 - repositories, _, err := gitClient.Repositories.List(ctx, selector.Org, &listOptions) + repositories, _, err := gitHubClient.Repositories.List(ctx, selector.Org, &listOptions) if err != nil { return nil, err } @@ -103,12 +107,12 @@ func (d *PackageDiscovery) getRepositoryNames(gitClient *github.Client, ctx cont return repositoryNames, nil } -func (d *PackageDiscovery) getPackagesForRepository(gitClient *github.Client, ctx context.Context, repoName string) ([]DiscoveredPackage, error) { +func (d *PackageDiscovery) getPackagesForRepository(gitHubClient *github.Client, ctx context.Context, repoName string) ([]DiscoveredPackage, error) { discoveredPackages := []DiscoveredPackage{} - selector := d.config.Git.Selector + selector := d.config.GitHub.Selector if isSelectorField(selector.Directory) { - tree, _, err := gitClient.Git.GetTree(ctx, selector.Org, repoName, selector.Revision, true) + tree, _, err := gitHubClient.Git.GetTree(ctx, selector.Org, repoName, selector.Revision, true) if err != nil { return nil, err } @@ -135,7 +139,7 @@ func (d *PackageDiscovery) getPackagesForRepository(gitClient *github.Client, ct } func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, error) { - selector := d.config.Git.Selector + selector := d.config.GitHub.Selector httpClient := &http.Client{} @@ -143,7 +147,7 @@ func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, var repositorySecret coreapi.Secret key := client.ObjectKey{Namespace: d.namespace, Name: secretName} if err := d.client.Get(ctx, key, &repositorySecret); err != nil { - return nil, fmt.Errorf("cannot retrieve git credentials %s: %v", key, err) + return nil, fmt.Errorf("cannot retrieve github credentials %s: %v", key, err) } accessToken := string(repositorySecret.Data["password"]) @@ -155,9 +159,9 @@ func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, httpClient = oauth2.NewClient(ctx, ts) } - gitClient := github.NewClient(httpClient) + gitHubClient := github.NewClient(httpClient) - return gitClient, nil + return gitHubClient, nil } func filterByPattern(pattern string, list []string) []string { From 5186d270960f73276a2cfb072d5afef3d867abba Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 4 Jan 2023 18:11:00 -0500 Subject: [PATCH 10/38] rollouts: allow the root directory of a repository to be synced (#3709) --- rollouts/api/v1alpha1/rollout_types.go | 2 +- rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml | 1 - rollouts/config/samples/gitops_rollout_oahu.yaml | 2 +- rollouts/controllers/rollout_controller.go | 4 ++++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index ffc80ada6d..77f8bec8fb 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -75,7 +75,7 @@ type GitHubSource struct { type GitHubSelector struct { Org string `json:"org"` Repo string `json:"repo"` - Directory string `json:"directory"` + Directory string `json:"directory,omitempty"` Revision string `json:"revision"` SecretRef SecretReference `json:"secretRef,omitempty"` } diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 6b73546f8f..bf45289204 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -79,7 +79,6 @@ spec: type: string type: object required: - - directory - org - repo - revision diff --git a/rollouts/config/samples/gitops_rollout_oahu.yaml b/rollouts/config/samples/gitops_rollout_oahu.yaml index cf1789a57f..1abff8f4dd 100644 --- a/rollouts/config/samples/gitops_rollout_oahu.yaml +++ b/rollouts/config/samples/gitops_rollout_oahu.yaml @@ -24,7 +24,7 @@ spec: selector: org: droot repo: oahu - directory: namespaces + directory: "/" revision: main packageToTargetMatcher: type: CEL diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 62e14c156a..2cac1f1eb1 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -406,6 +406,10 @@ func toRootSyncSpec(dpkg *packagediscovery.DiscoveredPackage) *gitopsv1alpha1.Ro } func pkgID(dpkg *packagediscovery.DiscoveredPackage) string { + if dpkg.Directory == "" || dpkg.Directory == "." || dpkg.Directory == "/" { + return fmt.Sprintf("%s-%s", dpkg.Org, dpkg.Repo) + } + return fmt.Sprintf("%s-%s-%s", dpkg.Org, dpkg.Repo, dpkg.Directory) } From 7d70e8bba0af722eec4b40eb1813d9482f6800bb Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 4 Jan 2023 19:54:06 -0500 Subject: [PATCH 11/38] rollouts: add caching for discovered packages (#3706) --- rollouts/controllers/rollout_controller.go | 38 +++++++++++-- rollouts/go.mod | 2 +- .../pkg/packagediscovery/packagediscovery.go | 56 +++++++++++++------ 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 2cac1f1eb1..054a93b87a 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -20,11 +20,13 @@ import ( "context" "flag" "fmt" + "sync" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/klog" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -58,6 +60,9 @@ type RolloutReconciler struct { store *clusterstore.ClusterStore Scheme *runtime.Scheme + + mutex sync.Mutex + packageDiscoveryCache map[types.NamespacedName]*packagediscovery.PackageDiscovery } //+kubebuilder:rbac:groups=gitops.kpt.dev,resources=rollouts,verbs=get;list;watch;create;update;patch;delete @@ -105,6 +110,14 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } else { // The object is being deleted if controllerutil.ContainsFinalizer(&rollout, myFinalizerName) { + func() { + r.mutex.Lock() + defer r.mutex.Unlock() + + // clean cache + delete(r.packageDiscoveryCache, req.NamespacedName) + }() + // remove our finalizer from the list and update it. controllerutil.RemoveFinalizer(&rollout, myFinalizerName) if err := r.Update(ctx, &rollout); err != nil { @@ -115,7 +128,9 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } - err := r.reconcileRollout(ctx, &rollout) + packageDiscoveryClient := r.getPackageDiscoveryClient(req.NamespacedName) + + err := r.reconcileRollout(ctx, &rollout, packageDiscoveryClient) if err != nil { return ctrl.Result{}, err } @@ -123,7 +138,20 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } -func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout) error { +func (r *RolloutReconciler) getPackageDiscoveryClient(rolloutNamespacedName types.NamespacedName) *packagediscovery.PackageDiscovery { + r.mutex.Lock() + defer r.mutex.Unlock() + + client, found := r.packageDiscoveryCache[rolloutNamespacedName] + if !found { + client = packagediscovery.NewPackageDiscovery(r.Client, rolloutNamespacedName.Namespace) + r.packageDiscoveryCache[rolloutNamespacedName] = client + } + + return client +} + +func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout, packageDiscoveryClient *packagediscovery.PackageDiscovery) error { logger := log.FromContext(ctx) clusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) @@ -132,9 +160,7 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop } logger.Info("discovered clusters", "count", len(clusters.Items)) - packageDiscoveryClient := packagediscovery.NewPackageDiscovery(rollout.Spec.Packages, r.Client, rollout.Namespace) - - discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx) + discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx, rollout.Spec.Packages) if err != nil { logger.Error(err, "failed to discover packages") return client.IgnoreNotFound(err) @@ -423,6 +449,8 @@ func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { } r.Client = mgr.GetClient() + r.packageDiscoveryCache = make(map[types.NamespacedName]*packagediscovery.PackageDiscovery) + // setup the clusterstore r.store = &clusterstore.ClusterStore{ Config: mgr.GetConfig(), diff --git a/rollouts/go.mod b/rollouts/go.mod index 8b402bd906..e424558d19 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -7,6 +7,7 @@ require ( github.com/GoogleCloudPlatform/k8s-config-connector v1.98.0 github.com/golang/protobuf v1.5.2 github.com/google/cel-go v0.13.0 + github.com/google/go-cmp v0.5.9 github.com/google/go-github/v48 v48.2.0 github.com/onsi/ginkgo/v2 v2.1.6 github.com/onsi/gomega v1.20.1 @@ -46,7 +47,6 @@ require ( github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect diff --git a/rollouts/pkg/packagediscovery/packagediscovery.go b/rollouts/pkg/packagediscovery/packagediscovery.go index f9c3ed08ea..114b555271 100644 --- a/rollouts/pkg/packagediscovery/packagediscovery.go +++ b/rollouts/pkg/packagediscovery/packagediscovery.go @@ -20,8 +20,11 @@ import ( "net/http" "regexp" "strings" + "sync" + "time" gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/google/go-cmp/cmp" "github.com/google/go-github/v48/github" "golang.org/x/oauth2" coreapi "k8s.io/api/core/v1" @@ -29,9 +32,10 @@ import ( ) type PackageDiscovery struct { - config gitopsv1alpha1.PackagesConfig client client.Client namespace string + mutex sync.Mutex + cache *Cache } type DiscoveredPackage struct { @@ -41,33 +45,47 @@ type DiscoveredPackage struct { Revision string } -func NewPackageDiscovery(config gitopsv1alpha1.PackagesConfig, client client.Client, namespace string) *PackageDiscovery { +type Cache struct { + config gitopsv1alpha1.PackagesConfig + packages []DiscoveredPackage + expiration time.Time +} + +func NewPackageDiscovery(client client.Client, namespace string) *PackageDiscovery { return &PackageDiscovery{ - config: config, client: client, namespace: namespace, } } -func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage, error) { - if d.config.SourceType != gitopsv1alpha1.GitHub { - return nil, fmt.Errorf("%v source type not supported yet", d.config.SourceType) +func (d *PackageDiscovery) GetPackages(ctx context.Context, config gitopsv1alpha1.PackagesConfig) ([]DiscoveredPackage, error) { + d.mutex.Lock() + defer d.mutex.Unlock() + + if d.useCache(config) { + return d.cache.packages, nil } - gitHubClient, err := d.getGitHubClient(ctx) + if config.SourceType != gitopsv1alpha1.GitHub { + return nil, fmt.Errorf("%v source type not supported yet", config.SourceType) + } + + gitHubSelector := config.GitHub.Selector + + gitHubClient, err := d.getGitHubClient(ctx, gitHubSelector) if err != nil { return nil, fmt.Errorf("unable to create github client: %w", err) } discoveredPackages := []DiscoveredPackage{} - repositoryNames, err := d.getRepositoryNames(gitHubClient, ctx) + repositoryNames, err := d.getRepositoryNames(gitHubClient, gitHubSelector, ctx) if err != nil { return nil, fmt.Errorf("unable to get repositories: %w", err) } for _, repositoryName := range repositoryNames { - repoPackages, err := d.getPackagesForRepository(gitHubClient, ctx, repositoryName) + repoPackages, err := d.getPackagesForRepository(gitHubClient, ctx, gitHubSelector, repositoryName) if err != nil { return nil, fmt.Errorf("unable to get packages: %w", err) } @@ -75,11 +93,16 @@ func (d *PackageDiscovery) GetPackages(ctx context.Context) ([]DiscoveredPackage discoveredPackages = append(discoveredPackages, repoPackages...) } + d.cache = &Cache{ + packages: discoveredPackages, + config: config, + expiration: time.Now().Add(1 * time.Minute), + } + return discoveredPackages, nil } -func (d *PackageDiscovery) getRepositoryNames(gitHubClient *github.Client, ctx context.Context) ([]string, error) { - selector := d.config.GitHub.Selector +func (d *PackageDiscovery) getRepositoryNames(gitHubClient *github.Client, selector gitopsv1alpha1.GitHubSelector, ctx context.Context) ([]string, error) { repositoryNames := []string{} if isSelectorField(selector.Repo) { @@ -107,9 +130,8 @@ func (d *PackageDiscovery) getRepositoryNames(gitHubClient *github.Client, ctx c return repositoryNames, nil } -func (d *PackageDiscovery) getPackagesForRepository(gitHubClient *github.Client, ctx context.Context, repoName string) ([]DiscoveredPackage, error) { +func (d *PackageDiscovery) getPackagesForRepository(gitHubClient *github.Client, ctx context.Context, selector gitopsv1alpha1.GitHubSelector, repoName string) ([]DiscoveredPackage, error) { discoveredPackages := []DiscoveredPackage{} - selector := d.config.GitHub.Selector if isSelectorField(selector.Directory) { tree, _, err := gitHubClient.Git.GetTree(ctx, selector.Org, repoName, selector.Revision, true) @@ -138,9 +160,7 @@ func (d *PackageDiscovery) getPackagesForRepository(gitHubClient *github.Client, return discoveredPackages, nil } -func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, error) { - selector := d.config.GitHub.Selector - +func (d *PackageDiscovery) getGitHubClient(ctx context.Context, selector gitopsv1alpha1.GitHubSelector) (*github.Client, error) { httpClient := &http.Client{} if secretName := selector.SecretRef.Name; secretName != "" { @@ -164,6 +184,10 @@ func (d *PackageDiscovery) getGitHubClient(ctx context.Context) (*github.Client, return gitHubClient, nil } +func (d *PackageDiscovery) useCache(config gitopsv1alpha1.PackagesConfig) bool { + return d.cache != nil && cmp.Equal(config, d.cache.config) && time.Now().Before(d.cache.expiration) +} + func filterByPattern(pattern string, list []string) []string { matches := []string{} From 64c9b228dd0c95cada221a0dabf67e7299345984 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Fri, 6 Jan 2023 18:40:15 -0500 Subject: [PATCH 12/38] rollouts: add rolling update strategy (#3714) --- rollouts/api/v1alpha1/rollout_types.go | 21 +-- .../api/v1alpha1/zz_generated.deepcopy.go | 14 +- .../crd/bases/gitops.kpt.dev_rollouts.yaml | 11 +- .../config/samples/gitops_rollout_oahu.yaml | 6 +- rollouts/controllers/rollout_controller.go | 123 ++++++++++++++---- 5 files changed, 127 insertions(+), 48 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 77f8bec8fb..fbf49fdce9 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -94,19 +94,19 @@ type PackageToClusterMatcher struct { MatchExpression string `json:"matchExpression"` } -// +kubebuilder:validation:Enum=AllAtOnce;Rolling;Progressive +// +kubebuilder:validation:Enum=AllAtOnce;RollingUpdate;Progressive type StrategyType string const ( - AllAtOnce StrategyType = "AllAtOnce" - Rolling StrategyType = "Rolling" - Progressive StrategyType = "Progressive" + AllAtOnce StrategyType = "AllAtOnce" + RollingUpdate StrategyType = "RollingUpdate" + Progressive StrategyType = "Progressive" ) type StrategyAllAtOnce struct{} -type StrategyRolling struct { - MaxUnavailable int64 `json:"maxUnavailable"` +type StrategyRollingUpdate struct { + MaxConcurrent int64 `json:"maxConcurrent"` } // StrategyProgressive allows staged rollouts @@ -114,10 +114,10 @@ type StrategyRolling struct { type StrategyProgressive struct{} type RolloutStrategy struct { - Type StrategyType `json:"type"` - AllAtOnce *StrategyAllAtOnce `json:"allAtOnce,omitempty"` - Rolling *StrategyRolling `json:"rolling,omitempty"` - Progressive *StrategyProgressive `json:"progressive,omitempty"` + Type StrategyType `json:"type"` + AllAtOnce *StrategyAllAtOnce `json:"allAtOnce,omitempty"` + RollingUpdate *StrategyRollingUpdate `json:"rollingUpdate,omitempty"` + Progressive *StrategyProgressive `json:"progressive,omitempty"` } // RolloutStatus defines the observed state of Rollout @@ -140,6 +140,7 @@ type ClusterStatus struct { type PackageStatus struct { PackageID string `json:"packageId"` SyncStatus string `json:"syncStatus"` + Status string `json:"status"` } //+kubebuilder:object:root=true diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index c8a87f4fcc..de78c1a139 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -387,9 +387,9 @@ func (in *RolloutStrategy) DeepCopyInto(out *RolloutStrategy) { *out = new(StrategyAllAtOnce) **out = **in } - if in.Rolling != nil { - in, out := &in.Rolling, &out.Rolling - *out = new(StrategyRolling) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(StrategyRollingUpdate) **out = **in } if in.Progressive != nil { @@ -495,16 +495,16 @@ func (in *StrategyProgressive) DeepCopy() *StrategyProgressive { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StrategyRolling) DeepCopyInto(out *StrategyRolling) { +func (in *StrategyRollingUpdate) DeepCopyInto(out *StrategyRollingUpdate) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyRolling. -func (in *StrategyRolling) DeepCopy() *StrategyRolling { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyRollingUpdate. +func (in *StrategyRollingUpdate) DeepCopy() *StrategyRollingUpdate { if in == nil { return nil } - out := new(StrategyRolling) + out := new(StrategyRollingUpdate) in.DeepCopyInto(out) return out } diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index bf45289204..3400a508cf 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -105,18 +105,18 @@ spec: the entire rollout will progress through different stages (aka steps, phases or waves). type: object - rolling: + rollingUpdate: properties: - maxUnavailable: + maxConcurrent: format: int64 type: integer required: - - maxUnavailable + - maxConcurrent type: object type: enum: - AllAtOnce - - Rolling + - RollingUpdate - Progressive type: string required: @@ -192,10 +192,13 @@ spec: properties: packageId: type: string + status: + type: string syncStatus: type: string required: - packageId + - status - syncStatus type: object required: diff --git a/rollouts/config/samples/gitops_rollout_oahu.yaml b/rollouts/config/samples/gitops_rollout_oahu.yaml index 1abff8f4dd..de914cd32f 100644 --- a/rollouts/config/samples/gitops_rollout_oahu.yaml +++ b/rollouts/config/samples/gitops_rollout_oahu.yaml @@ -32,6 +32,8 @@ spec: targets: selector: matchLabels: - env: staging + location/state: hawaii strategy: - type: AllAtOnce + type: RollingUpdate + rollingUpdate: + maxConcurrent: 2 diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 054a93b87a..d72e494038 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -20,6 +20,7 @@ import ( "context" "flag" "fmt" + "math" "sync" v1 "k8s.io/api/core/v1" @@ -273,13 +274,25 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, } func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, targets *Targets) ([]gitopsv1alpha1.ClusterStatus, error) { - clusterStatuses := []gitopsv1alpha1.ClusterStatus{} - if rollout.Spec.Strategy.Type != gitopsv1alpha1.AllAtOnce { + if rollout.Spec.Strategy.Type != gitopsv1alpha1.AllAtOnce && rollout.Spec.Strategy.Type != gitopsv1alpha1.RollingUpdate { return clusterStatuses, fmt.Errorf("%v strategy not supported yet", rollout.Spec.Strategy.Type) } + concurrentUpdates := 0 + maxConcurrent := math.MaxInt + + for _, target := range targets.Unchanged { + if !isRRSSynced(target) { + concurrentUpdates++ + } + } + + if rollout.Spec.Strategy.Type == gitopsv1alpha1.RollingUpdate { + maxConcurrent = int(rollout.Spec.Strategy.RollingUpdate.MaxConcurrent) + } + for _, target := range targets.ToBeCreated { rootSyncSpec := toRootSyncSpec(target.packageRef) rrs := newRemoteRootSync(rollout, @@ -287,46 +300,95 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv rootSyncSpec, pkgID(target.packageRef), ) - if err := r.Create(ctx, rrs); err != nil { - klog.Warningf("Error creating RemoteRootSync %s: %v", rrs.Name, err) - return nil, err + + if maxConcurrent > concurrentUpdates { + if err := r.Create(ctx, rrs); err != nil { + klog.Warningf("Error creating RemoteRootSync %s: %v", rrs.Name, err) + return nil, err + } + concurrentUpdates++ + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: rrs.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: rrs.Name, + SyncStatus: rrs.Status.SyncStatus, + Status: "Progressing", + }, + }) + } else { + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: rrs.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: rrs.Name, + SyncStatus: "", + Status: "Waiting", + }, + }) + } - clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ - Name: rrs.Spec.ClusterRef.Name, - PackageStatus: gitopsv1alpha1.PackageStatus{ - PackageID: rrs.Name, - SyncStatus: rrs.Status.SyncStatus, - }, - }) } for _, target := range targets.ToBeUpdated { - if err := r.Update(ctx, target); err != nil { - klog.Warningf("Error updating RemoteRootSync %s: %v", target.Name, err) - return nil, err + if maxConcurrent > concurrentUpdates { + if err := r.Update(ctx, target); err != nil { + klog.Warningf("Error updating RemoteRootSync %s: %v", target.Name, err) + return nil, err + } + concurrentUpdates++ + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: target.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: target.Name, + SyncStatus: target.Status.SyncStatus, + Status: "Progressing", + }, + }) + } else { + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: target.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: target.Name, + SyncStatus: "OutOfSync", + Status: "Waiting", + }, + }) } - clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ - Name: target.Spec.ClusterRef.Name, - PackageStatus: gitopsv1alpha1.PackageStatus{ - PackageID: target.Name, - SyncStatus: target.Status.SyncStatus, - }, - }) } for _, target := range targets.ToBeDeleted { - if err := r.Delete(ctx, target); err != nil { - klog.Warningf("Error deleting RemoteRootSync %s: %v", target.Name, err) - return nil, err + if maxConcurrent > concurrentUpdates { + if err := r.Delete(ctx, target); err != nil { + klog.Warningf("Error deleting RemoteRootSync %s: %v", target.Name, err) + return nil, err + } + concurrentUpdates++ + } else { + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ + Name: target.Spec.ClusterRef.Name, + PackageStatus: gitopsv1alpha1.PackageStatus{ + PackageID: target.Name, + SyncStatus: "OutOfSync", + Status: "Waiting", + }, + }) } } for _, target := range targets.Unchanged { + status := "Progressing" + + if isRRSSynced(target) { + status = "Synced" + } else if isRRSErrored(target) { + status = "Stalled" + } + clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ Name: target.Spec.ClusterRef.Name, PackageStatus: gitopsv1alpha1.PackageStatus{ PackageID: target.Name, SyncStatus: target.Status.SyncStatus, + Status: status, }, }) } @@ -389,6 +451,17 @@ func isRRSSynced(rss *gitopsv1alpha1.RemoteRootSync) bool { return false } +func isRRSErrored(rss *gitopsv1alpha1.RemoteRootSync) bool { + if rss.Generation != rss.Status.ObservedGeneration { + return false + } + + if rss.Status.SyncStatus == "Error" { + return true + } + return false +} + // Given a package identifier and cluster, create a RemoteRootSync object. func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha1.ClusterRef, rssSpec *gitopsv1alpha1.RootSyncSpec, pkgID string) *gitopsv1alpha1.RemoteRootSync { t := true From 3611ff981fdab78d77af5db0e6b13322f54ef527 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Fri, 6 Jan 2023 17:01:21 -0800 Subject: [PATCH 13/38] rollouts: added API for ProgressiveRolloutStrategy (#3716) --- rollouts/PROJECT | 9 ++ .../progressiverolloutstrategy_types.go | 79 ++++++++++ rollouts/api/v1alpha1/rollout_types.go | 11 +- .../api/v1alpha1/zz_generated.deepcopy.go | 112 ++++++++++++++ rollouts/config/crd/bases/_.yaml | 14 -- ....kpt.dev_progressiverolloutstrategies.yaml | 145 ++++++++++++++++++ .../bases/gitops.kpt.dev_remoterootsyncs.yaml | 14 ++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 28 +++- rollouts/config/crd/kustomization.yaml | 3 + ...ction_in_progressiverolloutstrategies.yaml | 21 +++ .../cainjection_in_remoterootsyncs.yaml | 14 ++ ...bhook_in_progressiverolloutstrategies.yaml | 30 ++++ .../patches/webhook_in_remoterootsyncs.yaml | 14 ++ ...rogressiverolloutstrategy_editor_role.yaml | 45 ++++++ ...rogressiverolloutstrategy_viewer_role.yaml | 41 +++++ .../rbac/remoterootsync_editor_role.yaml | 14 ++ .../rbac/remoterootsync_viewer_role.yaml | 14 ++ rollouts/config/rbac/role.yaml | 40 +++++ .../samples/gitops_rollout_cert_manager.yaml | 37 +++++ ...tops_rollout_cert_manager_progressive.yaml | 40 +++++ ...s_v1alpha1_progressiverolloutstrategy.yaml | 32 ++++ .../gitops_v1alpha1_remoterootsync.yaml | 14 ++ .../progressiverolloutstrategy_controller.go | 62 ++++++++ rollouts/controllers/rollout_controller.go | 21 +++ rollouts/main.go | 7 + 25 files changed, 839 insertions(+), 22 deletions(-) create mode 100644 rollouts/api/v1alpha1/progressiverolloutstrategy_types.go delete mode 100644 rollouts/config/crd/bases/_.yaml create mode 100644 rollouts/config/crd/bases/gitops.kpt.dev_progressiverolloutstrategies.yaml create mode 100644 rollouts/config/crd/patches/cainjection_in_progressiverolloutstrategies.yaml create mode 100644 rollouts/config/crd/patches/webhook_in_progressiverolloutstrategies.yaml create mode 100644 rollouts/config/rbac/progressiverolloutstrategy_editor_role.yaml create mode 100644 rollouts/config/rbac/progressiverolloutstrategy_viewer_role.yaml create mode 100644 rollouts/config/samples/gitops_rollout_cert_manager.yaml create mode 100644 rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml create mode 100644 rollouts/config/samples/gitops_v1alpha1_progressiverolloutstrategy.yaml create mode 100644 rollouts/controllers/progressiverolloutstrategy_controller.go diff --git a/rollouts/PROJECT b/rollouts/PROJECT index 8015fa091a..19cd7d0938 100644 --- a/rollouts/PROJECT +++ b/rollouts/PROJECT @@ -22,4 +22,13 @@ resources: kind: RemoteRootSync path: github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kpt.dev + group: gitops + kind: ProgressiveRolloutStrategy + path: github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/rollouts/api/v1alpha1/progressiverolloutstrategy_types.go b/rollouts/api/v1alpha1/progressiverolloutstrategy_types.go new file mode 100644 index 0000000000..b9fa1c2ac5 --- /dev/null +++ b/rollouts/api/v1alpha1/progressiverolloutstrategy_types.go @@ -0,0 +1,79 @@ +/* +Copyright 2023. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ProgressiveRolloutStrategySpec defines the desired state of ProgressiveRolloutStrategy +type ProgressiveRolloutStrategySpec struct { + // Description is a user friendly description of this rollout strategy. + Description string `json:"description,omitempty"` + + // Waves defines an order set of waves of rolling updates. + Waves []Wave `json:"waves"` +} + +// Wave represents a group of rolling updates in a progressive rollout. It is also referred as steps, stages or phases +// of a progressive rollout. +type Wave struct { + // Name identifies the wave. + Name string `json:"name"` + + Description string `json:"description,omitempty"` + + // MaxConcurrent specifies maximum number of concurrent updates to be performed in this wave. + MaxConcurrent int64 `json:"maxConcurrent"` + + // Targets specifies the clusters that are part of this wave. + Targets ClusterTargetSelector `json:"targets,omitempty"` +} + +// ProgressiveRolloutStrategyStatus defines the observed state of ProgressiveRolloutStrategy +type ProgressiveRolloutStrategyStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// ProgressiveRolloutStrategy is the Schema for the progressiverolloutstrategies API +type ProgressiveRolloutStrategy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ProgressiveRolloutStrategySpec `json:"spec,omitempty"` + Status ProgressiveRolloutStrategyStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ProgressiveRolloutStrategyList contains a list of ProgressiveRolloutStrategy +type ProgressiveRolloutStrategyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProgressiveRolloutStrategy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ProgressiveRolloutStrategy{}, &ProgressiveRolloutStrategyList{}) +} diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index fbf49fdce9..7ce9c5becb 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -25,8 +25,6 @@ import ( // RolloutSpec defines the desired state of Rollout type RolloutSpec struct { - // Important: Run "make" to regenerate code after modifying this file - // Description is a user friendly description of this Rollout. Description string `json:"description,omitempty"` @@ -109,9 +107,12 @@ type StrategyRollingUpdate struct { MaxConcurrent int64 `json:"maxConcurrent"` } -// StrategyProgressive allows staged rollouts -// where the entire rollout will progress through different stages (aka steps, phases or waves). -type StrategyProgressive struct{} +// StrategyProgressive defines the progressive rollout strategy to use. +type StrategyProgressive struct { + // Reference of ProgressiveRolloutStrategy to use. + Name string `json:"name"` + Namespace string `json:"namespace"` +} type RolloutStrategy struct { Type StrategyType `json:"type"` diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index de78c1a139..06b0910918 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -172,6 +172,102 @@ func (in *PackagesConfig) DeepCopy() *PackagesConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProgressiveRolloutStrategy) DeepCopyInto(out *ProgressiveRolloutStrategy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProgressiveRolloutStrategy. +func (in *ProgressiveRolloutStrategy) DeepCopy() *ProgressiveRolloutStrategy { + if in == nil { + return nil + } + out := new(ProgressiveRolloutStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProgressiveRolloutStrategy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProgressiveRolloutStrategyList) DeepCopyInto(out *ProgressiveRolloutStrategyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProgressiveRolloutStrategy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProgressiveRolloutStrategyList. +func (in *ProgressiveRolloutStrategyList) DeepCopy() *ProgressiveRolloutStrategyList { + if in == nil { + return nil + } + out := new(ProgressiveRolloutStrategyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProgressiveRolloutStrategyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProgressiveRolloutStrategySpec) DeepCopyInto(out *ProgressiveRolloutStrategySpec) { + *out = *in + if in.Waves != nil { + in, out := &in.Waves, &out.Waves + *out = make([]Wave, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProgressiveRolloutStrategySpec. +func (in *ProgressiveRolloutStrategySpec) DeepCopy() *ProgressiveRolloutStrategySpec { + if in == nil { + return nil + } + out := new(ProgressiveRolloutStrategySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProgressiveRolloutStrategyStatus) DeepCopyInto(out *ProgressiveRolloutStrategyStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProgressiveRolloutStrategyStatus. +func (in *ProgressiveRolloutStrategyStatus) DeepCopy() *ProgressiveRolloutStrategyStatus { + if in == nil { + return nil + } + out := new(ProgressiveRolloutStrategyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemoteRootSync) DeepCopyInto(out *RemoteRootSync) { *out = *in @@ -508,3 +604,19 @@ func (in *StrategyRollingUpdate) DeepCopy() *StrategyRollingUpdate { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Wave) DeepCopyInto(out *Wave) { + *out = *in + in.Targets.DeepCopyInto(&out.Targets) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Wave. +func (in *Wave) DeepCopy() *Wave { + if in == nil { + return nil + } + out := new(Wave) + in.DeepCopyInto(out) + return out +} diff --git a/rollouts/config/crd/bases/_.yaml b/rollouts/config/crd/bases/_.yaml deleted file mode 100644 index 25f6032cf9..0000000000 --- a/rollouts/config/crd/bases/_.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null -spec: - group: "" - names: - kind: "" - plural: "" - scope: "" - versions: null diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_progressiverolloutstrategies.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_progressiverolloutstrategies.yaml new file mode 100644 index 0000000000..6167112dd0 --- /dev/null +++ b/rollouts/config/crd/bases/gitops.kpt.dev_progressiverolloutstrategies.yaml @@ -0,0 +1,145 @@ +# Copyright 2023 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. + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: progressiverolloutstrategies.gitops.kpt.dev +spec: + group: gitops.kpt.dev + names: + kind: ProgressiveRolloutStrategy + listKind: ProgressiveRolloutStrategyList + plural: progressiverolloutstrategies + singular: progressiverolloutstrategy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ProgressiveRolloutStrategy is the Schema for the progressiverolloutstrategies + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ProgressiveRolloutStrategySpec defines the desired state + of ProgressiveRolloutStrategy + properties: + description: + description: Description is a user friendly description of this rollout + strategy. + type: string + waves: + description: Waves defines an order set of waves of rolling updates. + items: + description: Wave represents a group of rolling updates in a progressive + rollout. It is also referred as steps, stages or phases of a progressive + rollout. + properties: + description: + type: string + maxConcurrent: + description: MaxConcurrent specifies maximum number of concurrent + updates to be performed in this wave. + format: int64 + type: integer + name: + description: Name identifies the wave. + type: string + targets: + description: Targets specifies the clusters that are part of + this wave. + properties: + selector: + description: A label selector is a label query over a set + of resources. The result of matchLabels and matchExpressions + are ANDed. An empty label selector matches all objects. + A null label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + required: + - maxConcurrent + - name + type: object + type: array + required: + - waves + type: object + status: + description: ProgressiveRolloutStrategyStatus defines the observed state + of ProgressiveRolloutStrategy + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml index 2a8f382fcd..00a424b235 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_remoterootsyncs.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 3400a508cf..2100cac40b 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -101,9 +115,17 @@ spec: allAtOnce: type: object progressive: - description: StrategyProgressive allows staged rollouts where - the entire rollout will progress through different stages (aka - steps, phases or waves). + description: StrategyProgressive defines the progressive rollout + strategy to use. + properties: + name: + description: Reference of ProgressiveRolloutStrategy to use. + type: string + namespace: + type: string + required: + - name + - namespace type: object rollingUpdate: properties: diff --git a/rollouts/config/crd/kustomization.yaml b/rollouts/config/crd/kustomization.yaml index 03018b6199..2b9931e14c 100644 --- a/rollouts/config/crd/kustomization.yaml +++ b/rollouts/config/crd/kustomization.yaml @@ -18,6 +18,7 @@ resources: - bases/gitops.kpt.dev_rollouts.yaml - bases/gitops.kpt.dev_remoterootsyncs.yaml +- bases/gitops.kpt.dev_progressiverolloutstrategies.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -25,12 +26,14 @@ patchesStrategicMerge: # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_rollouts.yaml #- patches/webhook_in_remoterootsyncs.yaml +#- patches/webhook_in_progressiverolloutstrategies.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_rollouts.yaml #- patches/cainjection_in_remoterootsyncs.yaml +#- patches/cainjection_in_progressiverolloutstrategies.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/rollouts/config/crd/patches/cainjection_in_progressiverolloutstrategies.yaml b/rollouts/config/crd/patches/cainjection_in_progressiverolloutstrategies.yaml new file mode 100644 index 0000000000..85301ee3db --- /dev/null +++ b/rollouts/config/crd/patches/cainjection_in_progressiverolloutstrategies.yaml @@ -0,0 +1,21 @@ +# Copyright 2023 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. + +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: progressiverolloutstrategies.gitops.kpt.dev diff --git a/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml b/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml index 72be600847..4130f70a92 100644 --- a/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml +++ b/rollouts/config/crd/patches/cainjection_in_remoterootsyncs.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + # The following patch adds a directive for certmanager to inject CA into the CRD apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/rollouts/config/crd/patches/webhook_in_progressiverolloutstrategies.yaml b/rollouts/config/crd/patches/webhook_in_progressiverolloutstrategies.yaml new file mode 100644 index 0000000000..3db721765a --- /dev/null +++ b/rollouts/config/crd/patches/webhook_in_progressiverolloutstrategies.yaml @@ -0,0 +1,30 @@ +# Copyright 2023 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. + +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: progressiverolloutstrategies.gitops.kpt.dev +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml b/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml index 5aa8a7b329..8924ef9bac 100644 --- a/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml +++ b/rollouts/config/crd/patches/webhook_in_remoterootsyncs.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + # The following patch enables a conversion webhook for the CRD apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/rollouts/config/rbac/progressiverolloutstrategy_editor_role.yaml b/rollouts/config/rbac/progressiverolloutstrategy_editor_role.yaml new file mode 100644 index 0000000000..62a9604b51 --- /dev/null +++ b/rollouts/config/rbac/progressiverolloutstrategy_editor_role.yaml @@ -0,0 +1,45 @@ +# Copyright 2023 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. + +# permissions for end users to edit progressiverolloutstrategies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: progressiverolloutstrategy-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: progressiverolloutstrategy-editor-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies/status + verbs: + - get diff --git a/rollouts/config/rbac/progressiverolloutstrategy_viewer_role.yaml b/rollouts/config/rbac/progressiverolloutstrategy_viewer_role.yaml new file mode 100644 index 0000000000..9af3a11fdc --- /dev/null +++ b/rollouts/config/rbac/progressiverolloutstrategy_viewer_role.yaml @@ -0,0 +1,41 @@ +# Copyright 2023 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. + +# permissions for end users to view progressiverolloutstrategies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: progressiverolloutstrategy-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: rollouts + app.kubernetes.io/part-of: rollouts + app.kubernetes.io/managed-by: kustomize + name: progressiverolloutstrategy-viewer-role +rules: +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies + verbs: + - get + - list + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies/status + verbs: + - get diff --git a/rollouts/config/rbac/remoterootsync_editor_role.yaml b/rollouts/config/rbac/remoterootsync_editor_role.yaml index 04f3fede88..dd7ecbccc5 100644 --- a/rollouts/config/rbac/remoterootsync_editor_role.yaml +++ b/rollouts/config/rbac/remoterootsync_editor_role.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + # permissions for end users to edit remoterootsyncs. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/rollouts/config/rbac/remoterootsync_viewer_role.yaml b/rollouts/config/rbac/remoterootsync_viewer_role.yaml index a18e939194..172f5bd1d8 100644 --- a/rollouts/config/rbac/remoterootsync_viewer_role.yaml +++ b/rollouts/config/rbac/remoterootsync_viewer_role.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + # permissions for end users to view remoterootsyncs. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/rollouts/config/rbac/role.yaml b/rollouts/config/rbac/role.yaml index 94a5b34df8..4e8b523ee9 100644 --- a/rollouts/config/rbac/role.yaml +++ b/rollouts/config/rbac/role.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -5,6 +19,32 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies/finalizers + verbs: + - update +- apiGroups: + - gitops.kpt.dev + resources: + - progressiverolloutstrategies/status + verbs: + - get + - patch + - update - apiGroups: - gitops.kpt.dev resources: diff --git a/rollouts/config/samples/gitops_rollout_cert_manager.yaml b/rollouts/config/samples/gitops_rollout_cert_manager.yaml new file mode 100644 index 0000000000..212bf60738 --- /dev/null +++ b/rollouts/config/samples/gitops_rollout_cert_manager.yaml @@ -0,0 +1,37 @@ +# 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. + +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + name: cert-manager-rollout +spec: + description: kpt samples rollout + packages: + sourceType: GitHub + github: + selector: + org: droot + repo: kpt-packages + directory: cert-manager + revision: main + targets: + selector: + matchExpressions: + - {key: env, operator: In, values: [dev, staging]} + packageToTargetMatcher: + type: CEL + matchExpression: 'true' + strategy: + type: AllAtOnce diff --git a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml new file mode 100644 index 0000000000..a0f33ea053 --- /dev/null +++ b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml @@ -0,0 +1,40 @@ +# 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. + +apiVersion: gitops.kpt.dev/v1alpha1 +kind: Rollout +metadata: + name: cm-progressive +spec: + description: kpt samples rollout + packages: + sourceType: GitHub + github: + selector: + org: droot + repo: kpt-packages + directory: cert-manager + revision: main + targets: + selector: + matchExpressions: + - {key: env, operator: In, values: [dev, staging]} + packageToTargetMatcher: + type: CEL + matchExpression: 'true' + strategy: + type: Progressive + progressive: + name: cluster-addons-default-rollout + namespace: default diff --git a/rollouts/config/samples/gitops_v1alpha1_progressiverolloutstrategy.yaml b/rollouts/config/samples/gitops_v1alpha1_progressiverolloutstrategy.yaml new file mode 100644 index 0000000000..556572434d --- /dev/null +++ b/rollouts/config/samples/gitops_v1alpha1_progressiverolloutstrategy.yaml @@ -0,0 +1,32 @@ +# Copyright 2023 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. + +apiVersion: gitops.kpt.dev/v1alpha1 +kind: ProgressiveRolloutStrategy +metadata: + name: cluster-addons-default-rollout +spec: + waves: + - name: "dev clusters" + targets: + selector: + matchLabels: + env: dev + maxConcurrent: 2 + - name: "staging clusters" + targets: + selector: + matchLabels: + env: staging + maxConcurrent: 1 diff --git a/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml b/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml index 1622ae4532..a610e0e084 100644 --- a/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml +++ b/rollouts/config/samples/gitops_v1alpha1_remoterootsync.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 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. + apiVersion: gitops.kpt.dev/v1alpha1 kind: RemoteRootSync metadata: diff --git a/rollouts/controllers/progressiverolloutstrategy_controller.go b/rollouts/controllers/progressiverolloutstrategy_controller.go new file mode 100644 index 0000000000..a5516a84e6 --- /dev/null +++ b/rollouts/controllers/progressiverolloutstrategy_controller.go @@ -0,0 +1,62 @@ +/* +Copyright 2022. + +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 controllers + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" +) + +// ProgressiveRolloutStrategyReconciler reconciles a ProgressiveRolloutStrategy object +type ProgressiveRolloutStrategyReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=progressiverolloutstrategies,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=progressiverolloutstrategies/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=gitops.kpt.dev,resources=progressiverolloutstrategies/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the ProgressiveRolloutStrategy object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile +func (r *ProgressiveRolloutStrategyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ProgressiveRolloutStrategyReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&gitopsv1alpha1.ProgressiveRolloutStrategy{}). + Complete(r) +} diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index d72e494038..4b89d9cfd0 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -129,6 +129,27 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } + progressiveStrategy := &gitopsv1alpha1.ProgressiveRolloutStrategy{} + // validate the strategy as early as possible + switch typ := rollout.Spec.Strategy.Type; typ { + case gitopsv1alpha1.AllAtOnce, gitopsv1alpha1.RollingUpdate: + case gitopsv1alpha1.Progressive: + strategyRef := types.NamespacedName{ + Namespace: rollout.Spec.Strategy.Progressive.Namespace, + Name: rollout.Spec.Strategy.Progressive.Name, + } + if err := r.Get(ctx, strategyRef, progressiveStrategy); err != nil { + logger.Error(err, "unable to fetch progressive rollout strategy", "strategyref", strategyRef) + // TODO (droot): signal this as a condition in the rollout status + return ctrl.Result{}, err + } + logger.Info("progressive rollout is not supported yet, so nothing to reconcile here.") + return ctrl.Result{}, nil + default: + // TODO (droot): signal this as a condition in the rollout status + return ctrl.Result{}, fmt.Errorf("%v strategy not supported yet", typ) + } + packageDiscoveryClient := r.getPackageDiscoveryClient(req.NamespacedName) err := r.reconcileRollout(ctx, &rollout, packageDiscoveryClient) diff --git a/rollouts/main.go b/rollouts/main.go index 10fe89f4cf..876736c3dc 100644 --- a/rollouts/main.go +++ b/rollouts/main.go @@ -103,6 +103,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "RemoteRootSync") os.Exit(1) } + if err = (&controllers.ProgressiveRolloutStrategyReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ProgressiveRolloutStrategy") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From 21b5d277fd27ebc0004e11e71e104877b5e3b88d Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Tue, 10 Jan 2023 15:20:41 -0800 Subject: [PATCH 14/38] rollouts: refine package to cluster matcher (#3720) --- rollouts/api/v1alpha1/rollout_types.go | 12 ++++-- .../crd/bases/gitops.kpt.dev_rollouts.yaml | 7 +-- .../samples/gitops_rollout_cert_manager.yaml | 3 +- ...tops_rollout_cert_manager_progressive.yaml | 3 +- .../samples/gitops_rollout_kpt_samples.yaml | 3 +- .../config/samples/gitops_rollout_oahu.yaml | 3 +- .../samples/gitops_rollout_repo_selector.yaml | 2 +- .../packageclustermatcher.go | 43 ++++++++++++------- 8 files changed, 42 insertions(+), 34 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 7ce9c5becb..520a96ab6f 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -36,6 +36,7 @@ type RolloutSpec struct { // PackageToTargetMatcher specifies the clusters that will receive a specific package. PackageToTargetMatcher PackageToClusterMatcher `json:"packageToTargetMatcher"` + // Strategy specifies the rollout strategy to use for this rollout. Strategy RolloutStrategy `json:"strategy"` } @@ -84,12 +85,17 @@ type SecretReference struct { Name string `json:"name,omitempty"` } -// +kubebuilder:validation:Enum=CEL +// +kubebuilder:validation:Enum=AllClusters;Custom type MatcherType string +const ( + MatchAllClusters MatcherType = "AllClusters" + CustomMatcher MatcherType = "Custom" +) + type PackageToClusterMatcher struct { Type MatcherType `json:"type"` - MatchExpression string `json:"matchExpression"` + MatchExpression string `json:"matchExpression,omitempty"` } // +kubebuilder:validation:Enum=AllAtOnce;RollingUpdate;Progressive @@ -123,8 +129,6 @@ type RolloutStrategy struct { // RolloutStatus defines the observed state of Rollout type RolloutStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file ObservedGeneration int64 `json:"observedGeneration,omitempty"` // Conditions describes the reconciliation state of the object. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 2100cac40b..4a9ca6ed3e 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -60,10 +60,10 @@ spec: type: string type: enum: - - CEL + - AllClusters + - Custom type: string required: - - matchExpression - type type: object packages: @@ -299,9 +299,6 @@ spec: type: object type: array observedGeneration: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state - of cluster Important: Run "make" to regenerate code after modifying - this file' format: int64 type: integer type: object diff --git a/rollouts/config/samples/gitops_rollout_cert_manager.yaml b/rollouts/config/samples/gitops_rollout_cert_manager.yaml index 212bf60738..f08a757c48 100644 --- a/rollouts/config/samples/gitops_rollout_cert_manager.yaml +++ b/rollouts/config/samples/gitops_rollout_cert_manager.yaml @@ -31,7 +31,6 @@ spec: matchExpressions: - {key: env, operator: In, values: [dev, staging]} packageToTargetMatcher: - type: CEL - matchExpression: 'true' + type: AllClusters strategy: type: AllAtOnce diff --git a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml index a0f33ea053..396a8b067d 100644 --- a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml +++ b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml @@ -31,8 +31,7 @@ spec: matchExpressions: - {key: env, operator: In, values: [dev, staging]} packageToTargetMatcher: - type: CEL - matchExpression: 'true' + type: AllClusters strategy: type: Progressive progressive: diff --git a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml index e3e5b9ffff..fa65e4166b 100644 --- a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml +++ b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml @@ -31,7 +31,6 @@ spec: matchExpressions: - {key: location/island, operator: In, values: [oahu, maui]} packageToTargetMatcher: - type: CEL - matchExpression: 'true' + type: AllClusters strategy: type: AllAtOnce diff --git a/rollouts/config/samples/gitops_rollout_oahu.yaml b/rollouts/config/samples/gitops_rollout_oahu.yaml index de914cd32f..38445aa61b 100644 --- a/rollouts/config/samples/gitops_rollout_oahu.yaml +++ b/rollouts/config/samples/gitops_rollout_oahu.yaml @@ -27,8 +27,7 @@ spec: directory: "/" revision: main packageToTargetMatcher: - type: CEL - matchExpression: 'true' + type: AllClusters targets: selector: matchLabels: diff --git a/rollouts/config/samples/gitops_rollout_repo_selector.yaml b/rollouts/config/samples/gitops_rollout_repo_selector.yaml index 1e33198bb8..02bc875908 100644 --- a/rollouts/config/samples/gitops_rollout_repo_selector.yaml +++ b/rollouts/config/samples/gitops_rollout_repo_selector.yaml @@ -31,7 +31,7 @@ spec: matchExpressions: - {key: location/state, operator: In, values: [hawaii]} packageToTargetMatcher: - type: CEL + type: Custom matchExpression: rolloutPackage.repo == cluster.labels['location/island'] + '-apps' strategy: type: AllAtOnce diff --git a/rollouts/pkg/packageclustermatcher/packageclustermatcher.go b/rollouts/pkg/packageclustermatcher/packageclustermatcher.go index dc81cb1306..57a189707f 100644 --- a/rollouts/pkg/packageclustermatcher/packageclustermatcher.go +++ b/rollouts/pkg/packageclustermatcher/packageclustermatcher.go @@ -51,26 +51,37 @@ func (m *PackageClusterMatcher) GetClusterPackages(matcher gitopsv1alpha1.Packag for _, cluster := range clusters { matchedPackages := []packagediscovery.DiscoveredPackage{} - celCluster := map[string]interface{}{ - "name": cluster.ObjectMeta.Name, - "labels": cluster.ObjectMeta.Labels, - } - - for _, discoveredPackage := range packages { - celPackage := map[string]interface{}{ - "org": discoveredPackage.Org, - "repo": discoveredPackage.Repo, - "directory": discoveredPackage.Directory, + switch matcherType := matcher.Type; matcherType { + case gitopsv1alpha1.MatchAllClusters: + matchedPackages = packages + case gitopsv1alpha1.CustomMatcher: + celCluster := map[string]interface{}{ + "name": cluster.ObjectMeta.Name, + "labels": cluster.ObjectMeta.Labels, } - isMatch, err := isPackageClusterMatch(matcher.MatchExpression, celCluster, celPackage) - if err != nil { - return nil, fmt.Errorf("unable to execute package cluster matcher expression: %w", err) + for _, discoveredPackage := range packages { + celPackage := map[string]interface{}{ + "org": discoveredPackage.Org, + "repo": discoveredPackage.Repo, + "directory": discoveredPackage.Directory, + } + + isMatch, err := isPackageClusterMatch(matcher.MatchExpression, celCluster, celPackage) + if err != nil { + return nil, fmt.Errorf("unable to execute package cluster matcher expression: %w", err) + } + + if isMatch { + matchedPackages = append(matchedPackages, discoveredPackage) + } } + default: + return nil, fmt.Errorf("%v matcher is not supported", matcherType) + } - if isMatch { - matchedPackages = append(matchedPackages, discoveredPackage) - } + if len(matchedPackages) > 1 { + return nil, fmt.Errorf("more than one package rollout is not supported yet. Found %d packages for cluster %s", len(matchedPackages), cluster.Name) } clusterPackages := ClusterPackages{ From 24d5f618ccebb9d697ba39ffbbafe4ed8cf06a31 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 10 Jan 2023 18:33:48 -0500 Subject: [PATCH 15/38] rollouts: implement progressive strategy (#3719) --- .../samples/container_cluster_catalina.yaml | 29 +++ .../samples/container_cluster_kauai.yaml | 29 +++ rollouts/controllers/rollout_controller.go | 199 +++++++++++++----- rollouts/pkg/clusterstore/clusterstore.go | 52 ++++- 4 files changed, 242 insertions(+), 67 deletions(-) create mode 100644 rollouts/config/samples/container_cluster_catalina.yaml create mode 100644 rollouts/config/samples/container_cluster_kauai.yaml diff --git a/rollouts/config/samples/container_cluster_catalina.yaml b/rollouts/config/samples/container_cluster_catalina.yaml new file mode 100644 index 0000000000..58bfd80222 --- /dev/null +++ b/rollouts/config/samples/container_cluster_catalina.yaml @@ -0,0 +1,29 @@ +# Copyright 2023 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. + +apiVersion: container.cnrm.cloud.google.com/v1beta1 +kind: ContainerCluster +metadata: + name: catalina-dev + namespace: config-control + labels: + env: dev + location/island: catalina + location/state: california +spec: + description: Catalina dev autopilot cluster + enableAutopilot: true + location: us-central1 + releaseChannel: + channel: REGULAR diff --git a/rollouts/config/samples/container_cluster_kauai.yaml b/rollouts/config/samples/container_cluster_kauai.yaml new file mode 100644 index 0000000000..6da653402d --- /dev/null +++ b/rollouts/config/samples/container_cluster_kauai.yaml @@ -0,0 +1,29 @@ +# Copyright 2023 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. + +apiVersion: container.cnrm.cloud.google.com/v1beta1 +kind: ContainerCluster +metadata: + name: kauai + namespace: config-control + labels: + env: dev + location/island: kauai + location/state: hawaii +spec: + description: Kauai autopilot cluster + enableAutopilot: true + location: us-central1 + releaseChannel: + channel: REGULAR diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 4b89d9cfd0..1585efc20a 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -129,35 +129,107 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } - progressiveStrategy := &gitopsv1alpha1.ProgressiveRolloutStrategy{} + strategy, err := r.getStrategy(ctx, &rollout) + if err != nil { + return ctrl.Result{}, err + } + + packageDiscoveryClient := r.getPackageDiscoveryClient(req.NamespacedName) + + err = r.reconcileRollout(ctx, &rollout, strategy, packageDiscoveryClient) + if err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +func (r *RolloutReconciler) getStrategy(ctx context.Context, rollout *gitopsv1alpha1.Rollout) (*gitopsv1alpha1.ProgressiveRolloutStrategy, error) { + logger := log.FromContext(ctx) + + progressiveStrategy := gitopsv1alpha1.ProgressiveRolloutStrategy{} + progressiveStrategy.Spec = gitopsv1alpha1.ProgressiveRolloutStrategySpec{Waves: []gitopsv1alpha1.Wave{}} + // validate the strategy as early as possible switch typ := rollout.Spec.Strategy.Type; typ { - case gitopsv1alpha1.AllAtOnce, gitopsv1alpha1.RollingUpdate: + case gitopsv1alpha1.AllAtOnce: + wave := gitopsv1alpha1.Wave{MaxConcurrent: math.MaxInt, Targets: rollout.Spec.Targets} + progressiveStrategy.Spec.Waves = append(progressiveStrategy.Spec.Waves, wave) + + case gitopsv1alpha1.RollingUpdate: + wave := gitopsv1alpha1.Wave{MaxConcurrent: rollout.Spec.Strategy.RollingUpdate.MaxConcurrent, Targets: rollout.Spec.Targets} + progressiveStrategy.Spec.Waves = append(progressiveStrategy.Spec.Waves, wave) + case gitopsv1alpha1.Progressive: strategyRef := types.NamespacedName{ Namespace: rollout.Spec.Strategy.Progressive.Namespace, Name: rollout.Spec.Strategy.Progressive.Name, } - if err := r.Get(ctx, strategyRef, progressiveStrategy); err != nil { + if err := r.Get(ctx, strategyRef, &progressiveStrategy); err != nil { logger.Error(err, "unable to fetch progressive rollout strategy", "strategyref", strategyRef) // TODO (droot): signal this as a condition in the rollout status - return ctrl.Result{}, err + return nil, err } - logger.Info("progressive rollout is not supported yet, so nothing to reconcile here.") - return ctrl.Result{}, nil + + err := r.validateProgressiveRolloutStrategy(ctx, rollout, &progressiveStrategy) + if err != nil { + logger.Error(err, "progressive rollout strategy failed validation", "strategyref", strategyRef) + // TODO (cfry): signal this as a condition in the rollout status + return nil, err + } + default: // TODO (droot): signal this as a condition in the rollout status - return ctrl.Result{}, fmt.Errorf("%v strategy not supported yet", typ) + return nil, fmt.Errorf("%v strategy not supported yet", typ) } - packageDiscoveryClient := r.getPackageDiscoveryClient(req.NamespacedName) + return &progressiveStrategy, nil +} - err := r.reconcileRollout(ctx, &rollout, packageDiscoveryClient) +func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Context, rollout *gitopsv1alpha1.Rollout, strategy *gitopsv1alpha1.ProgressiveRolloutStrategy) error { + allClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) if err != nil { - return ctrl.Result{}, err + return err } - return ctrl.Result{}, nil + clusterWaveMap := make(map[string]int) + for _, cluster := range allClusters.Items { + clusterWaveMap[cluster.Name] = -1 + } + + for waveIdx, wave := range strategy.Spec.Waves { + waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) + if err != nil { + return err + } + + if len(waveClusters.Items) == 0 { + return fmt.Errorf("wave %d does not target any clusters", waveIdx) + } + + for _, cluster := range waveClusters.Items { + currentClusterWave, found := clusterWaveMap[cluster.Name] + if !found { + // this should never happen + return fmt.Errorf("wave %d references cluster %s not selected by the rollout", waveIdx, cluster.Name) + } + + if currentClusterWave > -1 { + return fmt.Errorf("a cluster cannot be selected by more than one wave - cluster %s is selected by waves %d and %d", cluster.Name, currentClusterWave, waveIdx) + } + + clusterWaveMap[cluster.Name] = waveIdx + } + } + + for _, cluster := range allClusters.Items { + wave, _ := clusterWaveMap[cluster.Name] + if wave == -1 { + return fmt.Errorf("waves should cover all clusters selected by the rollout - cluster %s is not covered by any waves", cluster.Name) + } + } + + return nil } func (r *RolloutReconciler) getPackageDiscoveryClient(rolloutNamespacedName types.NamespacedName) *packagediscovery.PackageDiscovery { @@ -173,15 +245,9 @@ func (r *RolloutReconciler) getPackageDiscoveryClient(rolloutNamespacedName type return client } -func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout, packageDiscoveryClient *packagediscovery.PackageDiscovery) error { +func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout, strategy *gitopsv1alpha1.ProgressiveRolloutStrategy, packageDiscoveryClient *packagediscovery.PackageDiscovery) error { logger := log.FromContext(ctx) - clusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) - if err != nil { - return err - } - logger.Info("discovered clusters", "count", len(clusters.Items)) - discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx, rollout.Spec.Packages) if err != nil { logger.Error(err, "failed to discover packages") @@ -189,30 +255,46 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop } logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages) - packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(clusters.Items, discoveredPackages) + allClusterStatuses := []gitopsv1alpha1.ClusterStatus{} - allClusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) - if err != nil { - logger.Error(err, "get cluster packages failed") - return client.IgnoreNotFound(err) - } + waveInProgress := false - for _, clusterPackages := range allClusterPackages { - clusterName := clusterPackages.Cluster.Name - logger.Info("cluster packages", "cluster", clusterName, "packagesCount", len(clusterPackages.Packages), "packages", clusterPackages.Packages) - } + for _, wave := range strategy.Spec.Waves { + waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) + if err != nil { + return err + } - targets, err := r.computeTargets(ctx, rollout, allClusterPackages) - if err != nil { - return err - } + packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(waveClusters.Items, discoveredPackages) + allClusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) + if err != nil { + logger.Error(err, "get cluster packages failed") + return client.IgnoreNotFound(err) + } - clusterStatuses, err := r.rolloutTargets(ctx, rollout, targets) - if err != nil { - return err + for _, clusterPackages := range allClusterPackages { + clusterName := clusterPackages.Cluster.Name + logger.Info("cluster packages", "cluster", clusterName, "packagesCount", len(clusterPackages.Packages), "packages", clusterPackages.Packages) + } + + targets, err := r.computeTargets(ctx, rollout, allClusterPackages, waveClusters.Items) + if err != nil { + return err + } + + thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, &wave, targets, waveInProgress) + if err != nil { + return err + } + + if thisWaveInProgress { + waveInProgress = true + } + + allClusterStatuses = append(allClusterStatuses, clusterStatuses...) } - if err := r.updateStatus(ctx, rollout, clusterStatuses); err != nil { + if err := r.updateStatus(ctx, rollout, allClusterStatuses); err != nil { return err } return nil @@ -234,7 +316,7 @@ For RRS, make rootSyncTemplate */ func (r *RolloutReconciler) computeTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, - clusterPackages []packageclustermatcher.ClusterPackages) (*Targets, error) { + clusterPackages []packageclustermatcher.ClusterPackages, allowClusters []gkeclusterapis.ContainerCluster) (*Targets, error) { RRSkeysToBeDeleted := map[client.ObjectKey]*gitopsv1alpha1.RemoteRootSync{} // let's take a look at existing remoterootsyncs @@ -242,10 +324,16 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, if err != nil { return nil, err } + // initially assume all the keys to be deleted for _, rrs := range existingRRSs { - RRSkeysToBeDeleted[client.ObjectKeyFromObject(rrs)] = rrs + for _, cluster := range allowClusters { + if rrs.Spec.ClusterRef.Name == cluster.Name { + RRSkeysToBeDeleted[client.ObjectKeyFromObject(rrs)] = rrs + } + } } + klog.Infof("Found remoterootsyncs: %s", toRemoteRootSyncNames(existingRRSs)) targets := &Targets{} // track keys of all the desired remote rootsyncs @@ -294,15 +382,17 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, return targets, nil } -func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, targets *Targets) ([]gitopsv1alpha1.ClusterStatus, error) { +func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, wave *gitopsv1alpha1.Wave, targets *Targets, previousWaveAlreadyInProgress bool) (bool, []gitopsv1alpha1.ClusterStatus, error) { clusterStatuses := []gitopsv1alpha1.ClusterStatus{} - if rollout.Spec.Strategy.Type != gitopsv1alpha1.AllAtOnce && rollout.Spec.Strategy.Type != gitopsv1alpha1.RollingUpdate { - return clusterStatuses, fmt.Errorf("%v strategy not supported yet", rollout.Spec.Strategy.Type) - } - concurrentUpdates := 0 - maxConcurrent := math.MaxInt + maxConcurrent := int(wave.MaxConcurrent) + waiting := "Waiting" + + if previousWaveAlreadyInProgress { + maxConcurrent = 0 + waiting = "Waiting (Upcoming Wave)" + } for _, target := range targets.Unchanged { if !isRRSSynced(target) { @@ -310,10 +400,6 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv } } - if rollout.Spec.Strategy.Type == gitopsv1alpha1.RollingUpdate { - maxConcurrent = int(rollout.Spec.Strategy.RollingUpdate.MaxConcurrent) - } - for _, target := range targets.ToBeCreated { rootSyncSpec := toRootSyncSpec(target.packageRef) rrs := newRemoteRootSync(rollout, @@ -325,7 +411,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv if maxConcurrent > concurrentUpdates { if err := r.Create(ctx, rrs); err != nil { klog.Warningf("Error creating RemoteRootSync %s: %v", rrs.Name, err) - return nil, err + return false, nil, err } concurrentUpdates++ clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ @@ -342,10 +428,9 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv PackageStatus: gitopsv1alpha1.PackageStatus{ PackageID: rrs.Name, SyncStatus: "", - Status: "Waiting", + Status: waiting, }, }) - } } @@ -353,7 +438,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv if maxConcurrent > concurrentUpdates { if err := r.Update(ctx, target); err != nil { klog.Warningf("Error updating RemoteRootSync %s: %v", target.Name, err) - return nil, err + return false, nil, err } concurrentUpdates++ clusterStatuses = append(clusterStatuses, gitopsv1alpha1.ClusterStatus{ @@ -370,7 +455,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv PackageStatus: gitopsv1alpha1.PackageStatus{ PackageID: target.Name, SyncStatus: "OutOfSync", - Status: "Waiting", + Status: waiting, }, }) } @@ -380,7 +465,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv if maxConcurrent > concurrentUpdates { if err := r.Delete(ctx, target); err != nil { klog.Warningf("Error deleting RemoteRootSync %s: %v", target.Name, err) - return nil, err + return false, nil, err } concurrentUpdates++ } else { @@ -389,7 +474,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv PackageStatus: gitopsv1alpha1.PackageStatus{ PackageID: target.Name, SyncStatus: "OutOfSync", - Status: "Waiting", + Status: waiting, }, }) } @@ -414,7 +499,9 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv }) } - return clusterStatuses, nil + thisWaveInProgress := concurrentUpdates > 0 + + return thisWaveInProgress, clusterStatuses, nil } type Targets struct { diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index 2f748798c0..d509b383d1 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -48,22 +48,30 @@ func (cs *ClusterStore) Init() error { return nil } -func (cs *ClusterStore) ListClusters(ctx context.Context, selector *metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { - gkeClusters := &gkeclusterapis.ContainerClusterList{} +func (cs *ClusterStore) ListClusters(ctx context.Context, selectors ...*metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { + gkeClusters, err := cs.listClusters(ctx, selectors[0]) + if err != nil { + return nil, err + } - var opts []client.ListOption - if selector != nil { - selector, err := metav1.LabelSelectorAsSelector(selector) + for _, selector := range selectors[1:] { + selectorClusters, err := cs.listClusters(ctx, selector) if err != nil { return nil, err } - opts = append(opts, client.MatchingLabelsSelector{Selector: selector}) - } - // TODO: make it configurable ? - opts = append(opts, client.InNamespace("config-control")) - if err := cs.List(ctx, gkeClusters, opts...); err != nil { - return nil, err + intersection := []gkeclusterapis.ContainerCluster{} + + for _, cluster := range gkeClusters.Items { + for _, selectorCluster := range selectorClusters.Items { + if cluster.Name == selectorCluster.Name { + intersection = append(intersection, cluster) + break + } + } + } + + gkeClusters.Items = intersection } return gkeClusters, nil @@ -108,6 +116,28 @@ func (cs *ClusterStore) GetClusterClient(ctx context.Context, cluster *gkecluste return cl, dynCl, err } +func (cs *ClusterStore) listClusters(ctx context.Context, selector *metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { + gkeClusters := &gkeclusterapis.ContainerClusterList{} + + var opts []client.ListOption + + if selector != nil { + selector, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return nil, err + } + opts = append(opts, client.MatchingLabelsSelector{Selector: selector}) + } + + // TODO: make it configurable ? + opts = append(opts, client.InNamespace("config-control")) + if err := cs.List(ctx, gkeClusters, opts...); err != nil { + return nil, err + } + + return gkeClusters, nil +} + func (cs *ClusterStore) getRESTConfig(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (*rest.Config, error) { logger := log.FromContext(ctx) restConfig := &rest.Config{} From 17c121d1be36ebeb1894c6074b5f8238384ca845 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 11 Jan 2023 16:50:32 -0500 Subject: [PATCH 16/38] rollouts: update progressive strategy to pause after wave (#3721) --- rollouts/api/v1alpha1/rollout_types.go | 14 +++++- .../api/v1alpha1/zz_generated.deepcopy.go | 16 +++++++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 15 +++++- ...tops_rollout_cert_manager_progressive.yaml | 2 + rollouts/controllers/rollout_controller.go | 48 +++++++++++++------ 5 files changed, 77 insertions(+), 18 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 520a96ab6f..ff2c12a4a7 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -115,9 +115,19 @@ type StrategyRollingUpdate struct { // StrategyProgressive defines the progressive rollout strategy to use. type StrategyProgressive struct { - // Reference of ProgressiveRolloutStrategy to use. - Name string `json:"name"` + // Name of the ProgressiveRolloutStrategy to use. + Name string `json:"name"` + + // Namespace of the ProgressiveRolloutStrategy to use. Namespace string `json:"namespace"` + + // PauseAfterWave represents the highest wave the strategy will deploy. + PauseAfterWave PauseAfterWave `json:"pauseAfterWave,omitempty"` +} + +type PauseAfterWave struct { + // WaveName represents name of the wave defined in the ProgressiveRolloutStrategy. + WaveName string `json:"waveName"` } type RolloutStrategy struct { diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 06b0910918..09bc6b1992 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -172,6 +172,21 @@ func (in *PackagesConfig) DeepCopy() *PackagesConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PauseAfterWave) DeepCopyInto(out *PauseAfterWave) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PauseAfterWave. +func (in *PauseAfterWave) DeepCopy() *PauseAfterWave { + if in == nil { + return nil + } + out := new(PauseAfterWave) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProgressiveRolloutStrategy) DeepCopyInto(out *ProgressiveRolloutStrategy) { *out = *in @@ -578,6 +593,7 @@ func (in *StrategyAllAtOnce) DeepCopy() *StrategyAllAtOnce { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StrategyProgressive) DeepCopyInto(out *StrategyProgressive) { *out = *in + out.PauseAfterWave = in.PauseAfterWave } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyProgressive. diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 4a9ca6ed3e..57872720ee 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -119,10 +119,23 @@ spec: strategy to use. properties: name: - description: Reference of ProgressiveRolloutStrategy to use. + description: Name of the ProgressiveRolloutStrategy to use. type: string namespace: + description: Namespace of the ProgressiveRolloutStrategy to + use. type: string + pauseAfterWave: + description: PauseAfterWave represents the highest wave the + strategy will deploy. + properties: + waveName: + description: WaveName represents name of the wave defined + in the ProgressiveRolloutStrategy. + type: string + required: + - waveName + type: object required: - name - namespace diff --git a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml index 396a8b067d..7f16e254b7 100644 --- a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml +++ b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml @@ -37,3 +37,5 @@ spec: progressive: name: cluster-addons-default-rollout namespace: default + pauseAfterWave: + waveName: dev clusters diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 1585efc20a..91c349f008 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -192,43 +192,56 @@ func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Conte return err } - clusterWaveMap := make(map[string]int) + clusterWaveMap := make(map[string]string) for _, cluster := range allClusters.Items { - clusterWaveMap[cluster.Name] = -1 + clusterWaveMap[cluster.Name] = "" } - for waveIdx, wave := range strategy.Spec.Waves { + pauseAfterWaveName := "" + pauseWaveNameFound := false + + if rollout.Spec.Strategy.Progressive != nil { + pauseAfterWaveName = rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName + } + + for _, wave := range strategy.Spec.Waves { waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) if err != nil { return err } if len(waveClusters.Items) == 0 { - return fmt.Errorf("wave %d does not target any clusters", waveIdx) + return fmt.Errorf("wave %q does not target any clusters", wave.Name) } for _, cluster := range waveClusters.Items { currentClusterWave, found := clusterWaveMap[cluster.Name] if !found { // this should never happen - return fmt.Errorf("wave %d references cluster %s not selected by the rollout", waveIdx, cluster.Name) + return fmt.Errorf("wave %q references cluster %s not selected by the rollout", wave.Name, cluster.Name) } - if currentClusterWave > -1 { - return fmt.Errorf("a cluster cannot be selected by more than one wave - cluster %s is selected by waves %d and %d", cluster.Name, currentClusterWave, waveIdx) + if currentClusterWave != "" { + return fmt.Errorf("a cluster cannot be selected by more than one wave - cluster %s is selected by waves %q and %q", cluster.Name, currentClusterWave, wave.Name) } - clusterWaveMap[cluster.Name] = waveIdx + clusterWaveMap[cluster.Name] = wave.Name } + + pauseWaveNameFound = pauseWaveNameFound || pauseAfterWaveName == wave.Name } for _, cluster := range allClusters.Items { wave, _ := clusterWaveMap[cluster.Name] - if wave == -1 { + if wave == "" { return fmt.Errorf("waves should cover all clusters selected by the rollout - cluster %s is not covered by any waves", cluster.Name) } } + if pauseAfterWaveName != "" && !pauseWaveNameFound { + return fmt.Errorf("%q pause wave not found in progressive rollout strategy", pauseAfterWaveName) + } + return nil } @@ -257,7 +270,12 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop allClusterStatuses := []gitopsv1alpha1.ClusterStatus{} - waveInProgress := false + pauseFutureWaves := false + pauseAfterWaveName := "" + + if rollout.Spec.Strategy.Progressive != nil { + pauseAfterWaveName = rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName + } for _, wave := range strategy.Spec.Waves { waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) @@ -282,13 +300,13 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop return err } - thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, &wave, targets, waveInProgress) + thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, &wave, targets, pauseFutureWaves) if err != nil { return err } - if thisWaveInProgress { - waveInProgress = true + if thisWaveInProgress || wave.Name == pauseAfterWaveName { + pauseFutureWaves = true } allClusterStatuses = append(allClusterStatuses, clusterStatuses...) @@ -382,14 +400,14 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, return targets, nil } -func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, wave *gitopsv1alpha1.Wave, targets *Targets, previousWaveAlreadyInProgress bool) (bool, []gitopsv1alpha1.ClusterStatus, error) { +func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, wave *gitopsv1alpha1.Wave, targets *Targets, pauseWave bool) (bool, []gitopsv1alpha1.ClusterStatus, error) { clusterStatuses := []gitopsv1alpha1.ClusterStatus{} concurrentUpdates := 0 maxConcurrent := int(wave.MaxConcurrent) waiting := "Waiting" - if previousWaveAlreadyInProgress { + if pauseWave { maxConcurrent = 0 waiting = "Waiting (Upcoming Wave)" } From 9b66acd8d7d878a90edb54dcad942fa7ddc76f35 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Wed, 11 Jan 2023 14:26:21 -0800 Subject: [PATCH 17/38] rollouts: added skeleton CLI (#3724) * rollouts: added skeleton CLI * added table display --- rollouts/Makefile | 5 ++ rollouts/cli/get/get.go | 66 ++++++++++++++++++++++ rollouts/cli/main.go | 53 ++++++++++++++++++ rollouts/cli/status/status.go | 86 +++++++++++++++++++++++++++++ rollouts/go.mod | 3 + rollouts/go.sum | 8 +++ rollouts/rolloutsclient/client.go | 92 +++++++++++++++++++++++++++++++ 7 files changed, 313 insertions(+) create mode 100644 rollouts/cli/get/get.go create mode 100644 rollouts/cli/main.go create mode 100644 rollouts/cli/status/status.go create mode 100644 rollouts/rolloutsclient/client.go diff --git a/rollouts/Makefile b/rollouts/Makefile index 2cc041386e..b1f1bdc474 100644 --- a/rollouts/Makefile +++ b/rollouts/Makefile @@ -82,6 +82,11 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go + +.PHONY: cli +cli: + go build -o ./bin/rollouts ./cli + # If you wish built the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ diff --git a/rollouts/cli/get/get.go b/rollouts/cli/get/get.go new file mode 100644 index 0000000000..d6cfd0253e --- /dev/null +++ b/rollouts/cli/get/get.go @@ -0,0 +1,66 @@ +// Copyright 2023 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 get + +import ( + "context" + "fmt" + + "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" + "github.com/spf13/cobra" +) + +func NewCommand(ctx context.Context) *cobra.Command { + return newRunner(ctx).Command +} + +func newRunner(ctx context.Context) *runner { + r := &runner{ + ctx: ctx, + } + c := &cobra.Command{ + Use: "get", + Short: "lists rollouts", + Long: "lists rollouts", + Example: "lists rollouts", + RunE: r.runE, + } + r.Command = c + return r +} + +type runner struct { + ctx context.Context + Command *cobra.Command +} + +func (r *runner) runE(cmd *cobra.Command, args []string) error { + rlc, err := rolloutsclient.New() + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + rollouts, err := rlc.List(r.ctx, "") + if err != nil { + fmt.Printf("%s\n", err) + return err + } + for _, rollout := range rollouts.Items { + fmt.Printf("%s\n", rollout.Name) + } + + return nil +} diff --git a/rollouts/cli/main.go b/rollouts/cli/main.go new file mode 100644 index 0000000000..b6eff2e499 --- /dev/null +++ b/rollouts/cli/main.go @@ -0,0 +1,53 @@ +// 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 ( + "context" + "os" + + "github.com/GoogleContainerTools/kpt/rollouts/cli/get" + "github.com/GoogleContainerTools/kpt/rollouts/cli/status" + "github.com/spf13/cobra" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +func main() { + ctx := context.Background() + + rolloutsCmd := &cobra.Command{ + Use: "cli", + Short: "cli ", + Long: "cli", + RunE: func(cmd *cobra.Command, args []string) error { + h, err := cmd.Flags().GetBool("help") + if err != nil { + return err + } + if h { + return cmd.Help() + } + return cmd.Usage() + }, + } + + rolloutsCmd.AddCommand( + get.NewCommand(ctx), + status.NewCommand(ctx), + ) + if err := rolloutsCmd.Execute(); err != nil { + os.Exit(-1) + } +} diff --git a/rollouts/cli/status/status.go b/rollouts/cli/status/status.go new file mode 100644 index 0000000000..0d8b88f553 --- /dev/null +++ b/rollouts/cli/status/status.go @@ -0,0 +1,86 @@ +// Copyright 2023 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 status + +import ( + "context" + "fmt" + + rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" + "github.com/spf13/cobra" + + "github.com/jedib0t/go-pretty/v6/table" +) + +func newRunner(ctx context.Context) *runner { + r := &runner{ + ctx: ctx, + } + c := &cobra.Command{ + Use: "status", + Short: "displays status of a rollout", + Long: "displays status of a rollout", + Example: "displays status of a rollout", + RunE: r.runE, + } + r.Command = c + return r +} + +func NewCommand(ctx context.Context) *cobra.Command { + return newRunner(ctx).Command +} + +type runner struct { + ctx context.Context + Command *cobra.Command +} + +func (r *runner) runE(cmd *cobra.Command, args []string) error { + rlc, err := rolloutsclient.New() + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + if len(args) == 0 { + fmt.Printf("must provide rollout name") + return nil + } + + rollout, err := rlc.Get(r.ctx, args[0]) + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + renderStatusAsTable(cmd, rollout) + return nil +} + +func renderStatusAsTable(cmd *cobra.Command, rollout *rolloutsapi.Rollout) { + t := table.NewWriter() + t.SetOutputMirror(cmd.OutOrStdout()) + t.AppendHeader(table.Row{"CLUSTER", "PACKAGE ID", "PACKAGE STATUS", "SYNC STATUS"}) + for _, cluster := range rollout.Status.ClusterStatuses { + pkgStatus := cluster.PackageStatus + t.AppendRow([]interface{}{cluster.Name, pkgStatus.PackageID, pkgStatus.Status, pkgStatus.SyncStatus}) + } + t.AppendSeparator() + // t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000}) + // t.AppendFooter(table.Row{"", "", "Total", 10000}) + t.Render() +} diff --git a/rollouts/go.mod b/rollouts/go.mod index e424558d19..d76ea5efa1 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -53,9 +53,11 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/imdario/mergo v0.3.12 // indirect + github.com/jedib0t/go-pretty/v6 v6.4.4 // 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/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -65,6 +67,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index 69139d8e9c..f470fd5cf7 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -220,6 +220,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jedib0t/go-pretty/v6 v6.4.4 h1:N+gz6UngBPF4M288kiMURPHELDMIhF/Em35aYuKrsSc= +github.com/jedib0t/go-pretty/v6 v6.4.4/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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= @@ -249,6 +251,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN 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-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -275,6 +279,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -299,6 +304,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -320,6 +327,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= diff --git a/rollouts/rolloutsclient/client.go b/rollouts/rolloutsclient/client.go new file mode 100644 index 0000000000..5fb6e3171b --- /dev/null +++ b/rollouts/rolloutsclient/client.go @@ -0,0 +1,92 @@ +// Copyright 2023 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 rolloutsclient + +import ( + "context" + "fmt" + + rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + coreapi "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +// Client implments client for the rollouts API. +type Client struct { + client client.Client +} + +func New() (*Client, error) { + scheme, err := createScheme() + if err != nil { + return nil, err + } + cl, err := client.New(config.GetConfigOrDie(), client.Options{ + Scheme: scheme, + }) + if err != nil { + return nil, err + } + return &Client{client: cl}, nil +} + +func createScheme() (*runtime.Scheme, error) { + scheme := runtime.NewScheme() + + for _, api := range (runtime.SchemeBuilder{ + rolloutsapi.AddToScheme, + coreapi.AddToScheme, + metav1.AddMetaToScheme, + }) { + if err := api(scheme); err != nil { + return nil, err + } + } + return scheme, nil +} + +func (rlc *Client) List(ctx context.Context, ns string) (*rolloutsapi.RolloutList, error) { + if ns == "" { + ns = "default" + } + + rollouts := &rolloutsapi.RolloutList{} + if err := rlc.client.List(context.Background(), rollouts, client.InNamespace(ns)); err != nil { + return nil, err + } + + return rollouts, nil +} + +func (rlc *Client) Get(ctx context.Context, name string) (*rolloutsapi.Rollout, error) { + if name == "" { + return nil, fmt.Errorf("must provide rollout name") + } + + key := types.NamespacedName{ + Namespace: "default", + Name: name, + } + rollout := &rolloutsapi.Rollout{} + if err := rlc.client.Get(context.Background(), key, rollout); err != nil { + return nil, err + } + + return rollout, nil +} From cfd0bb1e7a78a1a000c4e9c4f599196c3a90942f Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 11 Jan 2023 18:49:27 -0500 Subject: [PATCH 18/38] rollouts: add rollout summary status (#3725) --- rollouts/api/v1alpha1/rollout_types.go | 10 +++ .../api/v1alpha1/zz_generated.deepcopy.go | 27 ++++++++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 39 +++++++++++ rollouts/controllers/rollout_controller.go | 65 ++++++++++++++++++- 4 files changed, 138 insertions(+), 3 deletions(-) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index ff2c12a4a7..2143ac0da5 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -144,6 +144,16 @@ type RolloutStatus struct { // Conditions describes the reconciliation state of the object. Conditions []metav1.Condition `json:"conditions,omitempty"` + Overall string `json:"overall,omitempty"` + WaveStatuses []WaveStatus `json:"waveStatuses,omitempty"` + + ClusterStatuses []ClusterStatus `json:"clusterStatuses,omitempty"` +} + +type WaveStatus struct { + Name string `json:"name"` + Status string `json:"status"` + Paused bool `json:"paused,omitempty"` ClusterStatuses []ClusterStatus `json:"clusterStatuses,omitempty"` } diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 09bc6b1992..0337dc8b67 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -473,6 +473,13 @@ func (in *RolloutStatus) DeepCopyInto(out *RolloutStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.WaveStatuses != nil { + in, out := &in.WaveStatuses, &out.WaveStatuses + *out = make([]WaveStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.ClusterStatuses != nil { in, out := &in.ClusterStatuses, &out.ClusterStatuses *out = make([]ClusterStatus, len(*in)) @@ -636,3 +643,23 @@ func (in *Wave) DeepCopy() *Wave { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WaveStatus) DeepCopyInto(out *WaveStatus) { + *out = *in + if in.ClusterStatuses != nil { + in, out := &in.ClusterStatuses, &out.ClusterStatuses + *out = make([]ClusterStatus, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WaveStatus. +func (in *WaveStatus) DeepCopy() *WaveStatus { + if in == nil { + return nil + } + out := new(WaveStatus) + in.DeepCopyInto(out) + return out +} diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index 57872720ee..fed4a9dda0 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -314,6 +314,45 @@ spec: observedGeneration: format: int64 type: integer + overall: + type: string + waveStatuses: + items: + properties: + clusterStatuses: + items: + properties: + name: + type: string + packageStatus: + properties: + packageId: + type: string + status: + type: string + syncStatus: + type: string + required: + - packageId + - status + - syncStatus + type: object + required: + - name + - packageStatus + type: object + type: array + name: + type: string + paused: + type: boolean + status: + type: string + required: + - name + - status + type: object + type: array type: object type: object served: true diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 91c349f008..a949861f3e 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -21,6 +21,7 @@ import ( "flag" "fmt" "math" + "strings" "sync" v1 "k8s.io/api/core/v1" @@ -272,11 +273,14 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop pauseFutureWaves := false pauseAfterWaveName := "" + afterPauseAfterWave := false if rollout.Spec.Strategy.Progressive != nil { pauseAfterWaveName = rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName } + waveStatuses := []gitopsv1alpha1.WaveStatus{} + for _, wave := range strategy.Spec.Waves { waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) if err != nil { @@ -305,22 +309,36 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop return err } - if thisWaveInProgress || wave.Name == pauseAfterWaveName { + if thisWaveInProgress { pauseFutureWaves = true } allClusterStatuses = append(allClusterStatuses, clusterStatuses...) + + waveStatuses = append(waveStatuses, getWaveStatus(wave, clusterStatuses, afterPauseAfterWave)) + + if wave.Name == pauseAfterWaveName { + pauseFutureWaves = true + afterPauseAfterWave = true + } } - if err := r.updateStatus(ctx, rollout, allClusterStatuses); err != nil { + if err := r.updateStatus(ctx, rollout, waveStatuses, allClusterStatuses); err != nil { return err } return nil } -func (r *RolloutReconciler) updateStatus(ctx context.Context, rollout *gitopsv1alpha1.Rollout, clusterStatuses []gitopsv1alpha1.ClusterStatus) error { +func (r *RolloutReconciler) updateStatus(ctx context.Context, rollout *gitopsv1alpha1.Rollout, waveStatuses []gitopsv1alpha1.WaveStatus, clusterStatuses []gitopsv1alpha1.ClusterStatus) error { logger := log.FromContext(ctx) logger.Info("updating the status", "cluster statuses", len(clusterStatuses)) + + rollout.Status.Overall = getOverallStatus(clusterStatuses) + + if len(waveStatuses) > 1 { + rollout.Status.WaveStatuses = waveStatuses + } + rollout.Status.ClusterStatuses = clusterStatuses rollout.Status.ObservedGeneration = rollout.Generation return r.Client.Status().Update(ctx, rollout) @@ -664,3 +682,44 @@ func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&gitopsv1alpha1.RemoteRootSync{}). Complete(r) } + +func getWaveStatus(wave gitopsv1alpha1.Wave, clusterStatuses []gitopsv1alpha1.ClusterStatus, wavePaused bool) gitopsv1alpha1.WaveStatus { + return gitopsv1alpha1.WaveStatus{ + Name: wave.Name, + Status: getOverallStatus(clusterStatuses), + Paused: wavePaused, + ClusterStatuses: clusterStatuses, + } +} + +func getOverallStatus(clusterStatuses []gitopsv1alpha1.ClusterStatus) string { + overall := "Completed" + + anyProgressing := false + anyStalled := false + anyWaiting := false + + for _, clusterStatus := range clusterStatuses { + switch { + case clusterStatus.PackageStatus.Status == "Progressing": + anyProgressing = true + + case clusterStatus.PackageStatus.Status == "Stalled": + anyStalled = true + + case strings.HasPrefix(clusterStatus.PackageStatus.Status, "Waiting"): + anyWaiting = true + } + } + + switch { + case anyProgressing: + overall = "Progressing" + case anyStalled: + overall = "Stalled" + case anyWaiting: + overall = "Waiting" + } + + return overall +} From 563afce543ce604a9e00930c37145ed647eb86cc Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Thu, 12 Jan 2023 12:48:49 -0800 Subject: [PATCH 19/38] rollouts: tidy up go.mod/sum (#3726) --- rollouts/go.mod | 4 +++- rollouts/go.sum | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rollouts/go.mod b/rollouts/go.mod index d76ea5efa1..d280cc0277 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -9,8 +9,10 @@ require ( github.com/google/cel-go v0.13.0 github.com/google/go-cmp v0.5.9 github.com/google/go-github/v48 v48.2.0 + github.com/jedib0t/go-pretty/v6 v6.4.4 github.com/onsi/ginkgo/v2 v2.1.6 github.com/onsi/gomega v1.20.1 + github.com/spf13/cobra v1.4.0 golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 google.golang.org/api v0.103.0 google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c @@ -53,7 +55,7 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/jedib0t/go-pretty/v6 v6.4.4 // indirect + github.com/inconshreveable/mousetrap v1.0.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 diff --git a/rollouts/go.sum b/rollouts/go.sum index f470fd5cf7..607b962e71 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -87,6 +87,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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= @@ -220,6 +221,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jedib0t/go-pretty/v6 v6.4.4 h1:N+gz6UngBPF4M288kiMURPHELDMIhF/Em35aYuKrsSc= github.com/jedib0t/go-pretty/v6 v6.4.4/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -308,10 +311,13 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +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 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= From e9816077ae3763d07a96af84808b873c904e9384 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Thu, 12 Jan 2023 17:20:53 -0500 Subject: [PATCH 20/38] rollouts: duplicate target fix (#3727) --- rollouts/controllers/rollout_controller.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index a949861f3e..c68c02f500 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -91,7 +91,6 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct logger.Info("reconciling", "key", req.NamespacedName) if err := r.Get(ctx, req.NamespacedName, &rollout); err != nil { - logger.Error(err, "unable to fetch Rollout") // we'll ignore not-found errors, since they can't be fixed by an immediate // requeue (we'll need to wait for a new notification), and we can get them // on deleted requests. @@ -373,12 +372,12 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, klog.Infof("Found remoterootsyncs: %s", toRemoteRootSyncNames(existingRRSs)) targets := &Targets{} // track keys of all the desired remote rootsyncs - for _, clusterPkg := range clusterPackages { + for idx, clusterPkg := range clusterPackages { // TODO: figure out multiple packages per cluster story if len(clusterPkg.Packages) < 1 { continue } - cluster := &clusterPkg.Cluster + cluster := &clusterPackages[idx].Cluster pkg := &clusterPkg.Packages[0] rrs := gitopsv1alpha1.RemoteRootSync{} key := client.ObjectKey{ From ec5e40d730efc602eebc6ebe924458ee04005401 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Thu, 12 Jan 2023 18:09:17 -0500 Subject: [PATCH 21/38] rollouts: sort cluster status list (#3728) --- rollouts/controllers/rollout_controller.go | 15 ++++++++++++--- rollouts/pkg/clusterstore/clusterstore.go | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index c68c02f500..1286b907db 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -21,6 +21,7 @@ import ( "flag" "fmt" "math" + "sort" "strings" "sync" @@ -322,6 +323,8 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop } } + sortClusterStatuses(allClusterStatuses) + if err := r.updateStatus(ctx, rollout, waveStatuses, allClusterStatuses); err != nil { return err } @@ -334,9 +337,7 @@ func (r *RolloutReconciler) updateStatus(ctx context.Context, rollout *gitopsv1a rollout.Status.Overall = getOverallStatus(clusterStatuses) - if len(waveStatuses) > 1 { - rollout.Status.WaveStatuses = waveStatuses - } + rollout.Status.WaveStatuses = waveStatuses rollout.Status.ClusterStatuses = clusterStatuses rollout.Status.ObservedGeneration = rollout.Generation @@ -536,6 +537,8 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv thisWaveInProgress := concurrentUpdates > 0 + sortClusterStatuses(clusterStatuses) + return thisWaveInProgress, clusterStatuses, nil } @@ -682,6 +685,12 @@ func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } +func sortClusterStatuses(clusterStatuses []gitopsv1alpha1.ClusterStatus) { + sort.Slice(clusterStatuses, func(i, j int) bool { + return strings.Compare(clusterStatuses[i].Name, clusterStatuses[j].Name) == -1 + }) +} + func getWaveStatus(wave gitopsv1alpha1.Wave, clusterStatuses []gitopsv1alpha1.ClusterStatus, wavePaused bool) gitopsv1alpha1.WaveStatus { return gitopsv1alpha1.WaveStatus{ Name: wave.Name, diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index d509b383d1..20625229c2 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -18,6 +18,8 @@ import ( "context" "encoding/base64" "fmt" + "sort" + "strings" "golang.org/x/oauth2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -74,6 +76,10 @@ func (cs *ClusterStore) ListClusters(ctx context.Context, selectors ...*metav1.L gkeClusters.Items = intersection } + sort.Slice(gkeClusters.Items, func(i, j int) bool { + return strings.Compare(gkeClusters.Items[i].Name, gkeClusters.Items[j].Name) == -1 + }) + return gkeClusters, nil } From c58c4b9b787c9eb301deeaf4bf5d733713a549e5 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Thu, 12 Jan 2023 19:03:55 -0500 Subject: [PATCH 22/38] rollouts: conditionally show wave status (#3729) --- rollouts/controllers/rollout_controller.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 1286b907db..a7e59e3756 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -337,7 +337,9 @@ func (r *RolloutReconciler) updateStatus(ctx context.Context, rollout *gitopsv1a rollout.Status.Overall = getOverallStatus(clusterStatuses) - rollout.Status.WaveStatuses = waveStatuses + if len(waveStatuses) > 1 { + rollout.Status.WaveStatuses = waveStatuses + } rollout.Status.ClusterStatuses = clusterStatuses rollout.Status.ObservedGeneration = rollout.Generation From 1dd0983bbab6b66ac99195e26792dd6e7bc3e04b Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Fri, 13 Jan 2023 09:26:37 -0800 Subject: [PATCH 23/38] rollouts: CLI now supports displaying waves and progress counts (#3730) --- rollouts/cli/get/get.go | 27 ++++++++++++++++++++++++--- rollouts/cli/status/status.go | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/rollouts/cli/get/get.go b/rollouts/cli/get/get.go index d6cfd0253e..0a411c81cf 100644 --- a/rollouts/cli/get/get.go +++ b/rollouts/cli/get/get.go @@ -18,7 +18,9 @@ import ( "context" "fmt" + rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" + "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" ) @@ -58,9 +60,28 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error { fmt.Printf("%s\n", err) return err } + renderRolloutsAsTable(cmd, rollouts) + return nil +} + +func renderRolloutsAsTable(cmd *cobra.Command, rollouts *rolloutsapi.RolloutList) { + t := table.NewWriter() + t.SetOutputMirror(cmd.OutOrStdout()) + t.AppendHeader(table.Row{"ROLLOUT", "STATUS", "CLUSTERS (READY/TOTAL)"}) for _, rollout := range rollouts.Items { - fmt.Printf("%s\n", rollout.Name) + readyCount := 0 + for _, cluster := range rollout.Status.ClusterStatuses { + if cluster.PackageStatus.Status == "Synced" { + readyCount++ + } + } + t.AppendRow([]interface{}{ + rollout.Name, + rollout.Status.Overall, + fmt.Sprintf("%d/%d", readyCount, len(rollout.Status.ClusterStatuses))}) } - - return nil + t.AppendSeparator() + // t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000}) + // t.AppendFooter(table.Row{"", "", "Total", 10000}) + t.Render() } diff --git a/rollouts/cli/status/status.go b/rollouts/cli/status/status.go index 0d8b88f553..dc07b75945 100644 --- a/rollouts/cli/status/status.go +++ b/rollouts/cli/status/status.go @@ -67,6 +67,10 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error { return err } + if len(rollout.Status.WaveStatuses) > 0 { + renderWaveStatusAsTable(cmd, rollout) + return nil + } renderStatusAsTable(cmd, rollout) return nil } @@ -84,3 +88,23 @@ func renderStatusAsTable(cmd *cobra.Command, rollout *rolloutsapi.Rollout) { // t.AppendFooter(table.Row{"", "", "Total", 10000}) t.Render() } + +func renderWaveStatusAsTable(cmd *cobra.Command, rollout *rolloutsapi.Rollout) { + t := table.NewWriter() + t.SetOutputMirror(cmd.OutOrStdout()) + t.AppendHeader(table.Row{"WAVE", "CLUSTER", "PACKAGE ID", "PACKAGE STATUS", "SYNC STATUS"}) + for _, wave := range rollout.Status.WaveStatuses { + for i, cluster := range wave.ClusterStatuses { + pkgStatus := cluster.PackageStatus + waveName := "" + if i == 0 { + waveName = wave.Name + } + t.AppendRow([]interface{}{waveName, cluster.Name, pkgStatus.PackageID, pkgStatus.Status, pkgStatus.SyncStatus}) + } + t.AppendSeparator() + } + // t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000}) + // t.AppendFooter(table.Row{"", "", "Total", 10000}) + t.Render() +} From 53849a98465f623cc4fc3368ff33ee18cf401629 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Fri, 13 Jan 2023 15:49:40 -0500 Subject: [PATCH 24/38] rollouts: cli can now advance waves on progressive rollouts (#3731) --- rollouts/cli/advance/advance.go | 103 ++++++++++++++++++++++++++++++ rollouts/cli/main.go | 2 + rollouts/rolloutsclient/client.go | 8 +++ 3 files changed, 113 insertions(+) create mode 100644 rollouts/cli/advance/advance.go diff --git a/rollouts/cli/advance/advance.go b/rollouts/cli/advance/advance.go new file mode 100644 index 0000000000..96c10a0106 --- /dev/null +++ b/rollouts/cli/advance/advance.go @@ -0,0 +1,103 @@ +// Copyright 2023 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 advance + +import ( + "context" + "fmt" + + "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" + "github.com/spf13/cobra" +) + +func newRunner(ctx context.Context) *runner { + r := &runner{ + ctx: ctx, + } + c := &cobra.Command{ + Use: "advance rollout-name wave-name", + Short: "advances the wave of a progressive rollout", + Long: "advances the wave of a progressive rollout", + Example: "advances the wave of a progressive rollout", + RunE: r.runE, + } + r.Command = c + return r +} + +func NewCommand(ctx context.Context) *cobra.Command { + return newRunner(ctx).Command +} + +type runner struct { + ctx context.Context + Command *cobra.Command +} + +func (r *runner) runE(cmd *cobra.Command, args []string) error { + rlc, err := rolloutsclient.New() + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + if len(args) == 0 { + return fmt.Errorf("must provide rollout name\n") + } + + if len(args) == 1 { + return fmt.Errorf("must provide wave name\n") + } + + rolloutName := args[0] + waveName := args[1] + + rollout, err := rlc.Get(r.ctx, rolloutName) + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + if rollout.Spec.Strategy.Type != v1alpha1.Progressive { + return fmt.Errorf("rollout must be using the progressive strategy to use this command\n") + } + + if rollout.Status.WaveStatuses != nil { + waveFound := false + + for _, waveStatus := range rollout.Status.WaveStatuses { + if waveStatus.Name == waveName { + waveFound = true + break + } + } + + if !waveFound { + return fmt.Errorf("wave %q not found in this rollout\n", waveName) + } + } + + rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName = waveName + + err = rlc.Update(r.ctx, rollout) + if err != nil { + fmt.Printf("%s\n", err) + return err + } + + fmt.Println("done") + return nil +} diff --git a/rollouts/cli/main.go b/rollouts/cli/main.go index b6eff2e499..386944ba6a 100644 --- a/rollouts/cli/main.go +++ b/rollouts/cli/main.go @@ -18,6 +18,7 @@ import ( "context" "os" + "github.com/GoogleContainerTools/kpt/rollouts/cli/advance" "github.com/GoogleContainerTools/kpt/rollouts/cli/get" "github.com/GoogleContainerTools/kpt/rollouts/cli/status" "github.com/spf13/cobra" @@ -44,6 +45,7 @@ func main() { } rolloutsCmd.AddCommand( + advance.NewCommand(ctx), get.NewCommand(ctx), status.NewCommand(ctx), ) diff --git a/rollouts/rolloutsclient/client.go b/rollouts/rolloutsclient/client.go index 5fb6e3171b..ca5ef6885d 100644 --- a/rollouts/rolloutsclient/client.go +++ b/rollouts/rolloutsclient/client.go @@ -90,3 +90,11 @@ func (rlc *Client) Get(ctx context.Context, name string) (*rolloutsapi.Rollout, return rollout, nil } + +func (rlc *Client) Update(ctx context.Context, rollout *rolloutsapi.Rollout) error { + if err := rlc.client.Update(context.Background(), rollout); err != nil { + return err + } + + return nil +} From 247cb0dc1bf2e184c35f37285736609d5bec5849 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Fri, 13 Jan 2023 17:20:43 -0500 Subject: [PATCH 25/38] rollouts: enable server side throttling for cli (#3732) --- rollouts/go.mod | 15 ++++++----- rollouts/go.sum | 43 +++++++++++++++++++------------ rollouts/rolloutsclient/client.go | 25 +++++++++++++++++- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/rollouts/go.mod b/rollouts/go.mod index d280cc0277..3ed65c9b0e 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -10,17 +10,18 @@ require ( github.com/google/go-cmp v0.5.9 github.com/google/go-github/v48 v48.2.0 github.com/jedib0t/go-pretty/v6 v6.4.4 - github.com/onsi/ginkgo/v2 v2.1.6 - github.com/onsi/gomega v1.20.1 - github.com/spf13/cobra v1.4.0 + github.com/onsi/ginkgo/v2 v2.2.0 + github.com/onsi/gomega v1.20.2 + github.com/spf13/cobra v1.5.0 golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 google.golang.org/api v0.103.0 google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c - k8s.io/api v0.25.0 + k8s.io/api v0.25.3 k8s.io/apimachinery v0.25.4 - k8s.io/client-go v0.25.0 + k8s.io/client-go v0.25.3 k8s.io/klog v1.0.0 k8s.io/klog/v2 v2.80.1 + sigs.k8s.io/cli-utils v0.34.0 sigs.k8s.io/controller-runtime v0.13.1 ) @@ -89,8 +90,8 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/component-base v0.25.0 // indirect + k8s.io/apiextensions-apiserver v0.25.3 // indirect + k8s.io/component-base v0.25.3 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index 607b962e71..b989fc039c 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -87,7 +87,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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/cpuguy83/go-md2man/v2 v2.0.2/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= @@ -109,6 +109,7 @@ github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-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= @@ -206,6 +207,7 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf 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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -266,6 +268,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -274,10 +277,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= +github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -316,8 +319,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= -github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= 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 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= @@ -340,6 +343,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o 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/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= @@ -353,11 +357,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= @@ -694,31 +699,37 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= -k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= +k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= +k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= +k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= +k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= +k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= +k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= +k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 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/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= +sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= +sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/rollouts/rolloutsclient/client.go b/rollouts/rolloutsclient/client.go index ca5ef6885d..01d58cbe79 100644 --- a/rollouts/rolloutsclient/client.go +++ b/rollouts/rolloutsclient/client.go @@ -17,12 +17,15 @@ package rolloutsclient import ( "context" "fmt" + "time" rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" coreapi "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "sigs.k8s.io/cli-utils/pkg/flowcontrol" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" ) @@ -37,7 +40,9 @@ func New() (*Client, error) { if err != nil { return nil, err } - cl, err := client.New(config.GetConfigOrDie(), client.Options{ + + config := useServerSideThrottling(config.GetConfigOrDie()) + cl, err := client.New(config, client.Options{ Scheme: scheme, }) if err != nil { @@ -98,3 +103,21 @@ func (rlc *Client) Update(ctx context.Context, rollout *rolloutsapi.Rollout) err return nil } + +func useServerSideThrottling(config *rest.Config) *rest.Config { + // Timeout if the query takes too long + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + enabled, err := flowcontrol.IsEnabled(ctx, config) + if err != nil { + fmt.Printf("Failed to query apiserver to check for flow control enablement: %v\n", err) + } + + if enabled { + config.QPS = -1 + config.Burst = -1 + } + + return config +} From 2620f2df6f20f984acd715a06734dcff917d1012 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Thu, 19 Jan 2023 15:41:35 -0500 Subject: [PATCH 26/38] rollouts: add container cluster watch (#3738) --- rollouts/controllers/rollout_controller.go | 74 +++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index a7e59e3756..9ab674514e 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -28,13 +28,18 @@ import ( v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/klog" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" @@ -56,6 +61,14 @@ const ( rolloutLabel = "gitops.kpt.dev/rollout-name" ) +var ( + kccClusterGVK = schema.GroupVersionKind{ + Group: "container.cnrm.cloud.google.com", + Version: "v1beta1", + Kind: "ContainerCluster", + } +) + // RolloutReconciler reconciles a Rollout object type RolloutReconciler struct { client.Client @@ -588,6 +601,15 @@ func (r *RolloutReconciler) listRemoteRootSyncs(ctx context.Context, rsdName, rs return remoterootsyncs, nil } +func (r *RolloutReconciler) listAllRollouts(ctx context.Context) ([]gitopsv1alpha1.Rollout, error) { + var rolloutsList gitopsv1alpha1.RolloutList + if err := r.List(ctx, &rolloutsList); err != nil { + return nil, err + } + + return rolloutsList.Items, nil +} + func isRRSSynced(rss *gitopsv1alpha1.RemoteRootSync) bool { if rss.Generation != rss.Status.ObservedGeneration { return false @@ -680,13 +702,53 @@ func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { if err := r.store.Init(); err != nil { return err } - // TODO: watch cluster resources as well + + var containerCluster gkeclusterapis.ContainerCluster return ctrl.NewControllerManagedBy(mgr). For(&gitopsv1alpha1.Rollout{}). Owns(&gitopsv1alpha1.RemoteRootSync{}). + Watches( + &source.Kind{Type: &containerCluster}, + handler.EnqueueRequestsFromMapFunc(r.mapClusterUpdateToRequest), + ). Complete(r) } +func (r *RolloutReconciler) mapClusterUpdateToRequest(cluster client.Object) []reconcile.Request { + logger := log.FromContext(context.Background()) + + var requests []reconcile.Request + + allRollouts, err := r.listAllRollouts(context.Background()) + if err != nil { + logger.Error(err, "failed to list rollouts") + return []reconcile.Request{} + } + + for _, rollout := range allRollouts { + selector, err := metav1.LabelSelectorAsSelector(rollout.Spec.Targets.Selector) + if err != nil { + logger.Error(err, "failed to create label selector", "rolloutName", rollout.Name) + continue + } + + rolloutDeploysToCluster := rolloutIncludesCluster(&rollout, cluster.GetName()) + clusterInTargetSet := selector.Matches(labels.Set(cluster.GetLabels())) + + // Rollouts will be reconciled for cluster updates when + // 1) a cluster is added to the rollout target set (clusterInTargetSet will be true) + // 2) a cluster is removed from the rollout target saet (rolloutDeploysToCluster will be true) + // 3) an cluster in the rollout target set is being updated where the package matching logic may produce different results (both variables will be true) + reconcileRollout := rolloutDeploysToCluster || clusterInTargetSet + + if reconcileRollout { + requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{Name: rollout.Name, Namespace: rollout.Namespace}}) + } + } + + return requests +} + func sortClusterStatuses(clusterStatuses []gitopsv1alpha1.ClusterStatus) { sort.Slice(clusterStatuses, func(i, j int) bool { return strings.Compare(clusterStatuses[i].Name, clusterStatuses[j].Name) == -1 @@ -733,3 +795,13 @@ func getOverallStatus(clusterStatuses []gitopsv1alpha1.ClusterStatus) string { return overall } + +func rolloutIncludesCluster(rollout *gitopsv1alpha1.Rollout, clusterName string) bool { + for _, clusterStatus := range rollout.Status.ClusterStatuses { + if clusterStatus.Name == clusterName { + return true + } + } + + return false +} From 7bcd132dd5fc3df98c1fa4138c453880b2c5a5d5 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Mon, 23 Jan 2023 13:35:38 -0500 Subject: [PATCH 27/38] rollouts: delete remote root sync when no longer needed (#3742) --- rollouts/controllers/rollout_controller.go | 137 ++++++++++++++++----- 1 file changed, 104 insertions(+), 33 deletions(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 9ab674514e..1f38e4885f 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -275,6 +275,7 @@ func (r *RolloutReconciler) getPackageDiscoveryClient(rolloutNamespacedName type func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout, strategy *gitopsv1alpha1.ProgressiveRolloutStrategy, packageDiscoveryClient *packagediscovery.PackageDiscovery) error { logger := log.FromContext(ctx) + targetClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx, rollout.Spec.Packages) if err != nil { logger.Error(err, "failed to discover packages") @@ -282,7 +283,24 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop } logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages) + packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(targetClusters.Items, discoveredPackages) + clusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) + if err != nil { + return err + } + + targets, err := r.computeTargets(ctx, rollout, clusterPackages) + if err != nil { + return err + } + allClusterStatuses := []gitopsv1alpha1.ClusterStatus{} + waveStatuses := []gitopsv1alpha1.WaveStatus{} + + allWaveTargets, err := r.getWaveTargets(ctx, rollout, targets, strategy.Spec.Waves) + if err != nil { + return err + } pauseFutureWaves := false pauseAfterWaveName := "" @@ -292,32 +310,12 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop pauseAfterWaveName = rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName } - waveStatuses := []gitopsv1alpha1.WaveStatus{} - - for _, wave := range strategy.Spec.Waves { - waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) - if err != nil { - return err - } - - packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(waveClusters.Items, discoveredPackages) - allClusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) - if err != nil { - logger.Error(err, "get cluster packages failed") - return client.IgnoreNotFound(err) - } - - for _, clusterPackages := range allClusterPackages { - clusterName := clusterPackages.Cluster.Name - logger.Info("cluster packages", "cluster", clusterName, "packagesCount", len(clusterPackages.Packages), "packages", clusterPackages.Packages) - } + for i := range allWaveTargets { + thisWaveTargets := allWaveTargets[i] + waveTargets := thisWaveTargets.Targets + wave := thisWaveTargets.Wave - targets, err := r.computeTargets(ctx, rollout, allClusterPackages, waveClusters.Items) - if err != nil { - return err - } - - thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, &wave, targets, pauseFutureWaves) + thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, wave, waveTargets, pauseFutureWaves) if err != nil { return err } @@ -367,7 +365,7 @@ For RRS, make rootSyncTemplate */ func (r *RolloutReconciler) computeTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, - clusterPackages []packageclustermatcher.ClusterPackages, allowClusters []gkeclusterapis.ContainerCluster) (*Targets, error) { + clusterPackages []packageclustermatcher.ClusterPackages) (*Targets, error) { RRSkeysToBeDeleted := map[client.ObjectKey]*gitopsv1alpha1.RemoteRootSync{} // let's take a look at existing remoterootsyncs @@ -378,11 +376,7 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, // initially assume all the keys to be deleted for _, rrs := range existingRRSs { - for _, cluster := range allowClusters { - if rrs.Spec.ClusterRef.Name == cluster.Name { - RRSkeysToBeDeleted[client.ObjectKeyFromObject(rrs)] = rrs - } - } + RRSkeysToBeDeleted[client.ObjectKeyFromObject(rrs)] = rrs } klog.Infof("Found remoterootsyncs: %s", toRemoteRootSyncNames(existingRRSs)) @@ -433,6 +427,65 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, return targets, nil } +func (r *RolloutReconciler) getWaveTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, allTargets *Targets, + allWaves []gitopsv1alpha1.Wave) ([]WaveTarget, error) { + allWaveTargets := []WaveTarget{} + + clusterNameToWaveTarget := make(map[string]*WaveTarget) + + for i := range allWaves { + wave := allWaves[i] + thisWaveTarget := WaveTarget{Wave: &wave, Targets: &Targets{}} + + waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) + if err != nil { + return nil, err + } + + for _, cluster := range waveClusters.Items { + clusterNameToWaveTarget[cluster.Name] = &thisWaveTarget + } + + allWaveTargets = append(allWaveTargets, thisWaveTarget) + } + + for _, toCreate := range allTargets.ToBeCreated { + wavetTargets := clusterNameToWaveTarget[toCreate.cluster.Name].Targets + wavetTargets.ToBeCreated = append(wavetTargets.ToBeCreated, toCreate) + } + + for _, rrs := range allTargets.ToBeUpdated { + wavetTargets := clusterNameToWaveTarget[rrs.Spec.ClusterRef.Name].Targets + wavetTargets.ToBeUpdated = append(wavetTargets.ToBeUpdated, rrs) + } + + for _, rrs := range allTargets.Unchanged { + wavetTargets := clusterNameToWaveTarget[rrs.Spec.ClusterRef.Name].Targets + wavetTargets.Unchanged = append(wavetTargets.Unchanged, rrs) + } + + for _, rrs := range allTargets.ToBeDeleted { + // The remote root sync will be associated back to it's previous wave and then removed as part + // of that wave. If the previous wave the remote root sync cannot be determined, then the remote + // root sync will be removed with the last wave of the rollout. + + waveName, found := findWaveNameForCluster(rollout, rrs.Spec.ClusterRef.Name) + + if !found { + waveName = allWaveTargets[len(allWaveTargets)-1].Wave.Name + } + + for _, waveTarget := range allWaveTargets { + if waveTarget.Wave.Name == waveName { + wavetTargets := waveTarget.Targets + wavetTargets.ToBeDeleted = append(wavetTargets.ToBeDeleted, rrs) + } + } + } + + return allWaveTargets, nil +} + func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, wave *gitopsv1alpha1.Wave, targets *Targets, pauseWave bool) (bool, []gitopsv1alpha1.ClusterStatus, error) { clusterStatuses := []gitopsv1alpha1.ClusterStatus{} @@ -457,6 +510,7 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv gitopsv1alpha1.ClusterRef{Name: target.cluster.Name}, rootSyncSpec, pkgID(target.packageRef), + wave.Name, ) if maxConcurrent > concurrentUpdates { @@ -557,6 +611,11 @@ func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv return thisWaveInProgress, clusterStatuses, nil } +type WaveTarget struct { + Wave *gitopsv1alpha1.Wave + Targets *Targets +} + type Targets struct { ToBeCreated []*clusterPackagePair ToBeUpdated []*gitopsv1alpha1.RemoteRootSync @@ -633,7 +692,7 @@ func isRRSErrored(rss *gitopsv1alpha1.RemoteRootSync) bool { } // Given a package identifier and cluster, create a RemoteRootSync object. -func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha1.ClusterRef, rssSpec *gitopsv1alpha1.RootSyncSpec, pkgID string) *gitopsv1alpha1.RemoteRootSync { +func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha1.ClusterRef, rssSpec *gitopsv1alpha1.RootSyncSpec, pkgID string, waveName string) *gitopsv1alpha1.RemoteRootSync { t := true return &gitopsv1alpha1.RemoteRootSync{ ObjectMeta: metav1.ObjectMeta{ @@ -755,7 +814,7 @@ func sortClusterStatuses(clusterStatuses []gitopsv1alpha1.ClusterStatus) { }) } -func getWaveStatus(wave gitopsv1alpha1.Wave, clusterStatuses []gitopsv1alpha1.ClusterStatus, wavePaused bool) gitopsv1alpha1.WaveStatus { +func getWaveStatus(wave *gitopsv1alpha1.Wave, clusterStatuses []gitopsv1alpha1.ClusterStatus, wavePaused bool) gitopsv1alpha1.WaveStatus { return gitopsv1alpha1.WaveStatus{ Name: wave.Name, Status: getOverallStatus(clusterStatuses), @@ -796,6 +855,18 @@ func getOverallStatus(clusterStatuses []gitopsv1alpha1.ClusterStatus) string { return overall } +func findWaveNameForCluster(rollout *gitopsv1alpha1.Rollout, clusterName string) (string, bool) { + for _, waveStatus := range rollout.Status.WaveStatuses { + for _, clusterStatus := range waveStatus.ClusterStatuses { + if clusterStatus.Name == clusterName { + return waveStatus.Name, true + } + } + } + + return "", false +} + func rolloutIncludesCluster(rollout *gitopsv1alpha1.Rollout, clusterName string) bool { for _, clusterStatus := range rollout.Status.ClusterStatuses { if clusterStatus.Name == clusterName { From 568a5941ba77ab5bb81b01da106f0a870cdb6d67 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Tue, 24 Jan 2023 14:47:01 -0800 Subject: [PATCH 28/38] Rollouts rebase kpt main (#3750) * porch: don't save empty patches (#3695) * docs: fixes for some minor documentation typos (#3699) * docs: Update the kpt book with more details about namespaces and RBAC for porch (#3692) * Log enabled controllers and warn if no controllers are enabled (#3710) Because the default is to enable no controllers, it is easy to mistakenly start a no-op controller. * Extract out common parse-package logic (#3711) We had this code duplicated in a few places also. * refactor pod warmup to avoid vet warning (#3713) By refactoring the parallel operation into a separate function, it should be easier to read and we avoid a loop-closure go-tcha. * Bump json5 from 2.2.0 to 2.2.3 in /site (#3717) Bumps [json5](https://github.com/json5/json5) from 2.2.0 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.0...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * tests: add more logging around problematic test timeout (#3718) Trying to figure out why this test keeps timing out. * Refactor controller logic for getting RESTConfig to a remote cluster (#3712) We had two copies, rationalize and take the best of each. Also remove the HACK_ENABLE_LOOPBACK hack now that we can target remote clusters. * add a deletion approval flow with a validation webhook (#3678) * PackageVariant controller: implement pruning, deletionPolicy, and adoptionPolicy (#3701) * e2e: add delay after registering Repository (#3741) I believe this will help avoid the "failed to list resources" error immediately after registering a repository. * licensescan: fix ignore handling (#3740) The previous logic did not work correctly. * licensescan: Add licenses for more libraries. (#3736) Updating our database with the latest libraries, as needed by some other projects sharing this DB! * Docs: Updating 'Developing in Go' (#3715) * licensescan: Fix missing pipe character in README (#3739) The command is not correct without it. * RemoteRootSyncSet: able to specify a packageRef to a package (#3734) This makes it easy to apply packages we create. * chore: Upgrade cli-utils to v0.34.0 (#3746) Upgrades cli-utils to v0.34.0 which contains an upgrade to Go v1.18 and Kubernetes v1.25 resources. This PR was origininally authored by rquitales https://github.com/GoogleContainerTools/kpt/pull/3642 * rollouts: added top level directory * rollouts: scaffolded the project using kubebuilder (#3689) * rollouts: added cluster discovery and selection (#3696) * Rollouts package discovery (#3697) * rollouts: added remoterootsync API (#3698) * rollouts: add package cluster matcher (#3700) * rollouts: add AllAtOnce strategy (#3703) * rollouts: allow packages to be discovered from multiple repositories (#3702) * rollouts: rename packages git source to github (#3708) * rollouts: allow the root directory of a repository to be synced (#3709) * rollouts: add caching for discovered packages (#3706) * rollouts: add rolling update strategy (#3714) * rollouts: added API for ProgressiveRolloutStrategy (#3716) * rollouts: refine package to cluster matcher (#3720) * rollouts: implement progressive strategy (#3719) * rollouts: update progressive strategy to pause after wave (#3721) * rollouts: added skeleton CLI (#3724) * rollouts: added skeleton CLI * added table display * rollouts: add rollout summary status (#3725) * rollouts: tidy up go.mod/sum (#3726) * rollouts: duplicate target fix (#3727) * rollouts: sort cluster status list (#3728) * rollouts: conditionally show wave status (#3729) * rollouts: CLI now supports displaying waves and progress counts (#3730) * rollouts: cli can now advance waves on progressive rollouts (#3731) * rollouts: enable server side throttling for cli (#3732) * rollouts: add container cluster watch (#3738) * rollouts: delete remote root sync when no longer needed (#3742) Signed-off-by: dependabot[bot] Co-authored-by: Natasha Sarkar Co-authored-by: James Brook Co-authored-by: Morten Torkildsen Co-authored-by: Justin Santa Barbara Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: abangser Co-authored-by: Christopher Fry --- e2e/testdata/porch/rpkg-lifecycle/config.yaml | 2 +- .../packagevariantsets/config/rbac/role.yaml | 12 ++++++++++++ porch/go.mod | 7 +++++++ porch/test/e2e/e2e_test.go | 5 +++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/e2e/testdata/porch/rpkg-lifecycle/config.yaml b/e2e/testdata/porch/rpkg-lifecycle/config.yaml index 6aae1c4d3f..bb4db8e107 100644 --- a/e2e/testdata/porch/rpkg-lifecycle/config.yaml +++ b/e2e/testdata/porch/rpkg-lifecycle/config.yaml @@ -199,4 +199,4 @@ commands: - git-017a8366a5e0d9b35ae6dc489d4d3f68046d6034 - --namespace=rpkg-lifecycle stderr: "Error: the server could not find the requested resource (get packagerevisions.porch.kpt.dev git-017a8366a5e0d9b35ae6dc489d4d3f68046d6034) \n" - exitCode: 1 + exitCode: 1 \ No newline at end of file diff --git a/porch/controllers/packagevariantsets/config/rbac/role.yaml b/porch/controllers/packagevariantsets/config/rbac/role.yaml index dad1da23ee..f90bb0877d 100644 --- a/porch/controllers/packagevariantsets/config/rbac/role.yaml +++ b/porch/controllers/packagevariantsets/config/rbac/role.yaml @@ -32,6 +32,18 @@ rules: - patch - update - watch +- apiGroups: + - porch.kpt.dev + resources: + - packagerevisions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - config.porch.kpt.dev resources: diff --git a/porch/go.mod b/porch/go.mod index e89f34b97f..c0a401e159 100644 --- a/porch/go.mod +++ b/porch/go.mod @@ -53,7 +53,10 @@ require ( k8s.io/klog/v2 v2.70.1 k8s.io/kube-aggregator v0.24.0-beta.0 k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 +<<<<<<< HEAD sigs.k8s.io/cli-utils v0.34.0 +======= +>>>>>>> c96a7193 (Rollouts rebase kpt main (#3750)) sigs.k8s.io/controller-runtime v0.13.0 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/yaml v1.3.0 @@ -188,6 +191,10 @@ require ( k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/kubectl v0.25.3 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 // indirect +<<<<<<< HEAD +======= + sigs.k8s.io/cli-utils v0.34.0 // indirect +>>>>>>> c96a7193 (Rollouts rebase kpt main (#3750)) sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect diff --git a/porch/test/e2e/e2e_test.go b/porch/test/e2e/e2e_test.go index 8dbd1a04f6..61ee9f0f69 100644 --- a/porch/test/e2e/e2e_test.go +++ b/porch/test/e2e/e2e_test.go @@ -2152,6 +2152,11 @@ func (t *PorchSuite) registerGitRepositoryF(ctx context.Context, repo, name, dir }, }) + // TODO: Replace with readiness check or similar, once we get to CRDs + // Sometimes we see "failed to list resources" here, I believe because we need to wait for the repository to be crawled. + t.Logf("HACK: sleeping for 5 seconds to allow for repository registration") + time.Sleep(5 * time.Second) + t.Cleanup(func() { t.DeleteL(ctx, &configapi.Repository{ ObjectMeta: metav1.ObjectMeta{ From 54a1390e9d4bd4be769e3dfa0f8b3730e35eb2d9 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Wed, 25 Jan 2023 13:32:23 -0800 Subject: [PATCH 29/38] kpt cli: added rollouts subcommand (#3752) * kpt cli: added rollouts subcommand * fixed license check for k8s.io/klog * fixed linter warnings in rollouts cli --- commands/alpha/alphacmd.go | 2 + .../alpha/rollouts}/advance/advance.go | 10 +- .../alpha/rollouts}/get/get.go | 2 +- .../alpha/rollouts/rolloutsclient/client.go | 123 ++++++++++++ .../alpha/rollouts/rolloutscmd.go | 24 +-- .../alpha/rollouts}/status/status.go | 2 +- go.mod | 41 ++-- go.sum | 187 +++++------------- .../api/v1alpha1/groupversion_info.go | 4 +- .../api/v1alpha1/groupversion_info.go | 4 +- porch/go.mod | 48 +++-- porch/go.sum | 148 +++++--------- porch/pkg/oci/oci.go | 5 +- rollouts/Makefile | 5 - rollouts/go.mod | 5 - rollouts/go.sum | 14 -- scripts/create-licenses.sh | 2 + 17 files changed, 302 insertions(+), 324 deletions(-) rename {rollouts/cli => commands/alpha/rollouts}/advance/advance.go (88%) rename {rollouts/cli => commands/alpha/rollouts}/get/get.go (96%) create mode 100644 commands/alpha/rollouts/rolloutsclient/client.go rename rollouts/cli/main.go => commands/alpha/rollouts/rolloutscmd.go (71%) rename {rollouts/cli => commands/alpha/rollouts}/status/status.go (97%) diff --git a/commands/alpha/alphacmd.go b/commands/alpha/alphacmd.go index d8db3a9e84..f622c8de07 100644 --- a/commands/alpha/alphacmd.go +++ b/commands/alpha/alphacmd.go @@ -20,6 +20,7 @@ import ( "github.com/GoogleContainerTools/kpt/commands/alpha/license" "github.com/GoogleContainerTools/kpt/commands/alpha/live" "github.com/GoogleContainerTools/kpt/commands/alpha/repo" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts" "github.com/GoogleContainerTools/kpt/commands/alpha/rpkg" "github.com/GoogleContainerTools/kpt/commands/alpha/sync" "github.com/GoogleContainerTools/kpt/commands/alpha/wasm" @@ -54,6 +55,7 @@ func GetCommand(ctx context.Context, name, version string) *cobra.Command { wasm.NewCommand(ctx, version), live.GetCommand(ctx, "", version), license.NewCommand(ctx, version), + rollouts.NewCommand(ctx), ) return alpha diff --git a/rollouts/cli/advance/advance.go b/commands/alpha/rollouts/advance/advance.go similarity index 88% rename from rollouts/cli/advance/advance.go rename to commands/alpha/rollouts/advance/advance.go index 96c10a0106..30f25d985a 100644 --- a/rollouts/cli/advance/advance.go +++ b/commands/alpha/rollouts/advance/advance.go @@ -18,8 +18,8 @@ import ( "context" "fmt" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts/rolloutsclient" "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" - "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" "github.com/spf13/cobra" ) @@ -55,11 +55,11 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error { } if len(args) == 0 { - return fmt.Errorf("must provide rollout name\n") + return fmt.Errorf("must provide rollout name") } if len(args) == 1 { - return fmt.Errorf("must provide wave name\n") + return fmt.Errorf("must provide wave name") } rolloutName := args[0] @@ -72,7 +72,7 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error { } if rollout.Spec.Strategy.Type != v1alpha1.Progressive { - return fmt.Errorf("rollout must be using the progressive strategy to use this command\n") + return fmt.Errorf("rollout must be using the progressive strategy to use this command") } if rollout.Status.WaveStatuses != nil { @@ -86,7 +86,7 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error { } if !waveFound { - return fmt.Errorf("wave %q not found in this rollout\n", waveName) + return fmt.Errorf("wave %q not found in this rollout", waveName) } } diff --git a/rollouts/cli/get/get.go b/commands/alpha/rollouts/get/get.go similarity index 96% rename from rollouts/cli/get/get.go rename to commands/alpha/rollouts/get/get.go index 0a411c81cf..0b1d00a43c 100644 --- a/rollouts/cli/get/get.go +++ b/commands/alpha/rollouts/get/get.go @@ -18,8 +18,8 @@ import ( "context" "fmt" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts/rolloutsclient" rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" - "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" ) diff --git a/commands/alpha/rollouts/rolloutsclient/client.go b/commands/alpha/rollouts/rolloutsclient/client.go new file mode 100644 index 0000000000..01d58cbe79 --- /dev/null +++ b/commands/alpha/rollouts/rolloutsclient/client.go @@ -0,0 +1,123 @@ +// Copyright 2023 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 rolloutsclient + +import ( + "context" + "fmt" + "time" + + rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + coreapi "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "sigs.k8s.io/cli-utils/pkg/flowcontrol" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" +) + +// Client implments client for the rollouts API. +type Client struct { + client client.Client +} + +func New() (*Client, error) { + scheme, err := createScheme() + if err != nil { + return nil, err + } + + config := useServerSideThrottling(config.GetConfigOrDie()) + cl, err := client.New(config, client.Options{ + Scheme: scheme, + }) + if err != nil { + return nil, err + } + return &Client{client: cl}, nil +} + +func createScheme() (*runtime.Scheme, error) { + scheme := runtime.NewScheme() + + for _, api := range (runtime.SchemeBuilder{ + rolloutsapi.AddToScheme, + coreapi.AddToScheme, + metav1.AddMetaToScheme, + }) { + if err := api(scheme); err != nil { + return nil, err + } + } + return scheme, nil +} + +func (rlc *Client) List(ctx context.Context, ns string) (*rolloutsapi.RolloutList, error) { + if ns == "" { + ns = "default" + } + + rollouts := &rolloutsapi.RolloutList{} + if err := rlc.client.List(context.Background(), rollouts, client.InNamespace(ns)); err != nil { + return nil, err + } + + return rollouts, nil +} + +func (rlc *Client) Get(ctx context.Context, name string) (*rolloutsapi.Rollout, error) { + if name == "" { + return nil, fmt.Errorf("must provide rollout name") + } + + key := types.NamespacedName{ + Namespace: "default", + Name: name, + } + rollout := &rolloutsapi.Rollout{} + if err := rlc.client.Get(context.Background(), key, rollout); err != nil { + return nil, err + } + + return rollout, nil +} + +func (rlc *Client) Update(ctx context.Context, rollout *rolloutsapi.Rollout) error { + if err := rlc.client.Update(context.Background(), rollout); err != nil { + return err + } + + return nil +} + +func useServerSideThrottling(config *rest.Config) *rest.Config { + // Timeout if the query takes too long + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + enabled, err := flowcontrol.IsEnabled(ctx, config) + if err != nil { + fmt.Printf("Failed to query apiserver to check for flow control enablement: %v\n", err) + } + + if enabled { + config.QPS = -1 + config.Burst = -1 + } + + return config +} diff --git a/rollouts/cli/main.go b/commands/alpha/rollouts/rolloutscmd.go similarity index 71% rename from rollouts/cli/main.go rename to commands/alpha/rollouts/rolloutscmd.go index 386944ba6a..6b9b1c5322 100644 --- a/rollouts/cli/main.go +++ b/commands/alpha/rollouts/rolloutscmd.go @@ -12,26 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package rollouts import ( "context" - "os" - "github.com/GoogleContainerTools/kpt/rollouts/cli/advance" - "github.com/GoogleContainerTools/kpt/rollouts/cli/get" - "github.com/GoogleContainerTools/kpt/rollouts/cli/status" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts/advance" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts/get" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts/status" "github.com/spf13/cobra" - _ "k8s.io/client-go/plugin/pkg/client/auth" ) -func main() { - ctx := context.Background() - +func NewCommand(ctx context.Context) *cobra.Command { rolloutsCmd := &cobra.Command{ - Use: "cli", - Short: "cli ", - Long: "cli", + Use: "rollouts", + Short: "rollouts", + Long: "rollouts", RunE: func(cmd *cobra.Command, args []string) error { h, err := cmd.Flags().GetBool("help") if err != nil { @@ -49,7 +45,5 @@ func main() { get.NewCommand(ctx), status.NewCommand(ctx), ) - if err := rolloutsCmd.Execute(); err != nil { - os.Exit(-1) - } + return rolloutsCmd } diff --git a/rollouts/cli/status/status.go b/commands/alpha/rollouts/status/status.go similarity index 97% rename from rollouts/cli/status/status.go rename to commands/alpha/rollouts/status/status.go index dc07b75945..061002825b 100644 --- a/rollouts/cli/status/status.go +++ b/commands/alpha/rollouts/status/status.go @@ -18,8 +18,8 @@ import ( "context" "fmt" + "github.com/GoogleContainerTools/kpt/commands/alpha/rollouts/rolloutsclient" rolloutsapi "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" - "github.com/GoogleContainerTools/kpt/rollouts/rolloutsclient" "github.com/spf13/cobra" "github.com/jedib0t/go-pretty/v6/table" diff --git a/go.mod b/go.mod index 1180a02815..339fe8e4dd 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20230121152246-dc44dbd18a33 + github.com/GoogleContainerTools/kpt/rollouts v0.0.0-20230124224701-c96a71932235 github.com/bytecodealliance/wasmtime-go v0.39.0 github.com/cpuguy83/go-md2man/v2 v2.0.2 github.com/go-errors/errors v1.4.2 @@ -11,34 +12,36 @@ require ( github.com/google/go-containerregistry v0.11.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/igorsobreira/titlecase v0.0.0-20140109233139-4156b5b858ac + github.com/jedib0t/go-pretty/v6 v6.4.4 github.com/otiai10/copy v1.7.0 github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f github.com/prep/wasmexec v0.0.0-20220807105708-6554945c1dec github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 github.com/xlab/treeprint v1.1.0 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 - golang.org/x/text v0.3.7 + golang.org/x/mod v0.7.0 + golang.org/x/text v0.4.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible k8s.io/api v0.25.3 k8s.io/apiextensions-apiserver v0.25.3 - k8s.io/apimachinery v0.25.3 + k8s.io/apimachinery v0.25.4 k8s.io/cli-runtime v0.25.3 k8s.io/client-go v0.25.3 k8s.io/component-base v0.25.3 - k8s.io/klog/v2 v2.70.1 + k8s.io/klog/v2 v2.80.1 k8s.io/kubectl v0.25.3 sigs.k8s.io/cli-utils v0.34.0 - sigs.k8s.io/controller-runtime v0.13.0 + sigs.k8s.io/controller-runtime v0.13.1 sigs.k8s.io/kustomize/api v0.12.1 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/yaml v1.3.0 ) require ( - cloud.google.com/go/compute v1.7.0 // indirect + cloud.google.com/go/compute v1.12.1 // indirect + cloud.google.com/go/compute/metadata v0.2.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.27 // indirect @@ -68,9 +71,9 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/btree v1.0.1 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/imdario/mergo v0.3.12 // indirect @@ -82,9 +85,10 @@ require ( github.com/kr/pretty v0.2.1 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -97,6 +101,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect @@ -104,19 +109,19 @@ require ( github.com/spyzhov/ajson v0.7.1 // indirect github.com/vbatts/tar-split v0.11.2 // indirect go.starlark.net v0.0.0-20210901212718-87f333178d59 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect - golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 // indirect + k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) diff --git a/go.sum b/go.sum index ba8bfc726a..d280da10a1 100644 --- a/go.sum +++ b/go.sum @@ -26,29 +26,20 @@ cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSU cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= 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/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= 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/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= 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= @@ -60,7 +51,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo 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= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo= @@ -89,6 +79,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20230121152246-dc44dbd18a33 h1:9M1bvq7hU/JTY4VVcqhCQT0eAa5HznrFaLAm2ldfe70= github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20230121152246-dc44dbd18a33/go.mod h1:ASrhnLAL4ahTuiUJyepqcpVRXIoRMJyDs8/eSxwhgZM= +github.com/GoogleContainerTools/kpt/rollouts v0.0.0-20230124224701-c96a71932235 h1:iU8a95nm6UYk9JhLwLGKYFfbqWaepBHyjehdJrn9s28= +github.com/GoogleContainerTools/kpt/rollouts v0.0.0-20230124224701-c96a71932235/go.mod h1:q8E1T5TDBuhXa5+CNbooqIRNwEfho2f25mEVMGw1Z/s= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -152,12 +144,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/containerd/stargz-snapshotter/estargz v0.12.0 h1:idtwRTLjk2erqiYhPWy2L844By8NRFYEwYHcXhoIWPM= github.com/containerd/stargz-snapshotter/estargz v0.12.0/go.mod h1:AIQ59TewBFJ4GOPEQXujcrJ/EKxh5xXZegW1rkR1P/M= @@ -208,8 +195,6 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y 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/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= @@ -247,7 +232,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/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= @@ -337,8 +321,8 @@ github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4M github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= 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/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= @@ -355,15 +339,13 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= 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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.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= @@ -393,15 +375,9 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= @@ -475,6 +451,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jedib0t/go-pretty/v6 v6.4.4 h1:N+gz6UngBPF4M288kiMURPHELDMIhF/Em35aYuKrsSc= +github.com/jedib0t/go-pretty/v6 v6.4.4/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= @@ -569,6 +547,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -597,8 +577,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F 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/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -675,6 +655,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -711,6 +692,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQ github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 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= @@ -752,7 +735,6 @@ github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4l github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -777,8 +759,9 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -788,8 +771,10 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= @@ -885,8 +870,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -924,8 +909,8 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -976,14 +961,8 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= -golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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= @@ -1000,13 +979,8 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 h1:oVlhw3Oe+1reYsE2Nqu19PDJfLzwdU3QUUrG86rLK68= -golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1019,9 +993,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1095,30 +1068,17 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20220128215802-99c3d69c2c27/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-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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= @@ -1127,8 +1087,9 @@ 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/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1242,9 +1203,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= 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= @@ -1274,19 +1232,7 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= 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= @@ -1341,7 +1287,6 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D 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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1355,32 +1300,7 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1410,12 +1330,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1430,8 +1344,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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= +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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1483,23 +1397,22 @@ k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= +k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= @@ -1509,8 +1422,8 @@ 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/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= -sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= -sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= +sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= diff --git a/porch/controllers/rootsyncdeployments/api/v1alpha1/groupversion_info.go b/porch/controllers/rootsyncdeployments/api/v1alpha1/groupversion_info.go index 6866e81060..aa52eab1de 100644 --- a/porch/controllers/rootsyncdeployments/api/v1alpha1/groupversion_info.go +++ b/porch/controllers/rootsyncdeployments/api/v1alpha1/groupversion_info.go @@ -13,8 +13,8 @@ // limitations under the License. // Package v1alpha1 contains API Schema definitions for the config.porch.kpt.dev v1alpha1 API group -//+kubebuilder:object:generate=true -//+groupName=config.porch.kpt.dev +// +kubebuilder:object:generate=true +// +groupName=config.porch.kpt.dev package v1alpha1 import ( diff --git a/porch/controllers/rootsyncrollouts/api/v1alpha1/groupversion_info.go b/porch/controllers/rootsyncrollouts/api/v1alpha1/groupversion_info.go index 6866e81060..aa52eab1de 100644 --- a/porch/controllers/rootsyncrollouts/api/v1alpha1/groupversion_info.go +++ b/porch/controllers/rootsyncrollouts/api/v1alpha1/groupversion_info.go @@ -13,8 +13,8 @@ // limitations under the License. // Package v1alpha1 contains API Schema definitions for the config.porch.kpt.dev v1alpha1 API group -//+kubebuilder:object:generate=true -//+groupName=config.porch.kpt.dev +// +kubebuilder:object:generate=true +// +groupName=config.porch.kpt.dev package v1alpha1 import ( diff --git a/porch/go.mod b/porch/go.mod index c0a401e159..0513fb79de 100644 --- a/porch/go.mod +++ b/porch/go.mod @@ -28,7 +28,7 @@ require ( github.com/hexops/gotextdiff v1.0.3 github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 go.opentelemetry.io/otel v0.20.0 go.opentelemetry.io/otel/exporters/otlp v0.20.0 @@ -37,33 +37,39 @@ require ( go.opentelemetry.io/otel/sdk/metric v0.20.0 go.opentelemetry.io/otel/trace v0.20.0 golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 - golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 + golang.org/x/mod v0.7.0 + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 golang.org/x/tools v0.1.12 - google.golang.org/api v0.84.0 - google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 - google.golang.org/grpc v1.47.0 - google.golang.org/protobuf v1.28.0 + google.golang.org/api v0.100.0 + google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e + google.golang.org/grpc v1.50.1 + google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.25.3 - k8s.io/apimachinery v0.25.3 + k8s.io/apimachinery v0.25.4 k8s.io/apiserver v0.25.3 k8s.io/client-go v0.25.3 k8s.io/component-base v0.25.3 - k8s.io/klog/v2 v2.70.1 + k8s.io/klog/v2 v2.80.1 k8s.io/kube-aggregator v0.24.0-beta.0 +<<<<<<< HEAD k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 <<<<<<< HEAD sigs.k8s.io/cli-utils v0.34.0 ======= >>>>>>> c96a7193 (Rollouts rebase kpt main (#3750)) sigs.k8s.io/controller-runtime v0.13.0 +======= + k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 + sigs.k8s.io/controller-runtime v0.13.1 +>>>>>>> 987b25e4 (kpt cli: added rollouts subcommand (#3752)) sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/yaml v1.3.0 ) require ( - cloud.google.com/go/compute v1.7.0 // indirect + cloud.google.com/go/compute v1.12.1 // indirect + cloud.google.com/go/compute/metadata v0.2.1 // indirect github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect @@ -111,12 +117,12 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/btree v1.0.1 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa // indirect - github.com/googleapis/gax-go/v2 v2.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/googleapis/gax-go/v2 v2.6.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect @@ -132,7 +138,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -173,12 +179,12 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/porch/go.sum b/porch/go.sum index 7db298e9f9..bff4c6f5f9 100644 --- a/porch/go.sum +++ b/porch/go.sum @@ -30,8 +30,7 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0 h1:DAq3r8y4mDgyB/ZPJ9v/5VJNqjgJAxTn6ZYLlUywOu8= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8= 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= @@ -40,11 +39,10 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/container v1.2.0 h1:LPKlQa4XfBTWdaBSDx/KQ/v45l8FDRzSV0tDpU6e/38= cloud.google.com/go/container v1.2.0/go.mod h1:Cj2AgMsCUfMVfbGh0Fx7u5Ah/qeC0ajLrqqGGiAdCGw= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -64,7 +62,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo 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= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks= @@ -204,7 +201,6 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= @@ -276,7 +272,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m 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/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= @@ -429,8 +424,9 @@ github.com/golangci/revgrep v0.0.0-20210930125155-c22e5001d4f2/go.mod h1:LK+zW4M github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= 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/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= @@ -449,14 +445,14 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.11.0 h1:Xt8x1adcREjFcmDoDK8OdOsjxu90PHkGuwNP8GiHMLM= github.com/google/go-containerregistry v0.11.0/go.mod h1:BBaYtsHPHA42uEgAvd/NejvAfPSlz281sJWqupjSxfk= 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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.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= @@ -486,18 +482,15 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa h1:7MYGT2XEMam7Mtzv1yDUYXANedWvwk3HKkR3MyGowy8= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= @@ -715,8 +708,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F 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/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -898,7 +891,6 @@ github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag07 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -928,8 +920,9 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -940,8 +933,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= @@ -1103,8 +1097,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= @@ -1145,8 +1139,8 @@ 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.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1205,13 +1199,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= -golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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= @@ -1229,12 +1218,8 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 h1:oVlhw3Oe+1reYsE2Nqu19PDJfLzwdU3QUUrG86rLK68= -golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 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= @@ -1247,9 +1232,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1341,20 +1325,15 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/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-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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= @@ -1363,8 +1342,9 @@ 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/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1484,10 +1464,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1525,13 +1502,8 @@ google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3h google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0 h1:NMB9J4cCxs9xEm+1Z9QiO3eFvn7EnQj3Eo3hN6ugVlg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.100.0 h1:LGUYIrbW9pzYQQ8NWXlaIVkgnfubVBZbMFb9P8TK374= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= 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= @@ -1587,7 +1559,6 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D 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-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1614,20 +1585,8 @@ google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 h1:4SPz2GL2CXJt28MTF8V6Ap/9ZiVbQlJeGSd9qtA7DLs= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e h1:S9GbmC1iCgvbLyAokVCwiO6tVIrU9Y7c5oMx1V/ki/Y= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1660,11 +1619,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1679,8 +1635,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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= +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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1740,8 +1696,8 @@ k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= k8s.io/apimachinery v0.24.0-beta.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= +k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apiserver v0.24.0-beta.0/go.mod h1:pPF5/dEQukQjwauSxjW8IHKn8W1r8u2nERGojwFFlj4= k8s.io/apiserver v0.25.3 h1:m7+xGuG5+KYAnEsqaFtDyWMkmMMEOFYlu+NlWv5qSBI= k8s.io/apiserver v0.25.3/go.mod h1:9bT47iM2fzRuhICJpM/RcQR9sqDDfZ7Yw60h0p3JW08= @@ -1760,8 +1716,8 @@ k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAE 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/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-aggregator v0.24.0-beta.0 h1:pRl/qwCw2XBuWrMzkTiWOFfONZtClgHWdbKmIZdDY8o= k8s.io/kube-aggregator v0.24.0-beta.0/go.mod h1:i9SRRGwxhXwuNuArHlkfy/EuWSrrbUEsIah/7OROTZU= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= @@ -1772,8 +1728,8 @@ k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 h1:GfD9OzL11kvZN5iArC6oTS7RTj7oJOIfnislxYlqTj8= +k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= @@ -1786,8 +1742,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 h1:LYqFq+6Cj2D0g sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33/go.mod h1:soWkSNf2tZC7aMibXEqVhCd73GOY5fJikn8qbdzemB0= sigs.k8s.io/cli-utils v0.34.0 h1:zCUitt54f0/MYj/ajVFnG6XSXMhpZ72O/3RewIchW8w= sigs.k8s.io/cli-utils v0.34.0/go.mod h1:EXyMwPMu9OL+LRnj0JEMsGG/fRvbgFadcVlSnE8RhFs= -sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= -sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= +sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= diff --git a/porch/pkg/oci/oci.go b/porch/pkg/oci/oci.go index 2729ad0f1e..f39a7b0dc7 100644 --- a/porch/pkg/oci/oci.go +++ b/porch/pkg/oci/oci.go @@ -469,8 +469,9 @@ func (p *ociPackageRevision) Lifecycle() v1alpha1.PackageRevisionLifecycle { } // UpdateLifecycle should update the package revision lifecycle from DeletionProposed to Published or vice versa. -// This function is currently only partially implemented; it still needs to store whether the package has been -// proposed for deletion somewhere in OCI, probably as another OCI image with a "deletionProposed" tag. +// +// This function is currently only partially implemented; it still needs to store whether the package has been +// proposed for deletion somewhere in OCI, probably as another OCI image with a "deletionProposed" tag. func (p *ociPackageRevision) UpdateLifecycle(ctx context.Context, new v1alpha1.PackageRevisionLifecycle) error { old := p.Lifecycle() diff --git a/rollouts/Makefile b/rollouts/Makefile index b1f1bdc474..2cc041386e 100644 --- a/rollouts/Makefile +++ b/rollouts/Makefile @@ -82,11 +82,6 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go - -.PHONY: cli -cli: - go build -o ./bin/rollouts ./cli - # If you wish built the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ diff --git a/rollouts/go.mod b/rollouts/go.mod index 3ed65c9b0e..ad94a7822d 100644 --- a/rollouts/go.mod +++ b/rollouts/go.mod @@ -9,10 +9,8 @@ require ( github.com/google/cel-go v0.13.0 github.com/google/go-cmp v0.5.9 github.com/google/go-github/v48 v48.2.0 - github.com/jedib0t/go-pretty/v6 v6.4.4 github.com/onsi/ginkgo/v2 v2.2.0 github.com/onsi/gomega v1.20.2 - github.com/spf13/cobra v1.5.0 golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 google.golang.org/api v0.103.0 google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c @@ -56,11 +54,9 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.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/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -70,7 +66,6 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/rollouts/go.sum b/rollouts/go.sum index b989fc039c..9d3e42e18f 100644 --- a/rollouts/go.sum +++ b/rollouts/go.sum @@ -87,7 +87,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk 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.2/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= @@ -223,10 +222,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jedib0t/go-pretty/v6 v6.4.4 h1:N+gz6UngBPF4M288kiMURPHELDMIhF/Em35aYuKrsSc= -github.com/jedib0t/go-pretty/v6 v6.4.4/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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= @@ -256,8 +251,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN 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-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -285,7 +278,6 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -310,17 +302,12 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= 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 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= @@ -336,7 +323,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= diff --git a/scripts/create-licenses.sh b/scripts/create-licenses.sh index a5ea045ab2..9403fe7665 100755 --- a/scripts/create-licenses.sh +++ b/scripts/create-licenses.sh @@ -171,6 +171,8 @@ V2_LICENSE_DIR="vendor/github.com/cpuguy83/go-md2man" mv ${V2_LICENSE_DIR}/v2/LICENSE ${V2_LICENSE_DIR} GO_RESTFUL_LICENSE_DIR="vendor/github.com/emicklei/go-restful" mv ${GO_RESTFUL_LICENSE_DIR}/v3/LICENSE ${GO_RESTFUL_LICENSE_DIR} +KLOG_LICENSE_DIR="vendor/k8s.io/klog" +mv ${KLOG_LICENSE_DIR}/v2/LICENSE ${KLOG_LICENSE_DIR} # Loop through every vendored package mozilla_repos=() From 19c88bf0f0e1a233ff5fcad16a6b8caec60d5b60 Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Wed, 25 Jan 2023 14:18:02 -0800 Subject: [PATCH 30/38] rollouts: added cluster discovery types (#3753) --- internal/testutil/pkgbuilder/builder_test.go | 14 ++++++++++++++ rollouts/api/v1alpha1/rollout_types.go | 18 +++++++++++++++++- rollouts/api/v1alpha1/zz_generated.deepcopy.go | 16 ++++++++++++++++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 12 ++++++++++++ .../samples/gitops_rollout_cert_manager.yaml | 2 ++ ...itops_rollout_cert_manager_progressive.yaml | 2 ++ .../samples/gitops_rollout_kpt_samples.yaml | 2 ++ .../config/samples/gitops_rollout_oahu.yaml | 2 ++ .../samples/gitops_rollout_repo_selector.yaml | 2 ++ tools/licensescan/licensedb_test.go | 14 ++++++++++++++ 10 files changed, 83 insertions(+), 1 deletion(-) diff --git a/internal/testutil/pkgbuilder/builder_test.go b/internal/testutil/pkgbuilder/builder_test.go index 7e7e0ae871..b59c31f930 100644 --- a/internal/testutil/pkgbuilder/builder_test.go +++ b/internal/testutil/pkgbuilder/builder_test.go @@ -1,3 +1,17 @@ +// Copyright 2023 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 pkgbuilder import ( diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 2143ac0da5..127ae71c39 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -20,7 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // RolloutSpec defines the desired state of Rollout @@ -28,6 +27,9 @@ type RolloutSpec struct { // Description is a user friendly description of this Rollout. Description string `json:"description,omitempty"` + // Clusters specifies the source for discovering the clusters. + Clusters ClusterDiscovery `json:"clusters"` + // Packages source for this Rollout. Packages PackagesConfig `json:"packages"` @@ -51,6 +53,20 @@ type ClusterRef struct { Name string `json:"name"` } +// different types of cluster sources +const ( + KCC ClusterSourceType = "KCC" + GCPFleet ClusterSourceType = "GCPFleet" +) + +// +kubebuilder:validation:Enum=KCC;GCPFleet +type ClusterSourceType string + +// ClusterDiscovery represents configuration needed to discover clusters. +type ClusterDiscovery struct { + SourceType ClusterSourceType `json:"sourceType"` +} + const ( GitHub PackageSourceType = "GitHub" ) diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 0337dc8b67..94cba66723 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,21 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterDiscovery) DeepCopyInto(out *ClusterDiscovery) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDiscovery. +func (in *ClusterDiscovery) DeepCopy() *ClusterDiscovery { + if in == nil { + return nil + } + out := new(ClusterDiscovery) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterRef) DeepCopyInto(out *ClusterRef) { *out = *in @@ -447,6 +462,7 @@ func (in *RolloutList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { *out = *in + out.Clusters = in.Clusters out.Packages = in.Packages in.Targets.DeepCopyInto(&out.Targets) out.PackageToTargetMatcher = in.PackageToTargetMatcher diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index fed4a9dda0..cfe29aebc5 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -49,6 +49,17 @@ spec: spec: description: RolloutSpec defines the desired state of Rollout properties: + clusters: + description: Clusters specifies the source for discovering the clusters. + properties: + sourceType: + enum: + - KCC + - GCPFleet + type: string + required: + - sourceType + type: object description: description: Description is a user friendly description of this Rollout. type: string @@ -211,6 +222,7 @@ spec: x-kubernetes-map-type: atomic type: object required: + - clusters - packageToTargetMatcher - packages - strategy diff --git a/rollouts/config/samples/gitops_rollout_cert_manager.yaml b/rollouts/config/samples/gitops_rollout_cert_manager.yaml index f08a757c48..560d73699d 100644 --- a/rollouts/config/samples/gitops_rollout_cert_manager.yaml +++ b/rollouts/config/samples/gitops_rollout_cert_manager.yaml @@ -18,6 +18,8 @@ metadata: name: cert-manager-rollout spec: description: kpt samples rollout + clusters: + sourceType: KCC packages: sourceType: GitHub github: diff --git a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml index 7f16e254b7..1fac144bca 100644 --- a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml +++ b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml @@ -18,6 +18,8 @@ metadata: name: cm-progressive spec: description: kpt samples rollout + clusters: + sourceType: KCC packages: sourceType: GitHub github: diff --git a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml index fa65e4166b..ec88fbedd3 100644 --- a/rollouts/config/samples/gitops_rollout_kpt_samples.yaml +++ b/rollouts/config/samples/gitops_rollout_kpt_samples.yaml @@ -18,6 +18,8 @@ metadata: name: rollout-kpt-samples spec: description: kpt samples rollout + clusters: + sourceType: KCC packages: sourceType: GitHub github: diff --git a/rollouts/config/samples/gitops_rollout_oahu.yaml b/rollouts/config/samples/gitops_rollout_oahu.yaml index 38445aa61b..50a20d6194 100644 --- a/rollouts/config/samples/gitops_rollout_oahu.yaml +++ b/rollouts/config/samples/gitops_rollout_oahu.yaml @@ -18,6 +18,8 @@ metadata: name: rollout-oahu spec: description: oahu rollout + clusters: + sourceType: KCC packages: sourceType: GitHub github: diff --git a/rollouts/config/samples/gitops_rollout_repo_selector.yaml b/rollouts/config/samples/gitops_rollout_repo_selector.yaml index 02bc875908..b3bedb709a 100644 --- a/rollouts/config/samples/gitops_rollout_repo_selector.yaml +++ b/rollouts/config/samples/gitops_rollout_repo_selector.yaml @@ -18,6 +18,8 @@ metadata: name: rollout-repo-selector spec: description: repo selector rollout + clusters: + sourceType: KCC packages: sourceType: GitHub github: diff --git a/tools/licensescan/licensedb_test.go b/tools/licensescan/licensedb_test.go index 757735a308..3aeceeb3b8 100644 --- a/tools/licensescan/licensedb_test.go +++ b/tools/licensescan/licensedb_test.go @@ -1,3 +1,17 @@ +// Copyright 2023 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 ( From 29d8cf8ee9a10be21666b5d698523152bb0e726d Mon Sep 17 00:00:00 2001 From: Sunil Arora Date: Thu, 26 Jan 2023 09:23:02 -0800 Subject: [PATCH 31/38] rollouts: added sync template to the rollout API (#3754) --- rollouts/api/v1alpha1/rollout_types.go | 30 +++++++ .../api/v1alpha1/zz_generated.deepcopy.go | 70 +++++++++++++++ .../crd/bases/gitops.kpt.dev_rollouts.yaml | 88 +++++++++++++++++++ ...tops_rollout_cert_manager_progressive.yaml | 4 + 4 files changed, 192 insertions(+) diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 127ae71c39..09d6626479 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -39,6 +39,9 @@ type RolloutSpec struct { // PackageToTargetMatcher specifies the clusters that will receive a specific package. PackageToTargetMatcher PackageToClusterMatcher `json:"packageToTargetMatcher"` + // SyncTemplate defines the type and attributes for the RSync object used to syncing the packages. + SyncTemplate *SyncTemplate `json:"syncTemplate,omitempty"` + // Strategy specifies the rollout strategy to use for this rollout. Strategy RolloutStrategy `json:"strategy"` } @@ -101,6 +104,33 @@ type SecretReference struct { Name string `json:"name,omitempty"` } +// different types of sync templates. +const ( + TemplateTypeRootSync SyncTemplateType = "RootSync" + TemplateTypeRepoSync SyncTemplateType = "RepoSync" +) + +// +kubebuilder:validation:Enum=RootSync;RepoSync +type SyncTemplateType string + +// SyncTemplate defines the configuration for RSync templates. +type SyncTemplate struct { + Type SyncTemplateType `json:"type"` + RootSync *RootSyncTemplate `json:"rootSync,omitempty"` + RepoSync *RepoSyncTemplate `json:"repoSync,omitempty"` +} + +// RootSyncTemplate represent the sync template for RootSync. +type RootSyncTemplate struct { + SourceFormat string `json:"sourceFormat,omitempty"` + Git *GitInfo `json:"git,omitempty"` +} + +type RepoSyncTemplate struct { + SourceFormat string `json:"sourceFormat,omitempty"` + Git *GitInfo `json:"git,omitempty"` +} + // +kubebuilder:validation:Enum=AllClusters;Custom type MatcherType string diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 94cba66723..6ce983231f 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -400,6 +400,26 @@ func (in *RemoteRootSyncStatus) DeepCopy() *RemoteRootSyncStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RepoSyncTemplate) DeepCopyInto(out *RepoSyncTemplate) { + *out = *in + if in.Git != nil { + in, out := &in.Git, &out.Git + *out = new(GitInfo) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RepoSyncTemplate. +func (in *RepoSyncTemplate) DeepCopy() *RepoSyncTemplate { + if in == nil { + return nil + } + out := new(RepoSyncTemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Rollout) DeepCopyInto(out *Rollout) { *out = *in @@ -466,6 +486,11 @@ func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { out.Packages = in.Packages in.Targets.DeepCopyInto(&out.Targets) out.PackageToTargetMatcher = in.PackageToTargetMatcher + if in.SyncTemplate != nil { + in, out := &in.SyncTemplate, &out.SyncTemplate + *out = new(SyncTemplate) + (*in).DeepCopyInto(*out) + } in.Strategy.DeepCopyInto(&out.Strategy) } @@ -583,6 +608,26 @@ func (in *RootSyncSpec) DeepCopy() *RootSyncSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RootSyncTemplate) DeepCopyInto(out *RootSyncTemplate) { + *out = *in + if in.Git != nil { + in, out := &in.Git, &out.Git + *out = new(GitInfo) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootSyncTemplate. +func (in *RootSyncTemplate) DeepCopy() *RootSyncTemplate { + if in == nil { + return nil + } + out := new(RootSyncTemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretReference) DeepCopyInto(out *SecretReference) { *out = *in @@ -644,6 +689,31 @@ func (in *StrategyRollingUpdate) DeepCopy() *StrategyRollingUpdate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SyncTemplate) DeepCopyInto(out *SyncTemplate) { + *out = *in + if in.RootSync != nil { + in, out := &in.RootSync, &out.RootSync + *out = new(RootSyncTemplate) + (*in).DeepCopyInto(*out) + } + if in.RepoSync != nil { + in, out := &in.RepoSync, &out.RepoSync + *out = new(RepoSyncTemplate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SyncTemplate. +func (in *SyncTemplate) DeepCopy() *SyncTemplate { + if in == nil { + return nil + } + out := new(SyncTemplate) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Wave) DeepCopyInto(out *Wave) { *out = *in diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index cfe29aebc5..ad898309be 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -168,6 +168,94 @@ spec: required: - type type: object + syncTemplate: + description: SyncTemplate defines the type and attributes for the + RSync object used to syncing the packages. + properties: + repoSync: + properties: + git: + properties: + auth: + type: string + branch: + type: string + dir: + type: string + gcpServiceAccountEmail: + type: string + noSSLVerify: + type: boolean + period: + type: string + proxy: + type: string + repo: + type: string + revision: + type: string + secretRef: + description: SecretReference contains the reference to + the secret + properties: + name: + description: Name represents the secret name + type: string + type: object + required: + - auth + - repo + type: object + sourceFormat: + type: string + type: object + rootSync: + description: RootSyncTemplate represent the sync template for + RootSync. + properties: + git: + properties: + auth: + type: string + branch: + type: string + dir: + type: string + gcpServiceAccountEmail: + type: string + noSSLVerify: + type: boolean + period: + type: string + proxy: + type: string + repo: + type: string + revision: + type: string + secretRef: + description: SecretReference contains the reference to + the secret + properties: + name: + description: Name represents the secret name + type: string + type: object + required: + - auth + - repo + type: object + sourceFormat: + type: string + type: object + type: + enum: + - RootSync + - RepoSync + type: string + required: + - type + type: object targets: description: Targets specifies the clusters that will receive the KRM config packages. diff --git a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml index 1fac144bca..9d83d28fff 100644 --- a/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml +++ b/rollouts/config/samples/gitops_rollout_cert_manager_progressive.yaml @@ -34,6 +34,10 @@ spec: - {key: env, operator: In, values: [dev, staging]} packageToTargetMatcher: type: AllClusters + syncTemplate: + type: RootSync + rootSync: + sourceFormat: unstructured strategy: type: Progressive progressive: From ac9024817bd36cbd67e146377c84f03b2a403c0c Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 7 Feb 2023 12:24:36 -0500 Subject: [PATCH 32/38] rollouts: refactor logic for generating cluster client (#3790) --- .../controllers/remoterootsync_controller.go | 33 ++++++++++--------- rollouts/pkg/clusterstore/clusterstore.go | 21 +++++------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/rollouts/controllers/remoterootsync_controller.go b/rollouts/controllers/remoterootsync_controller.go index f20eb26eb8..3d2a39651c 100644 --- a/rollouts/controllers/remoterootsync_controller.go +++ b/rollouts/controllers/remoterootsync_controller.go @@ -132,14 +132,8 @@ func (r *RemoteRootSyncReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } - cr := &remoterootsync.Spec.ClusterRef - - gkeCluster, err := r.store.GetCluster(ctx, cr.Name) - if err != nil { - return ctrl.Result{}, err - } - - _, dynCl, err := r.store.GetClusterClient(ctx, gkeCluster) + clusterRef := &remoterootsync.Spec.ClusterRef + dynCl, err := r.getDynamicClientForCluster(ctx, clusterRef) if err != nil { return ctrl.Result{}, err } @@ -274,13 +268,8 @@ func BuildObjectsToApply(remoterootsync *gitopsv1alpha1.RemoteRootSync) (*unstru } func (r *RemoteRootSyncReconciler) deleteExternalResources(ctx context.Context, remoterootsync *gitopsv1alpha1.RemoteRootSync) error { - - gkeCluster, err := r.store.GetCluster(ctx, remoterootsync.Spec.ClusterRef.Name) - if err != nil { - return err - } - - _, dynCl, err := r.store.GetClusterClient(ctx, gkeCluster) + clusterRef := &remoterootsync.Spec.ClusterRef + dynCl, err := r.getDynamicClientForCluster(ctx, clusterRef) if err != nil { return err } @@ -294,6 +283,20 @@ func (r *RemoteRootSyncReconciler) deleteExternalResources(ctx context.Context, return err } +func (r *RemoteRootSyncReconciler) getDynamicClientForCluster(ctx context.Context, clusterRef *gitopsv1alpha1.ClusterRef) (dynamic.Interface, error) { + restConfig, err := r.store.GetRESTConfig(ctx, clusterRef.Name) + if err != nil { + return nil, err + } + + dynamicClient, err := dynamic.NewForConfig(restConfig) + if err != nil { + return nil, err + } + + return dynamicClient, nil +} + // SetupWithManager sets up the controller with the Manager. func (r *RemoteRootSyncReconciler) SetupWithManager(mgr ctrl.Manager) error { r.channel = make(chan event.GenericEvent, 10) diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index 20625229c2..2cb87de3c3 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -30,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) @@ -93,7 +92,7 @@ func (cs *ClusterStore) PrintClusterInfos(ctx context.Context, clusters *gkeclus } } -func (cs *ClusterStore) GetCluster(ctx context.Context, name string) (*gkeclusterapis.ContainerCluster, error) { +func (cs *ClusterStore) getCluster(ctx context.Context, name string) (*gkeclusterapis.ContainerCluster, error) { gkeCluster := gkeclusterapis.ContainerCluster{} clusterKey := client.ObjectKey{ Namespace: "config-control", @@ -106,20 +105,18 @@ func (cs *ClusterStore) GetCluster(ctx context.Context, name string) (*gkecluste return &gkeCluster, nil } -func (cs *ClusterStore) GetClusterClient(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (client.Client, dynamic.Interface, error) { - clusterClientConfig, err := cs.getRESTConfig(ctx, cluster) +func (cs *ClusterStore) GetRESTConfig(ctx context.Context, name string) (*rest.Config, error) { + cluster, err := cs.getCluster(ctx, name) if err != nil { - return nil, nil, err - } - cl, err := client.New(clusterClientConfig, client.Options{}) - if err != nil { - return nil, nil, err + return nil, err } - dynCl, err := dynamic.NewForConfig(clusterClientConfig) + + restConfig, err := cs.getRESTConfig(ctx, cluster) if err != nil { - return nil, nil, err + return nil, err } - return cl, dynCl, err + + return restConfig, nil } func (cs *ClusterStore) listClusters(ctx context.Context, selector *metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { From ccb0caa7a3849c54254be29be79c7946a1f4757f Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 7 Feb 2023 12:24:54 -0500 Subject: [PATCH 33/38] rollouts: update dockerfile (#3789) --- rollouts/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/rollouts/Dockerfile b/rollouts/Dockerfile index 2b3d768732..8152c15a87 100644 --- a/rollouts/Dockerfile +++ b/rollouts/Dockerfile @@ -29,6 +29,7 @@ RUN go mod download COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ +COPY pkg/ pkg/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command From f16704c16ebd80ce71cd4c7881c446df598e2250 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 7 Feb 2023 13:00:45 -0500 Subject: [PATCH 34/38] rollouts: update core logic to be agnostic of exact cluster type (#3791) --- rollouts/controllers/rollout_controller.go | 14 ++++----- rollouts/pkg/clusterstore/clusterstore.go | 31 +++++++++++++------ .../packageclustermatcher.go | 12 +++---- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 1f38e4885f..6f2eb9be2c 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -207,7 +207,7 @@ func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Conte } clusterWaveMap := make(map[string]string) - for _, cluster := range allClusters.Items { + for _, cluster := range allClusters { clusterWaveMap[cluster.Name] = "" } @@ -224,11 +224,11 @@ func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Conte return err } - if len(waveClusters.Items) == 0 { + if len(waveClusters) == 0 { return fmt.Errorf("wave %q does not target any clusters", wave.Name) } - for _, cluster := range waveClusters.Items { + for _, cluster := range waveClusters { currentClusterWave, found := clusterWaveMap[cluster.Name] if !found { // this should never happen @@ -245,7 +245,7 @@ func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Conte pauseWaveNameFound = pauseWaveNameFound || pauseAfterWaveName == wave.Name } - for _, cluster := range allClusters.Items { + for _, cluster := range allClusters { wave, _ := clusterWaveMap[cluster.Name] if wave == "" { return fmt.Errorf("waves should cover all clusters selected by the rollout - cluster %s is not covered by any waves", cluster.Name) @@ -283,7 +283,7 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop } logger.Info("discovered packages", "count", len(discoveredPackages), "packages", discoveredPackages) - packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(targetClusters.Items, discoveredPackages) + packageClusterMatcherClient := packageclustermatcher.NewPackageClusterMatcher(targetClusters, discoveredPackages) clusterPackages, err := packageClusterMatcherClient.GetClusterPackages(rollout.Spec.PackageToTargetMatcher) if err != nil { return err @@ -442,7 +442,7 @@ func (r *RolloutReconciler) getWaveTargets(ctx context.Context, rollout *gitopsv return nil, err } - for _, cluster := range waveClusters.Items { + for _, cluster := range waveClusters { clusterNameToWaveTarget[cluster.Name] = &thisWaveTarget } @@ -624,7 +624,7 @@ type Targets struct { } type clusterPackagePair struct { - cluster *gkeclusterapis.ContainerCluster + cluster *clusterstore.Cluster packageRef *packagediscovery.DiscoveredPackage } diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index 2cb87de3c3..5f30d60dec 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -42,6 +42,11 @@ type ClusterStore struct { WorkloadIdentityHelper } +type Cluster struct { + Name string + Labels map[string]string +} + func (cs *ClusterStore) Init() error { if err := cs.WorkloadIdentityHelper.Init(cs.Config); err != nil { return err @@ -49,7 +54,7 @@ func (cs *ClusterStore) Init() error { return nil } -func (cs *ClusterStore) ListClusters(ctx context.Context, selectors ...*metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { +func (cs *ClusterStore) ListClusters(ctx context.Context, selectors ...*metav1.LabelSelector) ([]Cluster, error) { gkeClusters, err := cs.listClusters(ctx, selectors[0]) if err != nil { return nil, err @@ -79,17 +84,14 @@ func (cs *ClusterStore) ListClusters(ctx context.Context, selectors ...*metav1.L return strings.Compare(gkeClusters.Items[i].Name, gkeClusters.Items[j].Name) == -1 }) - return gkeClusters, nil -} + clusters := []Cluster{} -func (cs *ClusterStore) PrintClusterInfos(ctx context.Context, clusters *gkeclusterapis.ContainerClusterList) { - logger := log.FromContext(ctx) - for _, gkeCluster := range clusters.Items { - logger.Info("gke clusters", "namespace", gkeCluster.Namespace, "name", gkeCluster.Name) - for _, cond := range gkeCluster.Status.Conditions { - logger.Info("gke cluster", "name", gkeCluster.Name, "condition", cond) - } + for _, containerCluster := range gkeClusters.Items { + cluster := toCluster(&containerCluster) + clusters = append(clusters, cluster) } + + return clusters, nil } func (cs *ClusterStore) getCluster(ctx context.Context, name string) (*gkeclusterapis.ContainerCluster, error) { @@ -199,3 +201,12 @@ func (cs *ClusterStore) getConfigConnectorContextTokenSource(ctx context.Context } return cs.WorkloadIdentityHelper.GetGcloudAccessTokenSource(ctx, kubeServiceAccount, googleServiceAccount) } + +func toCluster(containerCluster *gkeclusterapis.ContainerCluster) Cluster { + cluster := Cluster{ + Name: containerCluster.Name, + Labels: containerCluster.Labels, + } + + return cluster +} diff --git a/rollouts/pkg/packageclustermatcher/packageclustermatcher.go b/rollouts/pkg/packageclustermatcher/packageclustermatcher.go index 57a189707f..71d7302861 100644 --- a/rollouts/pkg/packageclustermatcher/packageclustermatcher.go +++ b/rollouts/pkg/packageclustermatcher/packageclustermatcher.go @@ -17,8 +17,8 @@ package packageclustermatcher import ( "fmt" - gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + "github.com/GoogleContainerTools/kpt/rollouts/pkg/clusterstore" "github.com/GoogleContainerTools/kpt/rollouts/pkg/packagediscovery" "github.com/google/cel-go/cel" @@ -26,16 +26,16 @@ import ( ) type PackageClusterMatcher struct { - clusters []gkeclusterapis.ContainerCluster + clusters []clusterstore.Cluster packages []packagediscovery.DiscoveredPackage } type ClusterPackages struct { - Cluster gkeclusterapis.ContainerCluster + Cluster clusterstore.Cluster Packages []packagediscovery.DiscoveredPackage } -func NewPackageClusterMatcher(clusters []gkeclusterapis.ContainerCluster, packages []packagediscovery.DiscoveredPackage) *PackageClusterMatcher { +func NewPackageClusterMatcher(clusters []clusterstore.Cluster, packages []packagediscovery.DiscoveredPackage) *PackageClusterMatcher { return &PackageClusterMatcher{ clusters: clusters, packages: packages, @@ -56,8 +56,8 @@ func (m *PackageClusterMatcher) GetClusterPackages(matcher gitopsv1alpha1.Packag matchedPackages = packages case gitopsv1alpha1.CustomMatcher: celCluster := map[string]interface{}{ - "name": cluster.ObjectMeta.Name, - "labels": cluster.ObjectMeta.Labels, + "name": cluster.Name, + "labels": cluster.Labels, } for _, discoveredPackage := range packages { From 28f46ec26256098c73a3bacb30e518db3e6154bd Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 7 Feb 2023 16:35:13 -0500 Subject: [PATCH 35/38] rollouts: filter wave clusters in process (#3793) --- rollouts/controllers/rollout_controller.go | 26 ++++++++++++++++++---- rollouts/pkg/clusterstore/clusterstore.go | 24 ++------------------ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 6f2eb9be2c..5ca5b779a2 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -219,7 +219,7 @@ func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Conte } for _, wave := range strategy.Spec.Waves { - waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) + waveClusters, err := filterClusters(allClusters, wave.Targets.Selector) if err != nil { return err } @@ -297,7 +297,7 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop allClusterStatuses := []gitopsv1alpha1.ClusterStatus{} waveStatuses := []gitopsv1alpha1.WaveStatus{} - allWaveTargets, err := r.getWaveTargets(ctx, rollout, targets, strategy.Spec.Waves) + allWaveTargets, err := r.getWaveTargets(ctx, rollout, targets, targetClusters, strategy.Spec.Waves) if err != nil { return err } @@ -427,7 +427,7 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, return targets, nil } -func (r *RolloutReconciler) getWaveTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, allTargets *Targets, +func (r *RolloutReconciler) getWaveTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, allTargets *Targets, allClusters []clusterstore.Cluster, allWaves []gitopsv1alpha1.Wave) ([]WaveTarget, error) { allWaveTargets := []WaveTarget{} @@ -437,7 +437,7 @@ func (r *RolloutReconciler) getWaveTargets(ctx context.Context, rollout *gitopsv wave := allWaves[i] thisWaveTarget := WaveTarget{Wave: &wave, Targets: &Targets{}} - waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector) + waveClusters, err := filterClusters(allClusters, wave.Targets.Selector) if err != nil { return nil, err } @@ -876,3 +876,21 @@ func rolloutIncludesCluster(rollout *gitopsv1alpha1.Rollout, clusterName string) return false } + +func filterClusters(allClusters []clusterstore.Cluster, labelSelector *metav1.LabelSelector) ([]clusterstore.Cluster, error) { + clusters := []clusterstore.Cluster{} + + for _, cluster := range allClusters { + clusterLabelSet := labels.Set(cluster.Labels) + selector, err := metav1.LabelSelectorAsSelector(labelSelector) + if err != nil { + return nil, err + } + + if selector.Matches(clusterLabelSet) { + clusters = append(clusters, cluster) + } + } + + return clusters, nil +} diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index 5f30d60dec..948743fa9c 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -54,32 +54,12 @@ func (cs *ClusterStore) Init() error { return nil } -func (cs *ClusterStore) ListClusters(ctx context.Context, selectors ...*metav1.LabelSelector) ([]Cluster, error) { - gkeClusters, err := cs.listClusters(ctx, selectors[0]) +func (cs *ClusterStore) ListClusters(ctx context.Context, selector *metav1.LabelSelector) ([]Cluster, error) { + gkeClusters, err := cs.listClusters(ctx, selector) if err != nil { return nil, err } - for _, selector := range selectors[1:] { - selectorClusters, err := cs.listClusters(ctx, selector) - if err != nil { - return nil, err - } - - intersection := []gkeclusterapis.ContainerCluster{} - - for _, cluster := range gkeClusters.Items { - for _, selectorCluster := range selectorClusters.Items { - if cluster.Name == selectorCluster.Name { - intersection = append(intersection, cluster) - break - } - } - } - - gkeClusters.Items = intersection - } - sort.Slice(gkeClusters.Items, func(i, j int) bool { return strings.Compare(gkeClusters.Items[i].Name, gkeClusters.Items[j].Name) == -1 }) From 1a28e2f52e21892ed41d24e0f3335521959395f7 Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Tue, 7 Feb 2023 17:07:24 -0500 Subject: [PATCH 36/38] rollouts: implement cluster discovery for gcp fleet (#3792) --- rollouts/api/v1alpha1/rollout_types.go | 8 +- .../api/v1alpha1/zz_generated.deepcopy.go | 27 ++- .../crd/bases/gitops.kpt.dev_rollouts.yaml | 11 + .../controllers/remoterootsync_controller.go | 11 +- rollouts/controllers/rollout_controller.go | 19 +- rollouts/pkg/clusterstore/clusterstore.go | 174 +++------------- .../pkg/clusterstore/containerclusterstore.go | 185 +++++++++++++++++ .../pkg/clusterstore/gcpfleetclusterstore.go | 190 ++++++++++++++++++ 8 files changed, 464 insertions(+), 161 deletions(-) create mode 100644 rollouts/pkg/clusterstore/containerclusterstore.go create mode 100644 rollouts/pkg/clusterstore/gcpfleetclusterstore.go diff --git a/rollouts/api/v1alpha1/rollout_types.go b/rollouts/api/v1alpha1/rollout_types.go index 09d6626479..736328f2f3 100644 --- a/rollouts/api/v1alpha1/rollout_types.go +++ b/rollouts/api/v1alpha1/rollout_types.go @@ -67,7 +67,13 @@ type ClusterSourceType string // ClusterDiscovery represents configuration needed to discover clusters. type ClusterDiscovery struct { - SourceType ClusterSourceType `json:"sourceType"` + SourceType ClusterSourceType `json:"sourceType"` + GCPFleet *ClusterSourceGCPFleet `json:"gcpFleet,omitempty"` +} + +// ClusterSourceGCPFleet represents configuration needed to discover gcp fleet clusters. +type ClusterSourceGCPFleet struct { + ProjectIds []string `json:"projectIds"` } const ( diff --git a/rollouts/api/v1alpha1/zz_generated.deepcopy.go b/rollouts/api/v1alpha1/zz_generated.deepcopy.go index 6ce983231f..bac94d0c9b 100644 --- a/rollouts/api/v1alpha1/zz_generated.deepcopy.go +++ b/rollouts/api/v1alpha1/zz_generated.deepcopy.go @@ -29,6 +29,11 @@ import ( // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterDiscovery) DeepCopyInto(out *ClusterDiscovery) { *out = *in + if in.GCPFleet != nil { + in, out := &in.GCPFleet, &out.GCPFleet + *out = new(ClusterSourceGCPFleet) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDiscovery. @@ -56,6 +61,26 @@ func (in *ClusterRef) DeepCopy() *ClusterRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterSourceGCPFleet) DeepCopyInto(out *ClusterSourceGCPFleet) { + *out = *in + if in.ProjectIds != nil { + in, out := &in.ProjectIds, &out.ProjectIds + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSourceGCPFleet. +func (in *ClusterSourceGCPFleet) DeepCopy() *ClusterSourceGCPFleet { + if in == nil { + return nil + } + out := new(ClusterSourceGCPFleet) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { *out = *in @@ -482,7 +507,7 @@ func (in *RolloutList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { *out = *in - out.Clusters = in.Clusters + in.Clusters.DeepCopyInto(&out.Clusters) out.Packages = in.Packages in.Targets.DeepCopyInto(&out.Targets) out.PackageToTargetMatcher = in.PackageToTargetMatcher diff --git a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml index ad898309be..7e77a08e42 100644 --- a/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml +++ b/rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml @@ -52,6 +52,17 @@ spec: clusters: description: Clusters specifies the source for discovering the clusters. properties: + gcpFleet: + description: ClusterSourceGCPFleet represents configuration needed + to discover gcp fleet clusters. + properties: + projectIds: + items: + type: string + type: array + required: + - projectIds + type: object sourceType: enum: - KCC diff --git a/rollouts/controllers/remoterootsync_controller.go b/rollouts/controllers/remoterootsync_controller.go index 3d2a39651c..5a6be06797 100644 --- a/rollouts/controllers/remoterootsync_controller.go +++ b/rollouts/controllers/remoterootsync_controller.go @@ -307,14 +307,13 @@ func (r *RemoteRootSyncReconciler) SetupWithManager(mgr ctrl.Manager) error { if err := gitopsv1alpha1.AddToScheme(mgr.GetScheme()); err != nil { return err } - // setup the clusterstore - r.store = &clusterstore.ClusterStore{ - Config: mgr.GetConfig(), - Client: r.Client, - } - if err := r.store.Init(); err != nil { + + clusterStore, err := clusterstore.NewClusterStore(r.Client, mgr.GetConfig()) + if err != nil { return err } + r.store = clusterStore + return ctrl.NewControllerManagedBy(mgr). For(&gitopsv1alpha1.RemoteRootSync{}). Watches( diff --git a/rollouts/controllers/rollout_controller.go b/rollouts/controllers/rollout_controller.go index 5ca5b779a2..56bb189169 100644 --- a/rollouts/controllers/rollout_controller.go +++ b/rollouts/controllers/rollout_controller.go @@ -201,7 +201,7 @@ func (r *RolloutReconciler) getStrategy(ctx context.Context, rollout *gitopsv1al } func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Context, rollout *gitopsv1alpha1.Rollout, strategy *gitopsv1alpha1.ProgressiveRolloutStrategy) error { - allClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) + allClusters, err := r.store.ListClusters(ctx, &rollout.Spec.Clusters, rollout.Spec.Targets.Selector) if err != nil { return err } @@ -275,7 +275,7 @@ func (r *RolloutReconciler) getPackageDiscoveryClient(rolloutNamespacedName type func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitopsv1alpha1.Rollout, strategy *gitopsv1alpha1.ProgressiveRolloutStrategy, packageDiscoveryClient *packagediscovery.PackageDiscovery) error { logger := log.FromContext(ctx) - targetClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector) + targetClusters, err := r.store.ListClusters(ctx, &rollout.Spec.Clusters, rollout.Spec.Targets.Selector) discoveredPackages, err := packageDiscoveryClient.GetPackages(ctx, rollout.Spec.Packages) if err != nil { logger.Error(err, "failed to discover packages") @@ -388,11 +388,12 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context, continue } cluster := &clusterPackages[idx].Cluster + clusterName := cluster.Name[strings.LastIndex(cluster.Name, "/")+1:] pkg := &clusterPkg.Packages[0] rrs := gitopsv1alpha1.RemoteRootSync{} key := client.ObjectKey{ Namespace: rollout.Namespace, - Name: fmt.Sprintf("%s-%s", pkgID(pkg), cluster.Name), + Name: fmt.Sprintf("%s-%s", pkgID(pkg), clusterName), } // since this RRS need to exist, remove it from the deletion list delete(RRSkeysToBeDeleted, key) @@ -694,9 +695,11 @@ func isRRSErrored(rss *gitopsv1alpha1.RemoteRootSync) bool { // Given a package identifier and cluster, create a RemoteRootSync object. func newRemoteRootSync(rollout *gitopsv1alpha1.Rollout, clusterRef gitopsv1alpha1.ClusterRef, rssSpec *gitopsv1alpha1.RootSyncSpec, pkgID string, waveName string) *gitopsv1alpha1.RemoteRootSync { t := true + clusterName := clusterRef.Name[strings.LastIndex(clusterRef.Name, "/")+1:] + return &gitopsv1alpha1.RemoteRootSync{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", pkgID, clusterRef.Name), + Name: fmt.Sprintf("%s-%s", pkgID, clusterName), Namespace: rollout.Namespace, Labels: map[string]string{ rolloutLabel: rollout.Name, @@ -754,13 +757,11 @@ func (r *RolloutReconciler) SetupWithManager(mgr ctrl.Manager) error { r.packageDiscoveryCache = make(map[types.NamespacedName]*packagediscovery.PackageDiscovery) // setup the clusterstore - r.store = &clusterstore.ClusterStore{ - Config: mgr.GetConfig(), - Client: r.Client, - } - if err := r.store.Init(); err != nil { + clusterStore, err := clusterstore.NewClusterStore(r.Client, mgr.GetConfig()) + if err != nil { return err } + r.store = clusterStore var containerCluster gkeclusterapis.ContainerCluster return ctrl.NewControllerManagedBy(mgr). diff --git a/rollouts/pkg/clusterstore/clusterstore.go b/rollouts/pkg/clusterstore/clusterstore.go index 948743fa9c..b37f88a1bc 100644 --- a/rollouts/pkg/clusterstore/clusterstore.go +++ b/rollouts/pkg/clusterstore/clusterstore.go @@ -1,4 +1,4 @@ -// Copyright 2022 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,177 +16,63 @@ package clusterstore import ( "context" - "encoding/base64" "fmt" - "sort" "strings" - "golang.org/x/oauth2" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" - - gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" ) -// ClusterStore represents a store of kubernetes cluster. type ClusterStore struct { - // Config/Client points to the config - // pointing to where the rollout controller is running - Config *rest.Config - client.Client - WorkloadIdentityHelper + containerClusterStore *ContainerClusterStore + gcpFleetClusterStore *GCPFleetClusterStore } - type Cluster struct { Name string Labels map[string]string } -func (cs *ClusterStore) Init() error { - if err := cs.WorkloadIdentityHelper.Init(cs.Config); err != nil { - return err - } - return nil -} - -func (cs *ClusterStore) ListClusters(ctx context.Context, selector *metav1.LabelSelector) ([]Cluster, error) { - gkeClusters, err := cs.listClusters(ctx, selector) - if err != nil { - return nil, err - } - - sort.Slice(gkeClusters.Items, func(i, j int) bool { - return strings.Compare(gkeClusters.Items[i].Name, gkeClusters.Items[j].Name) == -1 - }) - - clusters := []Cluster{} - - for _, containerCluster := range gkeClusters.Items { - cluster := toCluster(&containerCluster) - clusters = append(clusters, cluster) - } - - return clusters, nil -} - -func (cs *ClusterStore) getCluster(ctx context.Context, name string) (*gkeclusterapis.ContainerCluster, error) { - gkeCluster := gkeclusterapis.ContainerCluster{} - clusterKey := client.ObjectKey{ - Namespace: "config-control", - Name: name, - } - if err := cs.Get(ctx, clusterKey, &gkeCluster); err != nil { - return nil, err +func NewClusterStore(client client.Client, config *rest.Config) (*ClusterStore, error) { + containerClusterStore := &ContainerClusterStore{ + Config: config, + Client: client, } - - return &gkeCluster, nil -} - -func (cs *ClusterStore) GetRESTConfig(ctx context.Context, name string) (*rest.Config, error) { - cluster, err := cs.getCluster(ctx, name) - if err != nil { + if err := containerClusterStore.Init(); err != nil { return nil, err } - restConfig, err := cs.getRESTConfig(ctx, cluster) - if err != nil { - return nil, err + clusterStore := &ClusterStore{ + containerClusterStore: containerClusterStore, + gcpFleetClusterStore: &GCPFleetClusterStore{}, } - return restConfig, nil + return clusterStore, nil } -func (cs *ClusterStore) listClusters(ctx context.Context, selector *metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { - gkeClusters := &gkeclusterapis.ContainerClusterList{} +func (cs *ClusterStore) ListClusters(ctx context.Context, clusterDiscovery *gitopsv1alpha1.ClusterDiscovery, selector *metav1.LabelSelector) ([]Cluster, error) { + clusterSourceType := clusterDiscovery.SourceType - var opts []client.ListOption + switch clusterSourceType { + case gitopsv1alpha1.GCPFleet: + return cs.gcpFleetClusterStore.ListClusters(ctx, clusterDiscovery.GCPFleet, selector) - if selector != nil { - selector, err := metav1.LabelSelectorAsSelector(selector) - if err != nil { - return nil, err - } - opts = append(opts, client.MatchingLabelsSelector{Selector: selector}) - } - - // TODO: make it configurable ? - opts = append(opts, client.InNamespace("config-control")) - if err := cs.List(ctx, gkeClusters, opts...); err != nil { - return nil, err - } - - return gkeClusters, nil -} + case gitopsv1alpha1.KCC: + return cs.containerClusterStore.ListClusters(ctx, selector) -func (cs *ClusterStore) getRESTConfig(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (*rest.Config, error) { - logger := log.FromContext(ctx) - restConfig := &rest.Config{} - clusterCaCertificate := cluster.Spec.MasterAuth.ClusterCaCertificate - if clusterCaCertificate == nil || *clusterCaCertificate == "" { - return nil, fmt.Errorf("cluster CA certificate data is missing") + default: + return nil, fmt.Errorf("%v cluster source not supported", clusterSourceType) } - caData, err := base64.StdEncoding.DecodeString(*clusterCaCertificate) - if err != nil { - return nil, fmt.Errorf("error decoding ca certificate: %w", err) - } - restConfig.CAData = caData - if cluster.Status.Endpoint == "" { - return nil, fmt.Errorf("cluster master endpoint field is empty") - } - restConfig.Host = "https://" + cluster.Status.Endpoint - logger.Info("Host endpoint is", "endpoint", restConfig.Host) - tokenSource, err := cs.getConfigConnectorContextTokenSource(ctx, cluster.GetNamespace()) - if err != nil { - return nil, err - } - token, err := tokenSource.Token() - if err != nil { - return nil, fmt.Errorf("error getting token: %w", err) - } - restConfig.BearerToken = token.AccessToken - return restConfig, nil } -// getConfigConnectorContextTokenSource gets and returns the ConfigConnectorContext for the given namespace. -func (cs *ClusterStore) getConfigConnectorContextTokenSource(ctx context.Context, ns string) (oauth2.TokenSource, error) { - // TODO: migrate to it's own Go type and use client.Client instance for it - gvr := schema.GroupVersionResource{ - Group: "core.cnrm.cloud.google.com", - Version: "v1beta1", - Resource: "configconnectorcontexts", - } - - cr, err := cs.dynamicClient.Resource(gvr).Namespace(ns).Get(ctx, "configconnectorcontext.core.cnrm.cloud.google.com", metav1.GetOptions{}) - if err != nil { - return nil, err - } - - googleServiceAccount, _, err := unstructured.NestedString(cr.Object, "spec", "googleServiceAccount") - if err != nil { - return nil, fmt.Errorf("error reading spec.googleServiceAccount from ConfigConnectorContext in %q: %w", ns, err) - } - - if googleServiceAccount == "" { - return nil, fmt.Errorf("could not find spec.googleServiceAccount from ConfigConnectorContext in %q: %w", ns, err) - } - - kubeServiceAccount := types.NamespacedName{ - Namespace: "cnrm-system", - Name: "cnrm-controller-manager-" + ns, - } - return cs.WorkloadIdentityHelper.GetGcloudAccessTokenSource(ctx, kubeServiceAccount, googleServiceAccount) -} +func (cs *ClusterStore) GetRESTConfig(ctx context.Context, name string) (*rest.Config, error) { + switch { + case strings.Contains(name, "memberships") || strings.Contains(name, "gkeMemberships"): + return cs.gcpFleetClusterStore.GetRESTConfig(ctx, name) -func toCluster(containerCluster *gkeclusterapis.ContainerCluster) Cluster { - cluster := Cluster{ - Name: containerCluster.Name, - Labels: containerCluster.Labels, + default: + return cs.containerClusterStore.GetRESTConfig(ctx, name) } - - return cluster } diff --git a/rollouts/pkg/clusterstore/containerclusterstore.go b/rollouts/pkg/clusterstore/containerclusterstore.go new file mode 100644 index 0000000000..3297ebd8cf --- /dev/null +++ b/rollouts/pkg/clusterstore/containerclusterstore.go @@ -0,0 +1,185 @@ +// 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 clusterstore + +import ( + "context" + "encoding/base64" + "fmt" + "sort" + "strings" + + "golang.org/x/oauth2" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + gkeclusterapis "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/container/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" +) + +// ContainerClusterStore represents a store of kubernetes cluster. +type ContainerClusterStore struct { + // Config/Client points to the config + // pointing to where the rollout controller is running + Config *rest.Config + client.Client + WorkloadIdentityHelper +} + +func (cs *ContainerClusterStore) Init() error { + if err := cs.WorkloadIdentityHelper.Init(cs.Config); err != nil { + return err + } + return nil +} + +func (cs *ContainerClusterStore) ListClusters(ctx context.Context, selector *metav1.LabelSelector) ([]Cluster, error) { + gkeClusters, err := cs.listClusters(ctx, selector) + if err != nil { + return nil, err + } + + sort.Slice(gkeClusters.Items, func(i, j int) bool { + return strings.Compare(gkeClusters.Items[i].Name, gkeClusters.Items[j].Name) == -1 + }) + + clusters := []Cluster{} + + for _, containerCluster := range gkeClusters.Items { + cluster := cs.toCluster(&containerCluster) + clusters = append(clusters, cluster) + } + + return clusters, nil +} + +func (cs *ContainerClusterStore) getContainerCluster(ctx context.Context, name string) (*gkeclusterapis.ContainerCluster, error) { + gkeCluster := gkeclusterapis.ContainerCluster{} + clusterKey := client.ObjectKey{ + Namespace: "config-control", + Name: name, + } + if err := cs.Get(ctx, clusterKey, &gkeCluster); err != nil { + return nil, err + } + + return &gkeCluster, nil +} + +func (cs *ContainerClusterStore) GetRESTConfig(ctx context.Context, name string) (*rest.Config, error) { + cluster, err := cs.getContainerCluster(ctx, name) + if err != nil { + return nil, err + } + restConfig, err := cs.getRESTConfig(ctx, cluster) + if err != nil { + return nil, err + } + return restConfig, err +} + +func (cs *ContainerClusterStore) listClusters(ctx context.Context, selector *metav1.LabelSelector) (*gkeclusterapis.ContainerClusterList, error) { + gkeClusters := &gkeclusterapis.ContainerClusterList{} + + var opts []client.ListOption + + if selector != nil { + selector, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return nil, err + } + opts = append(opts, client.MatchingLabelsSelector{Selector: selector}) + } + + // TODO: make it configurable ? + opts = append(opts, client.InNamespace("config-control")) + if err := cs.List(ctx, gkeClusters, opts...); err != nil { + return nil, err + } + + return gkeClusters, nil +} + +func (cs *ContainerClusterStore) getRESTConfig(ctx context.Context, cluster *gkeclusterapis.ContainerCluster) (*rest.Config, error) { + logger := log.FromContext(ctx) + restConfig := &rest.Config{} + clusterCaCertificate := cluster.Spec.MasterAuth.ClusterCaCertificate + if clusterCaCertificate == nil || *clusterCaCertificate == "" { + return nil, fmt.Errorf("cluster CA certificate data is missing") + } + caData, err := base64.StdEncoding.DecodeString(*clusterCaCertificate) + if err != nil { + return nil, fmt.Errorf("error decoding ca certificate: %w", err) + } + restConfig.CAData = caData + if cluster.Status.Endpoint == "" { + return nil, fmt.Errorf("cluster master endpoint field is empty") + } + restConfig.Host = "https://" + cluster.Status.Endpoint + logger.Info("Host endpoint is", "endpoint", restConfig.Host) + tokenSource, err := cs.getConfigConnectorContextTokenSource(ctx, cluster.GetNamespace()) + if err != nil { + return nil, err + } + token, err := tokenSource.Token() + if err != nil { + return nil, fmt.Errorf("error getting token: %w", err) + } + restConfig.BearerToken = token.AccessToken + return restConfig, nil +} + +// getConfigConnectorContextTokenSource gets and returns the ConfigConnectorContext for the given namespace. +func (cs *ContainerClusterStore) getConfigConnectorContextTokenSource(ctx context.Context, ns string) (oauth2.TokenSource, error) { + // TODO: migrate to it's own Go type and use client.Client instance for it + gvr := schema.GroupVersionResource{ + Group: "core.cnrm.cloud.google.com", + Version: "v1beta1", + Resource: "configconnectorcontexts", + } + + cr, err := cs.dynamicClient.Resource(gvr).Namespace(ns).Get(ctx, "configconnectorcontext.core.cnrm.cloud.google.com", metav1.GetOptions{}) + if err != nil { + return nil, err + } + + googleServiceAccount, _, err := unstructured.NestedString(cr.Object, "spec", "googleServiceAccount") + if err != nil { + return nil, fmt.Errorf("error reading spec.googleServiceAccount from ConfigConnectorContext in %q: %w", ns, err) + } + + if googleServiceAccount == "" { + return nil, fmt.Errorf("could not find spec.googleServiceAccount from ConfigConnectorContext in %q: %w", ns, err) + } + + kubeServiceAccount := types.NamespacedName{ + Namespace: "cnrm-system", + Name: "cnrm-controller-manager-" + ns, + } + return cs.WorkloadIdentityHelper.GetGcloudAccessTokenSource(ctx, kubeServiceAccount, googleServiceAccount) +} + +func (cs *ContainerClusterStore) toCluster(containerCluster *gkeclusterapis.ContainerCluster) Cluster { + cluster := Cluster{ + Name: containerCluster.Name, + Labels: containerCluster.Labels, + } + + return cluster +} diff --git a/rollouts/pkg/clusterstore/gcpfleetclusterstore.go b/rollouts/pkg/clusterstore/gcpfleetclusterstore.go new file mode 100644 index 0000000000..d97fdf03db --- /dev/null +++ b/rollouts/pkg/clusterstore/gcpfleetclusterstore.go @@ -0,0 +1,190 @@ +// Copyright 2023 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 clusterstore + +import ( + "context" + "fmt" + "strings" + "sync" + + "golang.org/x/oauth2/google" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/rest" + + gitopsv1alpha1 "github.com/GoogleContainerTools/kpt/rollouts/api/v1alpha1" + cloudresourcemanagerv1 "google.golang.org/api/cloudresourcemanager/v1" + gkehubv1 "google.golang.org/api/gkehub/v1" +) + +type GCPFleetClusterStore struct { + projectId string + projectIdToNumberCache sync.Map +} + +func (cs *GCPFleetClusterStore) ListClusters(ctx context.Context, configuration *gitopsv1alpha1.ClusterSourceGCPFleet, labelSelector *metav1.LabelSelector) ([]Cluster, error) { + err := cs.validateGCPFleetConfiguration(configuration) + if err != nil { + return nil, fmt.Errorf("gcp fleet configuration error: %w", err) + } + + clusters := []Cluster{} + projectId := configuration.ProjectIds[0] + + // TODO: add support for listing meemberships for mulitple projects + memberships, err := cs.listMemberships(ctx, projectId) + if err != nil { + return nil, fmt.Errorf("memberships failed: %w", err) + } + + for _, membership := range memberships.Resources { + cluster := cs.toCluster(membership) + + clusterLabelSet := labels.Set(cluster.Labels) + shouldAdd := true + + if labelSelector != nil { + selector, _ := metav1.LabelSelectorAsSelector(labelSelector) + + if !selector.Matches(clusterLabelSet) { + shouldAdd = false + continue + } + } + + if shouldAdd { + clusters = append(clusters, cluster) + } + } + + return clusters, nil +} + +func (cs *GCPFleetClusterStore) GetRESTConfig(ctx context.Context, name string) (*rest.Config, error) { + membership, err := cs.getMembership(ctx, name) + if err != nil { + return nil, fmt.Errorf("unable to get membership: %w", err) + } + + // name format: projects/:projectId/locations/global/memberships/:membershipName + membershipName := strings.Split(membership.Name, "/")[5] + projectId := strings.Split(membership.Name, "/")[1] + + isGKE := membership.Endpoint.GkeCluster != nil + + projectNumber, err := cs.getProjectNumber(ctx, projectId) + if err != nil { + return nil, fmt.Errorf("unable to get project number: %w", err) + } + + membershipUrl := "memberships" + + if isGKE { + membershipUrl = "gkeMemberships" + } + + accessToken, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform") + if err != nil { + return nil, fmt.Errorf("unable to get access token: %w", err) + } + + token, err := accessToken.Token() + if err != nil { + return nil, fmt.Errorf("get access token failed: %w", err) + } + + host := fmt.Sprintf("https://connectgateway.googleapis.com/v1/projects/%d/locations/global/%s/%s", projectNumber, membershipUrl, membershipName) + + restConfig := &rest.Config{} + restConfig.Host = host + restConfig.BearerToken = token.AccessToken + + return restConfig, err +} + +func (cs *GCPFleetClusterStore) getProjectNumber(ctx context.Context, projectId string) (int64, error) { + projectNumberCache, found := cs.projectIdToNumberCache.Load(projectId) + + if found { + projectNumber := projectNumberCache.(int64) + return projectNumber, nil + } + + crmClient, err := cloudresourcemanagerv1.NewService(ctx) + if err != nil { + return -1, fmt.Errorf("failed to create new cloudresourcemanager client: %w", err) + } + + project, err := crmClient.Projects.Get(projectId).Context(ctx).Do() + if err != nil { + return -1, fmt.Errorf("error querying project %q: %w", projectId, err) + } + + projectNumber := project.ProjectNumber + + cs.projectIdToNumberCache.Store(projectId, projectNumber) + + return projectNumber, nil +} + +func (cs *GCPFleetClusterStore) validateGCPFleetConfiguration(configuration *gitopsv1alpha1.ClusterSourceGCPFleet) error { + if configuration == nil { + return fmt.Errorf("configuration is missing") + } + if len(configuration.ProjectIds) == 0 { + return fmt.Errorf("at least one project id must be listed") + } + + return nil +} + +func (cs *GCPFleetClusterStore) getMembership(ctx context.Context, name string) (*gkehubv1.Membership, error) { + hubClient, err := gkehubv1.NewService(ctx) + if err != nil { + return nil, err + } + + resp, err := hubClient.Projects.Locations.Memberships.Get(name).Do() + if err != nil { + return nil, err + } + + return resp, nil +} + +func (cs *GCPFleetClusterStore) listMemberships(ctx context.Context, projectId string) (*gkehubv1.ListMembershipsResponse, error) { + hubClient, err := gkehubv1.NewService(ctx) + if err != nil { + return nil, err + } + + parent := fmt.Sprintf("projects/%s/locations/global", projectId) + resp, err := hubClient.Projects.Locations.Memberships.List(parent).Do() + if err != nil { + return nil, err + } + + return resp, nil +} + +func (cs *GCPFleetClusterStore) toCluster(membership *gkehubv1.Membership) Cluster { + cluster := Cluster{ + Name: membership.Name, + Labels: membership.Labels, + } + + return cluster +} From cba3d75a8c7bf9c9c082bbcb03fc683a091b5e9e Mon Sep 17 00:00:00 2001 From: Christopher Fry Date: Wed, 8 Feb 2023 16:21:56 -0500 Subject: [PATCH 37/38] rollouts: cache gcp fleet rest config host (#3798) --- .../pkg/clusterstore/gcpfleetclusterstore.go | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/rollouts/pkg/clusterstore/gcpfleetclusterstore.go b/rollouts/pkg/clusterstore/gcpfleetclusterstore.go index d97fdf03db..a7dfd1f3f4 100644 --- a/rollouts/pkg/clusterstore/gcpfleetclusterstore.go +++ b/rollouts/pkg/clusterstore/gcpfleetclusterstore.go @@ -31,8 +31,9 @@ import ( ) type GCPFleetClusterStore struct { - projectId string - projectIdToNumberCache sync.Map + projectId string + membershipNameToConfigHostCache sync.Map + projectIdToNumberCache sync.Map } func (cs *GCPFleetClusterStore) ListClusters(ctx context.Context, configuration *gitopsv1alpha1.ClusterSourceGCPFleet, labelSelector *metav1.LabelSelector) ([]Cluster, error) { @@ -74,9 +75,36 @@ func (cs *GCPFleetClusterStore) ListClusters(ctx context.Context, configuration } func (cs *GCPFleetClusterStore) GetRESTConfig(ctx context.Context, name string) (*rest.Config, error) { + accessToken, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform") + if err != nil { + return nil, fmt.Errorf("unable to get access token: %w", err) + } + + token, err := accessToken.Token() + + host, err := cs.getRESTConfigHost(ctx, name) + if err != nil { + return nil, fmt.Errorf("building rest config host failed: %w", err) + } + + restConfig := &rest.Config{} + restConfig.Host = host + restConfig.BearerToken = token.AccessToken + + return restConfig, err +} + +func (cs *GCPFleetClusterStore) getRESTConfigHost(ctx context.Context, name string) (string, error) { + restConfigHost, found := cs.membershipNameToConfigHostCache.Load(name) + + if found { + restConfigHost := restConfigHost.(string) + return restConfigHost, nil + } + membership, err := cs.getMembership(ctx, name) if err != nil { - return nil, fmt.Errorf("unable to get membership: %w", err) + return "", fmt.Errorf("unable to get membership: %w", err) } // name format: projects/:projectId/locations/global/memberships/:membershipName @@ -87,7 +115,7 @@ func (cs *GCPFleetClusterStore) GetRESTConfig(ctx context.Context, name string) projectNumber, err := cs.getProjectNumber(ctx, projectId) if err != nil { - return nil, fmt.Errorf("unable to get project number: %w", err) + return "", fmt.Errorf("unable to get project number: %w", err) } membershipUrl := "memberships" @@ -96,23 +124,11 @@ func (cs *GCPFleetClusterStore) GetRESTConfig(ctx context.Context, name string) membershipUrl = "gkeMemberships" } - accessToken, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform") - if err != nil { - return nil, fmt.Errorf("unable to get access token: %w", err) - } - - token, err := accessToken.Token() - if err != nil { - return nil, fmt.Errorf("get access token failed: %w", err) - } - host := fmt.Sprintf("https://connectgateway.googleapis.com/v1/projects/%d/locations/global/%s/%s", projectNumber, membershipUrl, membershipName) - restConfig := &rest.Config{} - restConfig.Host = host - restConfig.BearerToken = token.AccessToken + cs.membershipNameToConfigHostCache.Store(name, host) - return restConfig, err + return host, nil } func (cs *GCPFleetClusterStore) getProjectNumber(ctx context.Context, projectId string) (int64, error) { From 469165e61ffc1afb6b7a1261bcd8630f8e85decf Mon Sep 17 00:00:00 2001 From: droot Date: Wed, 8 Feb 2023 23:28:42 +0000 Subject: [PATCH 38/38] resolved go.mod error --- porch/go.mod | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/porch/go.mod b/porch/go.mod index 0513fb79de..b44fdc414e 100644 --- a/porch/go.mod +++ b/porch/go.mod @@ -52,17 +52,9 @@ require ( k8s.io/component-base v0.25.3 k8s.io/klog/v2 v2.80.1 k8s.io/kube-aggregator v0.24.0-beta.0 -<<<<<<< HEAD - k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 -<<<<<<< HEAD - sigs.k8s.io/cli-utils v0.34.0 -======= ->>>>>>> c96a7193 (Rollouts rebase kpt main (#3750)) - sigs.k8s.io/controller-runtime v0.13.0 -======= k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2 + sigs.k8s.io/cli-utils v0.34.0 sigs.k8s.io/controller-runtime v0.13.1 ->>>>>>> 987b25e4 (kpt cli: added rollouts subcommand (#3752)) sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/yaml v1.3.0 ) @@ -197,10 +189,6 @@ require ( k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/kubectl v0.25.3 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 // indirect -<<<<<<< HEAD -======= - sigs.k8s.io/cli-utils v0.34.0 // indirect ->>>>>>> c96a7193 (Rollouts rebase kpt main (#3750)) sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect