From 15ac83282fa01e4c41d103b45ed708bed1152a3f Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Fri, 29 Apr 2022 10:56:12 +0200 Subject: [PATCH 01/19] Update makefiles --- .env.template | 7 ++ .gitignore | 3 + Jenkinsfile | 2 +- Makefile | 140 ++---------------------------- build/make/k8s-controller.mk | 100 +++++++++++++++++++++ build/make/k8s-dogu.mk | 44 ++++++++++ build/make/k8s-dogu.tpl | 9 ++ build/make/k8s.mk | 103 ++++++++++++++++++++++ build/make/static-analysis.mk | 10 ++- config/manager/kustomization.yaml | 14 +-- 10 files changed, 288 insertions(+), 144 deletions(-) create mode 100644 .env.template create mode 100644 build/make/k8s-controller.mk create mode 100644 build/make/k8s-dogu.mk create mode 100644 build/make/k8s-dogu.tpl create mode 100644 build/make/k8s.mk diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..259b5ef --- /dev/null +++ b/.env.template @@ -0,0 +1,7 @@ +# Usage: +# 1. Copy this file as `.env` into your project +# 2. Adapt the information below with the your personal data. +# +# The file `.env` is ignored by git. Note: DO NOT COMMIT your personal data. +export K8S_CLUSTER_ROOT=/home/jsprey/Documents/GIT/k3ces +export WATCH_NAMESPACE=$(shell kubectl config view --minify -o jsonpath='{..namespace}') \ No newline at end of file diff --git a/.gitignore b/.gitignore index 193d53b..40e2703 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Makefiles +.env + # Binaries for programs and plugins *.so *.dylib diff --git a/Jenkinsfile b/Jenkinsfile index 2bbf56f..e8bd468 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -53,7 +53,7 @@ node('docker') { } stage('Generate k8s Resources') { - make 'k8s-generate' + make 'k8s-create-temporary-resource' archiveArtifacts 'target/*.yaml' } } diff --git a/Makefile b/Makefile index 9df83f1..6b169f4 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,13 @@ # Set these to the desired values ARTIFACT_ID=k8s-service-discovery VERSION=0.1.0 - -GOTAG?=1.17.7 -MAKEFILES_VERSION=5.0.0 - # Image URL to use all building/pushing image targets IMAGE=cloudogu/${ARTIFACT_ID}:${VERSION} -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.23 -K8S_INTEGRATION_TEST_DIR=${TARGET_DIR}/k8s-integration-test -K8S_RESOURCE_YAML=${TARGET_DIR}/${ARTIFACT_ID}_${VERSION}.yaml -DEFAULT_NAMESPACE?="$(shell kubectl config view | grep namespace | head -n 1 | sed "s/^.*namespace: //g")" - -# make sure to create a statically linked binary otherwise it may quit with -# "exec user process caused: no such file or directory" -GO_BUILD_FLAGS=-mod=vendor -a -tags netgo,osusergo $(LDFLAGS) -o $(BINARY) -# remove DWARF symbol table and strip other symbols to shave ~13 MB from binary -ADDITIONAL_LDFLAGS=-extldflags -static -w -s - -.DEFAULT_GOAL:=help +GOTAG?=1.17.7 +MAKEFILES_VERSION=5.0.0 include build/make/variables.mk - -ADDITIONAL_CLEAN=dist-clean -PRE_COMPILE=generate vet - include build/make/self-update.mk include build/make/dependencies-gomod.mk include build/make/build.mk @@ -36,28 +17,10 @@ include build/make/test-unit.mk include build/make/static-analysis.mk include build/make/clean.mk include build/make/digital-signature.mk +include build/make/k8s-controller.mk - -# Setting SHELL to bash allows bash commands to be executed by recipes. -# This is a requirement for 'setup-envtest.sh' in the test target. -# 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 - -##@ EcoSystem - -.PHONY: build -build: docker-build image-import k8s-apply ## Builds a new version of the setup and deploys it into the K8s-EcoSystem. - -##@ Development (without go container) - -${STATIC_ANALYSIS_DIR}/report-govet.out: ${SRC} $(STATIC_ANALYSIS_DIR) - @go vet ./... | tee $@ - -.PHONY: vet -vet: ${STATIC_ANALYSIS_DIR}/report-govet.out ## Run go vet against code. - -##@ Kubernetes Controller +ADDITIONAL_CLEAN=dist-clean +PRE_COMPILE=generate vet .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. @@ -68,96 +31,3 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. @echo "Auto-generate deepcopy functions..." @$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." - -$(K8S_INTEGRATION_TEST_DIR): - @mkdir -p $@ - -.PHONY: k8s-integration-test -k8s-integration-test: $(K8S_INTEGRATION_TEST_DIR) manifests generate vet envtest ## Run k8s integration tests. - @echo "Running k8s integration tests..." - @KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -tags=k8s_integration ./... -coverprofile ${K8S_INTEGRATION_TEST_DIR}/report-k8s-integration.out - -##@ Build - -.PHONY: build-controller -build-controller: ${SRC} compile ## Builds the controller Go binary. - -.PHONY: run -run: manifests generate vet ## Run a controller from your host. - go run ./main.go - -##@ Release - -.PHONY: controller-release -controller-release: ## Interactively starts the release workflow. - @echo "Starting git flow release..." - @build/make/release.sh controller-tool - -##@ Docker - -.PHONY: docker-build -docker-build: ${SRC} ## Builds the docker image of the k8s-ces-setup `cloudogu/k8s-ces-setup:version`. - @echo "Building docker image of dogu..." - DOCKER_BUILDKIT=1 docker build . -t ${IMAGE} - -${K8S_CLUSTER_ROOT}/image.tar: check-k8s-cluster-root-env-var - # Saves the `cloudogu/k8s-ces-setup:version` image into a file into the K8s root path to be available on all nodes. - docker save ${IMAGE} -o ${K8S_CLUSTER_ROOT}/image.tar - -.PHONY: image-import -image-import: ${K8S_CLUSTER_ROOT}/image.tar - # Imports the currently available image `cloudogu/k8s-ces-setup:version` into the K8s cluster for all nodes. - @echo "Import docker image of dogu into all K8s nodes..." - @cd ${K8S_CLUSTER_ROOT} && \ - for node in $$(vagrant status --machine-readable | grep "state,running" | awk -F',' '{print $$2}'); \ - do \ - echo "...$${node}"; \ - vagrant ssh $${node} -- -t "sudo k3s ctr images import /vagrant/image.tar"; \ - done; - @echo "Done." - rm ${K8S_CLUSTER_ROOT}/image.tar - -.PHONY: check-k8s-cluster-root-env-var -check-k8s-cluster-root-env-var: - @echo "Checking if env var K8S_CLUSTER_ROOT is set..." - @bash -c export -p | grep K8S_CLUSTER_ROOT - @echo "Done." - -##@ Deployment - -ifndef ignore-not-found - ignore-not-found = false -endif - -${K8S_RESOURCE_YAML}: ${TARGET_DIR} manifests kustomize - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMAGE} - $(KUSTOMIZE) build config/default > ${K8S_RESOURCE_YAML} - -.PHONY: k8s-generate -k8s-generate: ${K8S_RESOURCE_YAML} ## Create required k8s resources in ./dist/... - @echo "Generating new kubernetes resources..." - -.PHONY: k8s-apply -k8s-apply: k8s-generate ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cat ${K8S_RESOURCE_YAML} | sed "s/{{ .Namespace }}/${DEFAULT_NAMESPACE}/" | kubectl apply -f - - -.PHONY: k8s-delete -k8s-delete: k8s-generate ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - cat ${K8S_RESOURCE_YAML} | kubectl delete --ignore-not-found=$(ignore-not-found) -f - - -##@ Download Kubernetes Utility Tools - -CONTROLLER_GEN = $(UTILITY_BIN_PATH)/controller-gen -.PHONY: controller-gen -controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) - -KUSTOMIZE = $(UTILITY_BIN_PATH)/kustomize -.PHONY: kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.2) - -ENVTEST = $(UTILITY_BIN_PATH)/setup-envtest -.PHONY: envtest -envtest: ## Download envtest-setup locally if necessary. - $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) \ No newline at end of file diff --git a/build/make/k8s-controller.mk b/build/make/k8s-controller.mk new file mode 100644 index 0000000..1d9072e --- /dev/null +++ b/build/make/k8s-controller.mk @@ -0,0 +1,100 @@ +# This script can be used to build and deploy kubernetes controllers. It is required to implement the controller +# specific targets `manifests` and `generate`: +# +# Examples: +# +#.PHONY: manifests +#manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +# @echo "Generate manifests..." +# @$(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. +# @echo "Auto-generate deepcopy functions..." +# @$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + + +# This script required the k8s.mk script +include $(WORKDIR)/build/make/k8s.mk + +## Variables + +# Contains the artifact yaml used as +K8S_RESOURCE_TEMP_YAML=${TARGET_DIR}/${ARTIFACT_ID}_${VERSION}.yaml + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# This is a requirement for 'setup-envtest.sh' in the test target. +# 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 + +# make sure to create a statically linked binary otherwise it may quit with +# "exec user process caused: no such file or directory" +GO_BUILD_FLAGS=-mod=vendor -a -tags netgo,osusergo $(LDFLAGS) -o $(BINARY) + +# remove DWARF symbol table and strip other symbols to shave ~13 MB from binary +ADDITIONAL_LDFLAGS=-extldflags -static -w -s + +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.23 +K8S_INTEGRATION_TEST_DIR=${TARGET_DIR}/k8s-integration-test + +##@ K8s - EcoSystem + +.PHONY: build +build: k8s-delete image-import k8s-apply ## Builds a new version of the dogu and deploys it into the K8s-EcoSystem. + +##@ Release + +.PHONY: controller-release +controller-release: ## Interactively starts the release workflow. + @echo "Starting git flow release..." + @build/make/release.sh controller-tool + +##@ K8s - Development + +.PHONY: build-controller +build-controller: ${SRC} compile ## Builds the controller Go binary. + +# Allows to perform tasks before locally running the controller +K8S_RUN_PRE_TARGETS ?= +.PHONY: run +run: manifests generate vet $(K8S_RUN_PRE_TARGETS) ## Run a controller from your host. + go run -ldflags "-X main.Version=$(VERSION)" ./main.go + +##@ K8s - Integration test with envtest + +$(K8S_INTEGRATION_TEST_DIR): + @mkdir -p $@ + +.PHONY: k8s-integration-test +k8s-integration-test: $(K8S_INTEGRATION_TEST_DIR) manifests generate vet envtest ## Run k8s integration tests. + @echo "Running k8s integration tests..." + @KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -tags=k8s_integration ./... -coverprofile ${K8S_INTEGRATION_TEST_DIR}/report-k8s-integration.out + +##@ K8s - Controller Resource + +# The pre generation script creates a k8s resource yaml containing generated manager yaml. +.PHONY: k8s-create-temporary-resource + k8s-create-temporary-resource: ${TARGET_DIR} manifests kustomize + @echo "Generating temporary k8s resources $(K8S_RESOURCE_TEMP_YAML)..." + cd $(WORKDIR)/config/manager && $(KUSTOMIZE) edit set image controller=$(IMAGE) + $(KUSTOMIZE) build config/default > $(K8S_RESOURCE_TEMP_YAML) + @echo "Done." + +##@ K8s - Download Kubernetes Utility Tools + +CONTROLLER_GEN = $(UTILITY_BIN_PATH)/controller-gen +.PHONY: controller-gen +controller-gen: ## Download controller-gen locally if necessary. + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) + +KUSTOMIZE = $(UTILITY_BIN_PATH)/kustomize +.PHONY: kustomize +kustomize: ## Download kustomize locally if necessary. + $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.2) + +ENVTEST = $(UTILITY_BIN_PATH)/setup-envtest +.PHONY: envtest +envtest: ## Download envtest-setup locally if necessary. + $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) \ No newline at end of file diff --git a/build/make/k8s-dogu.mk b/build/make/k8s-dogu.mk new file mode 100644 index 0000000..b7dea9d --- /dev/null +++ b/build/make/k8s-dogu.mk @@ -0,0 +1,44 @@ +# This script required the k8s.mk script +include $(WORKDIR)/build/make/k8s.mk + +## Variables + +# Path to the dogu json of the dogu +DOGU_JSON_FILE=$(WORKDIR)/dogu.json +# Name of the dogu is extracted from the dogu.json +ARTIFACT_ID=$(shell yq -e ".Name" $(DOGU_JSON_FILE) | sed "s|.*/||g") +# Namespace of the dogu is extracted from the dogu.json +ARTIFACT_NAMESPACE=$(shell yq -e ".Name" $(DOGU_JSON_FILE) | sed "s|/.*||g") +# Namespace of the dogu is extracted from the dogu.json +VERSION=$(shell yq -e ".Version" $(DOGU_JSON_FILE)) +# Image of the dogu is extracted from the dogu.json +IMAGE=$(shell yq -e ".Image" $(DOGU_JSON_FILE)):$(VERSION) + +##@ K8s - EcoSystem + +.PHONY: build +build: k8s-delete image-import install-dogu-descriptor k8s-apply ## Builds a new version of the dogu and deploys it into the K8s-EcoSystem. + +##@ K8s - Dogu - Resource + +# The additional k8s yaml files +K8S_RESOURCE_PRODUCTIVE_FOLDER ?= $(WORKDIR)/k8s +K8S_RESOURCE_PRODUCTIVE_YAML ?= $(K8S_RESOURCE_PRODUCTIVE_FOLDER)/$(ARTIFACT_ID).yaml +K8S_RESOURCE_DOGU_CR_TEMPLATE_YAML ?= $(WORKDIR)/build/make/k8s-dogu.tpl +# The pre generation script creates a k8s resource yaml containing the dogu crd and the content from the k8s folder. +.PHONY: k8s-create-temporary-resource + k8s-create-temporary-resource: + @echo "Generating temporary k8s resources $(K8S_RESOURCE_TEMP_YAML)..." + @rm -f $(K8S_RESOURCE_TEMP_YAML) + @test -f $(K8S_RESOURCE_PRODUCTIVE_YAML) && (cp $(K8S_RESOURCE_PRODUCTIVE_YAML) $(K8S_RESOURCE_TEMP_YAML)) || (touch $(K8S_RESOURCE_TEMP_YAML)) + @echo "---" >> $(K8S_RESOURCE_TEMP_YAML) + @sed "s|NAMESPACE|$(ARTIFACT_NAMESPACE)|g" $(K8S_RESOURCE_DOGU_CR_TEMPLATE_YAML) | sed "s|NAME|$(ARTIFACT_ID)|g" | sed "s|VERSION|$(VERSION)|g" >> $(K8S_RESOURCE_TEMP_YAML) + @echo "Done." + +##@ K8s - Dogu + +.PHONY: install-dogu-descriptor +install-dogu-descriptor: ## Installs a configmap with current dogu.json into the cluster. + @echo "Generate configmap from dogu.json..." + @kubectl create configmap "$(ARTIFACT_ID)-descriptor" --from-file=$(DOGU_JSON_FILE) --dry-run=client -o yaml | kubectl apply -f - + @echo "Done." \ No newline at end of file diff --git a/build/make/k8s-dogu.tpl b/build/make/k8s-dogu.tpl new file mode 100644 index 0000000..44cd9cf --- /dev/null +++ b/build/make/k8s-dogu.tpl @@ -0,0 +1,9 @@ +apiVersion: k8s.cloudogu.com/v1 +kind: Dogu +metadata: + name: NAME + labels: + dogu: NAME +spec: + name: NAMESPACE/NAME + version: VERSION \ No newline at end of file diff --git a/build/make/k8s.mk b/build/make/k8s.mk new file mode 100644 index 0000000..16b611f --- /dev/null +++ b/build/make/k8s.mk @@ -0,0 +1,103 @@ +# This file is optional and can be used to set personal information without committing them to the repository. +MY_ENV_FILE ?= $(WORKDIR)/.env +ifneq (,$(wildcard $(MY_ENV_FILE))) + include .env +endif + +## Variables + +# The cluster root variable is used to the build images to the cluster. It can be defined in a .myenv file. +K8S_CLUSTER_ROOT ?= +# The productive tag of the image +IMAGE ?= + +# Variables for the temporary yaml files. These are used as template to generate a development resource containing +# the current namespace and the dev image. +K8S_RESOURCE_TEMP_FOLDER ?= $(TARGET_DIR)/make/k8s +K8S_RESOURCE_TEMP_YAML ?= $(K8S_RESOURCE_TEMP_FOLDER)/$(ARTIFACT_ID).yaml + +# The current namespace is extracted from the current context. +K8S_CURRENT_NAMESPACE=$(shell kubectl config view --minify -o jsonpath='{..namespace}') + +##@ K8s - Variables + +.PHONY: check-all-vars +check-all-vars: check-k8s-cluster-root-env-var check-k8s-image-env-var check-k8s-artifact-id + +.PHONY: check-k8s-cluster-root-env-var +check-k8s-cluster-root-env-var: + @$(call check_defined, K8S_CLUSTER_ROOT, root path of your k3ces) + +.PHONY: check-k8s-image-env-var +check-k8s-image-env-var: + @$(call check_defined, IMAGE, docker image tag) + +.PHONY: check-k8s-artifact-id +check-k8s-artifact-id: + @$(call check_defined, ARTIFACT_ID, app/dogu name) + +##@ K8s - Resources + +${K8S_RESOURCE_TEMP_FOLDER}: + @mkdir -p $@ + +.PHONY: k8s-delete +k8s-delete: k8s-generate $(K8S_POST_GENERATE_TARGETS) ## Deletes all dogu related resources from the K8s cluster. + @echo "Delete old dogu resources..." + @kubectl delete -f $(K8S_RESOURCE_TEMP_YAML) --wait=false --ignore-not-found=true + +# The additional targets executed after the generate target, executed before each apply and delete. The generate target +# produces a temporary yaml. This yaml is accessible via K8S_RESOURCE_TEMP_YAML an can be changed before the apply/delete. +K8S_POST_GENERATE_TARGETS ?= +# The additional targets executed before the generate target, executed before each apply and delete. +K8S_PRE_GENERATE_TARGETS ?= k8s-create-temporary-resource +.PHONY: k8s-generate +k8s-generate: $(K8S_RESOURCE_TEMP_FOLDER) $(K8S_PRE_GENERATE_TARGETS) ## Generates the final resource yaml. + @echo "Applying general transformations..." + @sed -i "s/'{{ .Namespace }}'/$(K8S_CURRENT_NAMESPACE)/" $(K8S_RESOURCE_TEMP_YAML) + @yq -i e "(select(.kind == \"Deployment\").spec.template.spec.containers[]|select(.image == \"*$(ARTIFACT_ID)*\").image)=\"$(IMAGE)\"" $(K8S_RESOURCE_TEMP_YAML) + @echo "Done." + +.PHONY: k8s-apply +k8s-apply: k8s-generate $(K8S_POST_GENERATE_TARGETS) ## Applies all dogu related resources from the K8s cluster. + @echo "Apply new dogu resources..." + @kubectl apply -f $(K8S_RESOURCE_TEMP_YAML) + +##@ K8s - Docker + +.PHONY: docker-build +docker-build: check-k8s-image-env-var ## Builds the docker image of the k8s app. + @echo "Building docker image of dogu..." + DOCKER_BUILDKIT=1 docker build . -t $(IMAGE) + +${K8S_CLUSTER_ROOT}/${ARTIFACT_ID}.tar: check-k8s-artifact-id docker-build ## Saves the current image into a file into the K8s root path to be available on all nodes. + @echo "Saving image into k3ces as $(ARTIFACT_ID).tar..." + @docker save $(IMAGE) -o $(K8S_CLUSTER_ROOT)/$(ARTIFACT_ID).tar + @echo "Done." + +.PHONY: image-import +image-import: check-all-vars $(K8S_CLUSTER_ROOT)/$(ARTIFACT_ID).tar ## Imports the currently available image into the K8s cluster for all nodes. + @echo "Import $(K8S_CLUSTER_ROOT)/$(ARTIFACT_ID).tar into all K8s nodes..." + @cd $(K8S_CLUSTER_ROOT) && \ + for node in $$(vagrant status --machine-readable | grep "state,running" | awk -F',' '{print $$2}'); \ + do \ + echo "...$${node}"; \ + vagrant ssh $${node} -- -t "sudo k3s ctr images import /vagrant/${ARTIFACT_ID}.tar"; \ + done; + @echo "Done." + rm $(K8S_CLUSTER_ROOT)/$(ARTIFACT_ID).tar + +## Functions + +# Check that given variables are set and all have non-empty values, +# die with an error otherwise. +# +# Params: +# 1. Variable name(s) to test. +# 2. (optional) Error message to print. +check_defined = \ + $(strip $(foreach 1,$1, \ + $(call __check_defined,$1,$(strip $(value 2))))) +__check_defined = \ + $(if $(value $1),, \ + $(error Undefined $1$(if $2, ($2)))) diff --git a/build/make/static-analysis.mk b/build/make/static-analysis.mk index 2920d73..2fc71b0 100644 --- a/build/make/static-analysis.mk +++ b/build/make/static-analysis.mk @@ -60,4 +60,12 @@ $(LINT): $(TMP_DIR) @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TMP_DIR)/bin v1.33.0 $(REVIEW_DOG): $(TMP_DIR) - @curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh| sh -s -- -b $(TMP_DIR)/bin \ No newline at end of file + @curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh| sh -s -- -b $(TMP_DIR)/bin + +##@ Go Static Analysis + +${STATIC_ANALYSIS_DIR}/report-govet.out: ${SRC} $(STATIC_ANALYSIS_DIR) + @go vet ./... | tee $@ + +.PHONY: vet +vet: ${STATIC_ANALYSIS_DIR}/report-govet.out ## Run go vet against code. \ No newline at end of file diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2e3636a..02823ce 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,14 +1,14 @@ resources: - - manager.yaml +- manager.yaml generatorOptions: disableNameSuffixHash: true configMapGenerator: - - files: - - controller_manager_config.yaml - name: manager-config +- files: + - controller_manager_config.yaml + name: manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - - name: controller - newName: cloudogu/k8s-service-discovery - newTag: 0.1.0 +- name: controller + newName: cloudogu/k8s-service-discovery + newTag: 0.1.0 From 9fe08f5b17ecbc798ee7d6445535d6ef27806909 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Mon, 2 May 2022 08:30:12 +0200 Subject: [PATCH 02/19] Update makefiles to version 5.1.0 --- CHANGELOG.md | 2 ++ Makefile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b5dbc..547fb92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- Update makefiles to version 5.1.0 ## [v0.1.0] - 2022-04-20 ### Added diff --git a/Makefile b/Makefile index 6b169f4..5d945c5 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ VERSION=0.1.0 IMAGE=cloudogu/${ARTIFACT_ID}:${VERSION} GOTAG?=1.17.7 -MAKEFILES_VERSION=5.0.0 +MAKEFILES_VERSION=5.1.0 include build/make/variables.mk include build/make/self-update.mk From a55b8b05a5f4794563f87bd820a409f52824d2a8 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Tue, 31 May 2022 16:42:07 +0200 Subject: [PATCH 03/19] Change controller for the ingress class from `ingress-nginx` to `nginx-ingress` This is a required operation as we renamed our nginx ingress from `ingress-nginx` to `nginx-ingress`. Otherwise, the `nginx-ingress` would ignore all created ingress objects. --- controllers/ingress_class_creator.go | 2 +- controllers/ingress_class_creator_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/ingress_class_creator.go b/controllers/ingress_class_creator.go index 9e5c04b..72e9ad2 100644 --- a/controllers/ingress_class_creator.go +++ b/controllers/ingress_class_creator.go @@ -48,7 +48,7 @@ func (icc IngressClassCreator) CreateIngressClass(logger logr.Logger) error { Name: icc.ClassName, }, Spec: networking.IngressClassSpec{ - Controller: "k8s.io/ingress-nginx", + Controller: "k8s.io/nginx-ingress", }, } diff --git a/controllers/ingress_class_creator_test.go b/controllers/ingress_class_creator_test.go index 7901813..b097a5e 100644 --- a/controllers/ingress_class_creator_test.go +++ b/controllers/ingress_class_creator_test.go @@ -56,7 +56,7 @@ func TestIngressClassCreator_CreateIngressClass(t *testing.T) { Name: "myIngressClass", }, Spec: networking.IngressClassSpec{ - Controller: "k8s.io/ingress-nginx", + Controller: "k8s.io/nginx-ingress", }, } clientMock := testclient.NewClientBuilder().WithScheme(getScheme()).WithObjects(ingressClass).Build() From 9b8b03d54e9b595e715ab4ac136430d83e382395 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 1 Jun 2022 10:34:44 +0200 Subject: [PATCH 04/19] #3 Implement etcd watcher for generating a menu.json for the warp menu. --- .env.template | 21 +++ .gitignore | 5 +- Makefile | 146 +++-------------- build/make/k8s-controller.mk | 100 ++++++++++++ build/make/k8s-dogu.mk | 50 ++++++ build/make/k8s-dogu.tpl | 9 ++ build/make/k8s.mk | 124 +++++++++++++++ build/make/static-analysis.mk | 14 +- config/manager/kustomization.yaml | 14 +- config/manager/manager.yaml | 1 + controllers/warp/category.go | 46 ++++++ controllers/warp/configReader.go | 172 +++++++++++++++++++++ controllers/warp/configReader_test.go | 77 +++++++++ controllers/warp/dogu_entry.go | 108 +++++++++++++ controllers/warp/external_entry.go | 70 +++++++++ controllers/warp/link_entry.go | 51 ++++++ controllers/warp/warp.go | 101 ++++++++++++ controllers/warp/warp_test.go | 44 ++++++ controllers/warp_menu_creator.go | 134 ++++++++++++++++ go.mod | 23 ++- go.sum | 50 ++++-- k8s/dev-resources/k8s-ces-warp-config.yaml | 19 +++ k8s/k8s-ces-warp-config.yaml | 30 ++++ main.go | 14 ++ 24 files changed, 1264 insertions(+), 159 deletions(-) create mode 100644 .env.template create mode 100644 build/make/k8s-controller.mk create mode 100644 build/make/k8s-dogu.mk create mode 100644 build/make/k8s-dogu.tpl create mode 100644 build/make/k8s.mk create mode 100644 controllers/warp/category.go create mode 100644 controllers/warp/configReader.go create mode 100644 controllers/warp/configReader_test.go create mode 100644 controllers/warp/dogu_entry.go create mode 100644 controllers/warp/external_entry.go create mode 100644 controllers/warp/link_entry.go create mode 100644 controllers/warp/warp.go create mode 100644 controllers/warp/warp_test.go create mode 100644 controllers/warp_menu_creator.go create mode 100644 k8s/dev-resources/k8s-ces-warp-config.yaml create mode 100644 k8s/k8s-ces-warp-config.yaml diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..458717a --- /dev/null +++ b/.env.template @@ -0,0 +1,21 @@ +# Usage: +# 1. Copy this file as `.env` into your project +# 2. Adapt the information below with the your personal data. +# 3. INFO: escape special characters like # with \ +# +# The file `.env` is ignored by git. Note: DO NOT COMMIT your personal data. + +export NAMESPACE=$(shell kubectl config view --minify -o jsonpath='{..namespace}') + +export K8S_CLUSTER_ROOT= + +# credentials for the dogu registry +export DOGU_REGISTRY_ENDPOINT=https://dogu.cloudogu.com/api/v2/dogus +export DOGU_REGISTRY_USERNAME= +export DOGU_REGISTRY_PASSWORD= + +# credentials for the docker registry +docker_registry_server= +docker_registry_username= +docker_registry_password= +export DOCKER_REGISTRY={"auths":{"${docker_registry_server}":{"username":"${docker_registry_username}","password":"${docker_registry_password}","email":"ignore@me.com","auth":"ignoreMe"}}} diff --git a/.gitignore b/.gitignore index 193d53b..e3cc700 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,7 @@ vendor *.swp *.swo .code -*~ \ No newline at end of file +*~ + +# Makefiles +.env \ No newline at end of file diff --git a/Makefile b/Makefile index 9df83f1..40f6049 100644 --- a/Makefile +++ b/Makefile @@ -2,31 +2,18 @@ ARTIFACT_ID=k8s-service-discovery VERSION=0.1.0 -GOTAG?=1.17.7 -MAKEFILES_VERSION=5.0.0 - -# Image URL to use all building/pushing image targets +## Image URL to use all building/pushing image targets +IMAGE_DEV=${K3CES_REGISTRY_URL_PREFIX}/${ARTIFACT_ID}:${VERSION} IMAGE=cloudogu/${ARTIFACT_ID}:${VERSION} +GOTAG?=1.17.7 +MAKEFILES_VERSION=6.0.1 -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.23 -K8S_INTEGRATION_TEST_DIR=${TARGET_DIR}/k8s-integration-test -K8S_RESOURCE_YAML=${TARGET_DIR}/${ARTIFACT_ID}_${VERSION}.yaml -DEFAULT_NAMESPACE?="$(shell kubectl config view | grep namespace | head -n 1 | sed "s/^.*namespace: //g")" - -# make sure to create a statically linked binary otherwise it may quit with -# "exec user process caused: no such file or directory" -GO_BUILD_FLAGS=-mod=vendor -a -tags netgo,osusergo $(LDFLAGS) -o $(BINARY) -# remove DWARF symbol table and strip other symbols to shave ~13 MB from binary -ADDITIONAL_LDFLAGS=-extldflags -static -w -s +ADDITIONAL_CLEAN=dist-clean -.DEFAULT_GOAL:=help +K8S_RESOURCE_DIR=${WORKDIR}/k8s +K8S_WARP_CONFIG_RESOURCE_YAML=${K8S_RESOURCE_DIR}/k8s-ces-warp-config.yaml include build/make/variables.mk - -ADDITIONAL_CLEAN=dist-clean -PRE_COMPILE=generate vet - include build/make/self-update.mk include build/make/dependencies-gomod.mk include build/make/build.mk @@ -37,27 +24,12 @@ include build/make/static-analysis.mk include build/make/clean.mk include build/make/digital-signature.mk +K8S_RUN_PRE_TARGETS=setup-etcd-port-forward +PRE_COMPILE=generate vet +K8S_PRE_GENERATE_TARGETS=k8s-create-temporary-resource generate-warp-config -# Setting SHELL to bash allows bash commands to be executed by recipes. -# This is a requirement for 'setup-envtest.sh' in the test target. -# 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 - -##@ EcoSystem - -.PHONY: build -build: docker-build image-import k8s-apply ## Builds a new version of the setup and deploys it into the K8s-EcoSystem. - -##@ Development (without go container) - -${STATIC_ANALYSIS_DIR}/report-govet.out: ${SRC} $(STATIC_ANALYSIS_DIR) - @go vet ./... | tee $@ - -.PHONY: vet -vet: ${STATIC_ANALYSIS_DIR}/report-govet.out ## Run go vet against code. +include build/make/k8s-controller.mk -##@ Kubernetes Controller .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. @@ -69,95 +41,15 @@ generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and @echo "Auto-generate deepcopy functions..." @$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." -$(K8S_INTEGRATION_TEST_DIR): - @mkdir -p $@ - -.PHONY: k8s-integration-test -k8s-integration-test: $(K8S_INTEGRATION_TEST_DIR) manifests generate vet envtest ## Run k8s integration tests. - @echo "Running k8s integration tests..." - @KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -tags=k8s_integration ./... -coverprofile ${K8S_INTEGRATION_TEST_DIR}/report-k8s-integration.out - -##@ Build - -.PHONY: build-controller -build-controller: ${SRC} compile ## Builds the controller Go binary. - -.PHONY: run -run: manifests generate vet ## Run a controller from your host. - go run ./main.go - -##@ Release - -.PHONY: controller-release -controller-release: ## Interactively starts the release workflow. - @echo "Starting git flow release..." - @build/make/release.sh controller-tool - -##@ Docker - -.PHONY: docker-build -docker-build: ${SRC} ## Builds the docker image of the k8s-ces-setup `cloudogu/k8s-ces-setup:version`. - @echo "Building docker image of dogu..." - DOCKER_BUILDKIT=1 docker build . -t ${IMAGE} - -${K8S_CLUSTER_ROOT}/image.tar: check-k8s-cluster-root-env-var - # Saves the `cloudogu/k8s-ces-setup:version` image into a file into the K8s root path to be available on all nodes. - docker save ${IMAGE} -o ${K8S_CLUSTER_ROOT}/image.tar - -.PHONY: image-import -image-import: ${K8S_CLUSTER_ROOT}/image.tar - # Imports the currently available image `cloudogu/k8s-ces-setup:version` into the K8s cluster for all nodes. - @echo "Import docker image of dogu into all K8s nodes..." - @cd ${K8S_CLUSTER_ROOT} && \ - for node in $$(vagrant status --machine-readable | grep "state,running" | awk -F',' '{print $$2}'); \ - do \ - echo "...$${node}"; \ - vagrant ssh $${node} -- -t "sudo k3s ctr images import /vagrant/image.tar"; \ - done; - @echo "Done." - rm ${K8S_CLUSTER_ROOT}/image.tar - -.PHONY: check-k8s-cluster-root-env-var -check-k8s-cluster-root-env-var: - @echo "Checking if env var K8S_CLUSTER_ROOT is set..." - @bash -c export -p | grep K8S_CLUSTER_ROOT - @echo "Done." - -##@ Deployment - -ifndef ignore-not-found - ignore-not-found = false -endif - -${K8S_RESOURCE_YAML}: ${TARGET_DIR} manifests kustomize - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMAGE} - $(KUSTOMIZE) build config/default > ${K8S_RESOURCE_YAML} - -.PHONY: k8s-generate -k8s-generate: ${K8S_RESOURCE_YAML} ## Create required k8s resources in ./dist/... - @echo "Generating new kubernetes resources..." - -.PHONY: k8s-apply -k8s-apply: k8s-generate ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cat ${K8S_RESOURCE_YAML} | sed "s/{{ .Namespace }}/${DEFAULT_NAMESPACE}/" | kubectl apply -f - - -.PHONY: k8s-delete -k8s-delete: k8s-generate ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - cat ${K8S_RESOURCE_YAML} | kubectl delete --ignore-not-found=$(ignore-not-found) -f - +## Local Development -##@ Download Kubernetes Utility Tools +.PHONY: setup-etcd-port-forward +setup-etcd-port-forward: + kubectl port-forward etcd-0 4001:2379 & -CONTROLLER_GEN = $(UTILITY_BIN_PATH)/controller-gen -.PHONY: controller-gen -controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) +.PHONY: generate-warp-config +generate-warp-config: + @echo "---" >> $(K8S_RESOURCE_TEMP_YAML) + @cat $(K8S_WARP_CONFIG_RESOURCE_YAML) >> $(K8S_RESOURCE_TEMP_YAML) -KUSTOMIZE = $(UTILITY_BIN_PATH)/kustomize -.PHONY: kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.2) -ENVTEST = $(UTILITY_BIN_PATH)/setup-envtest -.PHONY: envtest -envtest: ## Download envtest-setup locally if necessary. - $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) \ No newline at end of file diff --git a/build/make/k8s-controller.mk b/build/make/k8s-controller.mk new file mode 100644 index 0000000..9b633cf --- /dev/null +++ b/build/make/k8s-controller.mk @@ -0,0 +1,100 @@ +# This script can be used to build and deploy kubernetes controllers. It is required to implement the controller +# specific targets `manifests` and `generate`: +# +# Examples: +# +#.PHONY: manifests +#manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +# @echo "Generate manifests..." +# @$(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. +# @echo "Auto-generate deepcopy functions..." +# @$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + + +# This script required the k8s.mk script +include $(WORKDIR)/build/make/k8s.mk + +## Variables + +# Contains the artifact yaml used as +K8S_RESOURCE_TEMP_YAML=${TARGET_DIR}/${ARTIFACT_ID}_${VERSION}.yaml + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# This is a requirement for 'setup-envtest.sh' in the test target. +# 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 + +# make sure to create a statically linked binary otherwise it may quit with +# "exec user process caused: no such file or directory" +GO_BUILD_FLAGS=-mod=vendor -a -tags netgo,osusergo $(LDFLAGS) -o $(BINARY) + +# remove DWARF symbol table and strip other symbols to shave ~13 MB from binary +ADDITIONAL_LDFLAGS=-extldflags -static -w -s + +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.23 +K8S_INTEGRATION_TEST_DIR=${TARGET_DIR}/k8s-integration-test + +##@ K8s - EcoSystem + +.PHONY: build +build: k8s-delete image-import k8s-apply ## Builds a new version of the dogu and deploys it into the K8s-EcoSystem. + +##@ Release + +.PHONY: controller-release +controller-release: ## Interactively starts the release workflow. + @echo "Starting git flow release..." + @build/make/release.sh controller-tool + +##@ K8s - Development + +.PHONY: build-controller +build-controller: ${SRC} compile ## Builds the controller Go binary. + +# Allows to perform tasks before locally running the controller +K8S_RUN_PRE_TARGETS ?= +.PHONY: run +run: manifests generate vet $(K8S_RUN_PRE_TARGETS) ## Run a controller from your host. + go run -ldflags "-X main.Version=$(VERSION)" ./main.go + +##@ K8s - Integration test with envtest + +$(K8S_INTEGRATION_TEST_DIR): + @mkdir -p $@ + +.PHONY: k8s-integration-test +k8s-integration-test: $(K8S_INTEGRATION_TEST_DIR) manifests generate vet envtest ## Run k8s integration tests. + @echo "Running K8s integration tests..." + @KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -tags=k8s_integration ./... -coverprofile ${K8S_INTEGRATION_TEST_DIR}/report-k8s-integration.out + +##@ K8s - Controller Resource + +# The pre generation script creates a K8s resource yaml containing generated manager yaml. +.PHONY: k8s-create-temporary-resource + k8s-create-temporary-resource: ${TARGET_DIR} manifests kustomize + @echo "Generating temporary k8s resources $(K8S_RESOURCE_TEMP_YAML)..." + cd $(WORKDIR)/config/manager && $(KUSTOMIZE) edit set image controller=$(IMAGE) + $(KUSTOMIZE) build config/default > $(K8S_RESOURCE_TEMP_YAML) + @echo "Done." + +##@ K8s - Download Kubernetes Utility Tools + +CONTROLLER_GEN = $(UTILITY_BIN_PATH)/controller-gen +.PHONY: controller-gen +controller-gen: ## Download controller-gen locally if necessary. + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) + +KUSTOMIZE = $(UTILITY_BIN_PATH)/kustomize +.PHONY: kustomize +kustomize: ## Download kustomize locally if necessary. + $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.2) + +ENVTEST = $(UTILITY_BIN_PATH)/setup-envtest +.PHONY: envtest +envtest: ## Download envtest-setup locally if necessary. + $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) \ No newline at end of file diff --git a/build/make/k8s-dogu.mk b/build/make/k8s-dogu.mk new file mode 100644 index 0000000..294e841 --- /dev/null +++ b/build/make/k8s-dogu.mk @@ -0,0 +1,50 @@ + +# Variables + +# Path to the dogu json of the dogu +DOGU_JSON_FILE=$(WORKDIR)/dogu.json +DOGU_JSON_DEV_FILE=${TARGET_DIR}/dogu.json +# Name of the dogu is extracted from the dogu.json +ARTIFACT_ID=$(shell yq -e ".Name" $(DOGU_JSON_FILE) | sed "s|.*/||g") +# Namespace of the dogu is extracted from the dogu.json +ARTIFACT_NAMESPACE=$(shell yq -e ".Name" $(DOGU_JSON_FILE) | sed "s|/.*||g") +# Namespace of the dogu is extracted from the dogu.json +VERSION=$(shell yq -e ".Version" $(DOGU_JSON_FILE)) +# Image of the dogu is extracted from the dogu.json +IMAGE=$(shell yq -e ".Image" $(DOGU_JSON_FILE)):$(VERSION) +IMAGE_DEV_WITHOUT_TAG=$(shell yq -e ".Image" $(DOGU_JSON_FILE) | sed "s|registry\.cloudogu\.com\(.\+\)|${K3CES_REGISTRY_URL_PREFIX}\1|g") +IMAGE_DEV=${IMAGE_DEV_WITHOUT_TAG}:${VERSION} + +include $(WORKDIR)/build/make/k8s.mk + +##@ K8s - EcoSystem + +.PHONY: build +build: k8s-delete image-import install-dogu-descriptor k8s-apply ## Builds a new version of the dogu and deploys it into the K8s-EcoSystem. + +##@ K8s - Dogu - Resource + +# The additional k8s yaml files +K8S_RESOURCE_PRODUCTIVE_FOLDER ?= $(WORKDIR)/k8s +K8S_RESOURCE_PRODUCTIVE_YAML ?= $(K8S_RESOURCE_PRODUCTIVE_FOLDER)/$(ARTIFACT_ID).yaml +K8S_RESOURCE_DOGU_CR_TEMPLATE_YAML ?= $(WORKDIR)/build/make/k8s-dogu.tpl +# The pre generation script creates a k8s resource yaml containing the dogu crd and the content from the k8s folder. +.PHONY: k8s-create-temporary-resource + k8s-create-temporary-resource: + @echo "Generating temporary K8s resources $(K8S_RESOURCE_TEMP_YAML)..." + @rm -f $(K8S_RESOURCE_TEMP_YAML) + @test -f $(K8S_RESOURCE_PRODUCTIVE_YAML) && (cp $(K8S_RESOURCE_PRODUCTIVE_YAML) $(K8S_RESOURCE_TEMP_YAML)) || (touch $(K8S_RESOURCE_TEMP_YAML)) + @echo "---" >> $(K8S_RESOURCE_TEMP_YAML) + @sed "s|NAMESPACE|$(ARTIFACT_NAMESPACE)|g" $(K8S_RESOURCE_DOGU_CR_TEMPLATE_YAML) | sed "s|NAME|$(ARTIFACT_ID)|g" | sed "s|VERSION|$(VERSION)|g" >> $(K8S_RESOURCE_TEMP_YAML) + @echo "Done." + +##@ K8s - Dogu + +.PHONY: install-dogu-descriptor +install-dogu-descriptor: ## Installs a configmap with current dogu.json into the cluster. + @echo "Generate configmap from dogu.json..." + + @jq ".Image=\"${IMAGE_DEV_WITHOUT_TAG}\"" ${DOGU_JSON_FILE} > ${DOGU_JSON_DEV_FILE} + + @kubectl create configmap "$(ARTIFACT_ID)-descriptor" --from-file=$(DOGU_JSON_DEV_FILE) --dry-run=client -o yaml | kubectl apply -f - + @echo "Done." \ No newline at end of file diff --git a/build/make/k8s-dogu.tpl b/build/make/k8s-dogu.tpl new file mode 100644 index 0000000..44cd9cf --- /dev/null +++ b/build/make/k8s-dogu.tpl @@ -0,0 +1,9 @@ +apiVersion: k8s.cloudogu.com/v1 +kind: Dogu +metadata: + name: NAME + labels: + dogu: NAME +spec: + name: NAMESPACE/NAME + version: VERSION \ No newline at end of file diff --git a/build/make/k8s.mk b/build/make/k8s.mk new file mode 100644 index 0000000..5189816 --- /dev/null +++ b/build/make/k8s.mk @@ -0,0 +1,124 @@ +# This file is optional and can be used to set personal information without committing them to the repository. +MY_ENV_FILE ?= $(WORKDIR)/.env +ifneq (,$(wildcard $(MY_ENV_FILE))) + include .env +endif + +## Variables + +# The cluster root variable is used to the build images to the cluster. It can be defined in a .myenv file. +K8S_CLUSTER_ROOT ?= +# The productive tag of the image +IMAGE ?= + +K3S_CLUSTER_FQDN?=k3ces.local +K3S_LOCAL_REGISTRY_PORT?=30099 +K3CES_REGISTRY_URL_PREFIX="${K3S_CLUSTER_FQDN}:${K3S_LOCAL_REGISTRY_PORT}" + +# Variables for the temporary yaml files. These are used as template to generate a development resource containing +# the current namespace and the dev image. +K8S_RESOURCE_TEMP_FOLDER ?= $(TARGET_DIR)/make/k8s +K8S_RESOURCE_TEMP_YAML ?= $(K8S_RESOURCE_TEMP_FOLDER)/$(ARTIFACT_ID).yaml + +# The current namespace is extracted from the current context. +K8S_CURRENT_NAMESPACE=$(shell kubectl config view --minify -o jsonpath='{..namespace}') + +##@ K8s - Variables + +.PHONY: check-all-vars +check-all-vars: check-k8s-cluster-root-env-var check-k8s-image-env-var check-k8s-artifact-id check-etc-hosts check-insecure-cluster-registry ## Conduct a sanity check against selected build artefacts or local environment + +.PHONY: check-k8s-cluster-root-env-var +check-k8s-cluster-root-env-var: + @$(call check_defined, K8S_CLUSTER_ROOT, root path of your k3ces) + +.PHONY: check-k8s-image-env-var +check-k8s-image-env-var: + @$(call check_defined, IMAGE, docker image tag) + +.PHONY: check-k8s-artifact-id +check-k8s-artifact-id: + @$(call check_defined, ARTIFACT_ID, app/dogu name) + +.PHONY: check-etc-hosts +check-etc-hosts: + @grep -E "^.+\s+${K3S_CLUSTER_FQDN}\$$" /etc/hosts > /dev/null || \ + (echo "Missing /etc/hosts entry for ${K3S_CLUSTER_FQDN}" && exit 1) + +.PHONY: check-insecure-cluster-registry +check-insecure-cluster-registry: + @grep "${K3CES_REGISTRY_URL_PREFIX}" /etc/docker/daemon.json > /dev/null || \ + (echo "Missing /etc/docker/daemon.json for ${K3CES_REGISTRY_URL_PREFIX}" && exit 1) + +##@ K8s - Resources + +${K8S_RESOURCE_TEMP_FOLDER}: + @mkdir -p $@ + +.PHONY: k8s-delete +k8s-delete: k8s-generate $(K8S_POST_GENERATE_TARGETS) ## Deletes all dogu related resources from the K8s cluster. + @echo "Delete old dogu resources..." + @kubectl delete -f $(K8S_RESOURCE_TEMP_YAML) --wait=false --ignore-not-found=true + +# The additional targets executed after the generate target, executed before each apply and delete. The generate target +# produces a temporary yaml. This yaml is accessible via K8S_RESOURCE_TEMP_YAML an can be changed before the apply/delete. +K8S_POST_GENERATE_TARGETS ?= +# The additional targets executed before the generate target, executed before each apply and delete. +K8S_PRE_GENERATE_TARGETS ?= k8s-create-temporary-resource + +.PHONY: k8s-generate +k8s-generate: binary-yq $(K8S_RESOURCE_TEMP_FOLDER) $(K8S_PRE_GENERATE_TARGETS) ## Generates the final resource yaml. + @echo "Applying general transformations..." + @sed -i "s/'{{ .Namespace }}'/$(K8S_CURRENT_NAMESPACE)/" $(K8S_RESOURCE_TEMP_YAML) + @$(BINARY_YQ) -i e "(select(.kind == \"Deployment\").spec.template.spec.containers[]|select(.image == \"*$(ARTIFACT_ID)*\").image)=\"$(IMAGE_DEV)\"" $(K8S_RESOURCE_TEMP_YAML) + @echo "Done." + +.PHONY: k8s-apply +k8s-apply: k8s-generate $(K8S_POST_GENERATE_TARGETS) ## Applies all generated K8s resources to the current cluster and namespace. + @echo "Apply generated K8s resources..." + @kubectl apply -f $(K8S_RESOURCE_TEMP_YAML) + +##@ K8s - Docker + +.PHONY: docker-build +docker-build: check-k8s-image-env-var ## Builds the docker image of the K8s app. + @echo "Building docker image..." + DOCKER_BUILDKIT=1 docker build . -t $(IMAGE) + +.PHONY: docker-dev-tag +docker-dev-tag: check-k8s-image-dev-var docker-build ## Tags a Docker image for local K3ces deployment. + @echo "Tagging image with dev tag..." + DOCKER_BUILDKIT=1 docker tag ${IMAGE} ${IMAGE_DEV} + +.PHONY: check-k8s-image-dev-var +check-k8s-image-dev-var: +ifeq (${IMAGE_DEV},) + @echo "Missing make variable IMAGE_DEV detected. It should look like \$${K3CES_REGISTRY_URL_PREFIX}/docker-image:tag" + @exit 19 +endif + +.PHONY: image-import +image-import: check-all-vars check-k8s-artifact-id docker-dev-tag ## Imports the currently available image into the cluster-local registry. + @echo "Import ${IMAGE_DEV} into K8s cluster ${K3S_CLUSTER_FQDN}..." + @docker push ${IMAGE_DEV} + @echo "Done." + +## Functions + +# Check that given variables are set and all have non-empty values, +# die with an error otherwise. +# +# Params: +# 1. Variable name(s) to test. +# 2. (optional) Error message to print. +check_defined = \ + $(strip $(foreach 1,$1, \ + $(call __check_defined,$1,$(strip $(value 2))))) +__check_defined = \ + $(if $(value $1),, \ + $(error Undefined $1$(if $2, ($2)))) + +BINARY_YQ = $(UTILITY_BIN_PATH)/yq +.PHONY: binary-yq +binary-yq: ## Download controller-gen locally if necessary. + $(call go-get-tool,$(BINARY_YQ),github.com/mikefarah/yq/v4@v4.25.1) \ No newline at end of file diff --git a/build/make/static-analysis.mk b/build/make/static-analysis.mk index 2920d73..238dae8 100644 --- a/build/make/static-analysis.mk +++ b/build/make/static-analysis.mk @@ -7,6 +7,7 @@ CUSTOM_GO_MOUNT?=-v /tmp:/tmp REVIEW_DOG=$(TMP_DIR)/bin/reviewdog LINT=$(TMP_DIR)/bin/golangci-lint +LINT_VERSION?=v1.33.0 # ignore tests and mocks LINTFLAGS=--tests=false --skip-files="^.*_mock.go$$" --skip-files="^.*/mock.*.go$$" @@ -57,7 +58,16 @@ static-analysis-ci-report-local: $(STATIC_ANALYSIS_DIR)/static-analysis-cs.log $ @cat $(STATIC_ANALYSIS_DIR)/static-analysis-cs.log | $(REVIEW_DOG) -f checkstyle -diff "git diff develop" $(LINT): $(TMP_DIR) - @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TMP_DIR)/bin v1.33.0 + @echo "Download golangci-lint $(LINT_VERSION)..." + @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TMP_DIR)/bin $(LINT_VERSION) $(REVIEW_DOG): $(TMP_DIR) - @curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh| sh -s -- -b $(TMP_DIR)/bin \ No newline at end of file + @curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh| sh -s -- -b $(TMP_DIR)/bin + +##@ Go Static Analysis + +${STATIC_ANALYSIS_DIR}/report-govet.out: ${SRC} $(STATIC_ANALYSIS_DIR) + @go vet ./... | tee $@ + +.PHONY: vet +vet: ${STATIC_ANALYSIS_DIR}/report-govet.out ## Run go vet against code. \ No newline at end of file diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2e3636a..02823ce 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,14 +1,14 @@ resources: - - manager.yaml +- manager.yaml generatorOptions: disableNameSuffixHash: true configMapGenerator: - - files: - - controller_manager_config.yaml - name: manager-config +- files: + - controller_manager_config.yaml + name: manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - - name: controller - newName: cloudogu/k8s-service-discovery - newTag: 0.1.0 +- name: controller + newName: cloudogu/k8s-service-discovery + newTag: 0.1.0 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 75dee01..412f6dd 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -24,6 +24,7 @@ spec: args: - --leader-elect image: controller:latest + imagePullPolicy: Always env: - name: WATCH_NAMESPACE valueFrom: diff --git a/controllers/warp/category.go b/controllers/warp/category.go new file mode 100644 index 0000000..1c2e6ce --- /dev/null +++ b/controllers/warp/category.go @@ -0,0 +1,46 @@ +package warp + +// Category Category of multiple entries in the warp menu +type Category struct { + Title string + Order int + Entries Entries +} + +func (c Category) String() string { + return c.Title +} + +// Categories collection of warp Categories +type Categories []*Category + +func (c Categories) Len() int { + return len(c) +} + +func (c Categories) Less(i, j int) bool { + if c[i].Order == c[j].Order { + return c[i].Title < c[j].Title + } + return c[i].Order > c[j].Order +} + +func (c Categories) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + +func (c *Categories) insertCategories(newCategories Categories) { + for _, newCategory := range newCategories { + c.insertCategory(newCategory) + } +} + +func (c *Categories) insertCategory(newCategory *Category) { + for _, category := range *c { + if category.Title == newCategory.Title { + category.Entries = append(category.Entries, newCategory.Entries...) + return + } + } + *c = append(*c, newCategory) +} diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go new file mode 100644 index 0000000..110e01d --- /dev/null +++ b/controllers/warp/configReader.go @@ -0,0 +1,172 @@ +package warp + +import ( + "encoding/json" + "github.com/cloudogu/cesapp-lib/registry" + "log" + + "sort" + + "github.com/pkg/errors" +) + +// ConfigReader reads the configuration for the warp menu from etcd +type ConfigReader struct { + configuration *Configuration + registry registry.WatchConfigurationContext +} + +type DisabledSupportEntries struct { + name []string +} + +const disableWarpSupportEntriesConfigurationKey = "/config/_global/disabled_warpmenu_support_entries" + +func (reader *ConfigReader) createCategories(entries []EntryWithCategory) Categories { + categories := map[string]*Category{} + + for _, entry := range entries { + categoryName := entry.Category + category := categories[categoryName] + if category == nil { + category = &Category{ + Title: categoryName, + Entries: Entries{}, + Order: reader.configuration.Order[categoryName], + } + categories[categoryName] = category + } + category.Entries = append(category.Entries, entry.Entry) + } + + result := Categories{} + for _, cat := range categories { + sort.Sort(cat.Entries) + result = append(result, cat) + } + sort.Sort(result) + return result +} + +// dogusReader reads from etcd and converts the keys and values to a warp menu +// conform structure +func (reader *ConfigReader) dogusReader(source Source) (Categories, error) { + log.Printf("read dogus from %s for warp menu", source.Path) + resp, err := reader.registry.GetChildrenPaths(source.Path) + if err != nil { + return nil, errors.Wrapf(err, "failed to read root entry %s from etcd", source.Path) + } + dogus := []EntryWithCategory{} + for _, child := range resp { + dogu, err := readAndUnmarshalDogu(reader.registry, child, source.Tag) + if err != nil { + log.Printf("failed to read and unmarshal dogu: %v", err) + } else if dogu.Entry.Title != "" { + dogus = append(dogus, dogu) + } + } + + return reader.createCategories(dogus), nil +} + +func (reader *ConfigReader) externalsReader(source Source) (Categories, error) { + log.Printf("read externals from %s for warp menu", source.Path) + resp, err := reader.registry.GetChildrenPaths(source.Path) + if err != nil { + return nil, errors.Wrapf(err, "failed to read root entry %s from etcd", source.Path) + } + externals := []EntryWithCategory{} + for _, child := range resp { + external, err := readAndUnmarshalExternal(reader.registry, child) + if err != nil { + log.Printf("failed to read and unmarshal external: %v", err) + } else { + externals = append(externals, external) + } + } + return reader.createCategories(externals), nil +} + +func (reader *ConfigReader) readSource(source Source) (Categories, error) { + switch source.Type { + case "dogus": + return reader.dogusReader(source) + case "externals": + return reader.externalsReader(source) + } + return nil, errors.Errorf("wrong source type: %v", source.Type) +} + +func (reader *ConfigReader) getDisabledSupportIdentifiers() ([]string, error) { + disabledSupportEntries, err := reader.registry.Get(disableWarpSupportEntriesConfigurationKey) + if err != nil { + return []string{}, errors.Wrapf(err, "failed to read configuration entry %s from etcd", disableWarpSupportEntriesConfigurationKey) + } + + var disabledEntries []string + err = json.Unmarshal([]byte(disabledSupportEntries), &disabledEntries) + if err != nil { + return []string{}, errors.Wrapf(err, "failed to unmarshal etcd key") + } + + return disabledEntries, nil +} + +func (reader *ConfigReader) readSupport(supportSources []SupportSource, disabledSupportEntries []string) (Categories, error) { + var supportEntries []EntryWithCategory + + for _, supportSource := range supportSources { + // supportSource -> EntryWithCategory + if !StringInSlice(supportSource.Identifier, disabledSupportEntries) { + entry := Entry{} + if supportSource.External { + entry = Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: TARGET_EXTERNAL} + } else { + entry = Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: TARGET_SELF} + } + entryWithCategory := EntryWithCategory{Entry: entry, Category: "Support"} + supportEntries = append(supportEntries, entryWithCategory) + } + } + + return reader.createCategories(supportEntries), nil +} + +func (reader *ConfigReader) readFromConfig(configuration *Configuration) (Categories, error) { + var data Categories + + for _, source := range configuration.Sources { + categories, err := reader.readSource(source) + if err != nil { + log.Println("Error during read:", err) + } + data.insertCategories(categories) + } + + log.Println("read SupportEntries") + disabledSupportEntries, err := reader.getDisabledSupportIdentifiers() + if err != nil { + log.Printf("Warning, could not read etcd Key: %v. Err: %v", disableWarpSupportEntriesConfigurationKey, err) + } + // add support Category + supportCategory, err := reader.readSupport(configuration.Support, disabledSupportEntries) + if err != nil { + log.Println("Error during support read:", err) + } + if supportCategory.Len() == 0 { + log.Printf("support Category is empty, no support Category will be added to menu.json") + return data, nil + } + + data.insertCategories(supportCategory) + return data, nil +} + +func StringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} diff --git a/controllers/warp/configReader_test.go b/controllers/warp/configReader_test.go new file mode 100644 index 0000000..4fb71cc --- /dev/null +++ b/controllers/warp/configReader_test.go @@ -0,0 +1,77 @@ +package warp + +import ( + "github.com/cloudogu/cesapp-lib/registry/mocks" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestConfigReader_readSupport(t *testing.T) { + reader := &ConfigReader{ + configuration: &Configuration{Support: []SupportSource{}}, + registry: nil, + } + + supportSources := []SupportSource{{Identifier: "aboutCloudoguToken", External: false, Href: "/local/href"}, {Identifier: "myCloudogu", External: true, Href: "https://ecosystem.cloudogu.com/"}, {Identifier: "docsCloudoguComUrl", External: true, Href: "https://docs.cloudogu.com/"}} + + actual, err := reader.readSupport(supportSources, []string{"docsCloudoguComUrl"}) + + if err != nil { + t.Fail() + } + + expectedCategories := Categories{{Title: "Support", Entries: []Entry{ + {Title: "aboutCloudoguToken", Target: TARGET_SELF, Href: "/local/href"}, + {Title: "myCloudogu", Target: TARGET_EXTERNAL, Href: "https://ecosystem.cloudogu.com/"}}}} + + assert.Equal(t, expectedCategories, actual, "readSupport did not return the correct Category of two entries") + + // test with empty filter + actual, err = reader.readSupport(supportSources, []string{}) + expectedCategories = Categories{ + {Title: "Support", Entries: []Entry{ + {Title: "aboutCloudoguToken", Target: TARGET_SELF, Href: "/local/href"}, + {Title: "myCloudogu", Target: TARGET_EXTERNAL, Href: "https://ecosystem.cloudogu.com/"}, + {Title: "docsCloudoguComUrl", Target: TARGET_EXTERNAL, Href: "https://docs.cloudogu.com/"}}}} + + // test with complete filter + actual, err = reader.readSupport(supportSources, []string{"myCloudogu", "aboutCloudoguToken", "docsCloudoguComUrl"}) + expectedCategories = Categories{} + + assert.Equal(t, 0, expectedCategories.Len()) + assert.Equal(t, expectedCategories, actual, "readSupport did not return the correct Category of three entries") +} + +func TestConfigReader_getDisabledSupportIdentifiers(t *testing.T) { + mockRegistry := &mocks.WatchConfigurationContext{} + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) + + reader := &ConfigReader{ + configuration: &Configuration{Support: []SupportSource{}}, + registry: mockRegistry, + } + + identifiers, err := reader.getDisabledSupportIdentifiers() + assert.Empty(t, err) + assert.Equal(t, []string{"lorem", "ipsum"}, identifiers) +} + +func TestConfigReader_readFromConfig(t *testing.T) { + mockRegistry := &mocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", "/path/to/etcd/key").Return([]string{"/path/to/etcd/key"}, nil) + mockRegistry.On("Get", "/path/to/etcd/key").Return("", nil) + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) + + reader := &ConfigReader{ + configuration: &Configuration{Support: []SupportSource{}}, + registry: mockRegistry, + } + + testSources := []Source{{Path: "/path/to/etcd/key", Type: "externals", Tag: "tag"}} + testSupportSoureces := []SupportSource{{Identifier: "supportSrc", External: true, Href: "path/to/external"}} + + actual, err := reader.readFromConfig(&Configuration{Sources: testSources, Support: testSupportSoureces}) + + assert.Empty(t, err) + assert.NotEmpty(t, actual) +} diff --git a/controllers/warp/dogu_entry.go b/controllers/warp/dogu_entry.go new file mode 100644 index 0000000..e23b579 --- /dev/null +++ b/controllers/warp/dogu_entry.go @@ -0,0 +1,108 @@ +package warp + +import ( + "encoding/json" + "github.com/cloudogu/cesapp-lib/registry" + coreosclient "github.com/coreos/etcd/client" + "strings" + + "github.com/pkg/errors" +) + +type doguEntry struct { + Name string + DisplayName string + Description string + Category string + Tags []string +} + +func readAndUnmarshalDogu(registry registry.WatchConfigurationContext, key string, tag string) (EntryWithCategory, error) { + doguBytes, err := readDoguAsBytes(registry, key) + if err != nil { + return EntryWithCategory{}, err + } + + doguEntry, err := unmarshalDogu(doguBytes) + if err != nil { + return EntryWithCategory{}, err + } + + if tag == "" || containsString(doguEntry.Tags, tag) { + return mapDoguEntry(doguEntry) + } + + return EntryWithCategory{}, nil +} + +func readDoguAsBytes(registry registry.WatchConfigurationContext, key string) ([]byte, error) { + resp, err := registry.Get(key + "/current") + if err != nil { + // the dogu seems to be unregistered + if isKeyNotFound(err) { + return nil, nil + } + return nil, errors.Wrapf(err, "failed to read key %s from etcd", key) + } + + version := resp + resp, err = registry.Get(key + "/" + version) + if err != nil { + return nil, errors.Wrapf(err, "failed to read version child from key %s", key) + } + + return []byte(resp), nil +} + +func unmarshalDogu(doguBytes []byte) (doguEntry, error) { + doguEntry := doguEntry{} + err := json.Unmarshal(doguBytes, &doguEntry) + if err != nil { + return doguEntry, errors.Wrap(err, "failed to unmarshall json from etcd") + } + return doguEntry, nil +} + +func mapDoguEntry(entry doguEntry) (EntryWithCategory, error) { + if entry.Name == "" { + return EntryWithCategory{}, errors.New("name is required for dogu entries") + } + + displayName := entry.DisplayName + if displayName == "" { + displayName = entry.Name + } + + return EntryWithCategory{ + Entry: Entry{ + DisplayName: displayName, + Title: entry.Description, + Target: TARGET_SELF, + Href: createDoguHref(entry.Name), + }, + Category: entry.Category, + }, nil +} + +func createDoguHref(name string) string { + // remove namespace + parts := strings.Split(name, "/") + return "/" + parts[len(parts)-1] +} + +// ContainsString returns true if the slice contains the item +func containsString(slice []string, item string) bool { + for _, sliceItem := range slice { + if sliceItem == item { + return true + } + } + return false +} + +func isKeyNotFound(err error) bool { + if cErr, ok := err.(coreosclient.Error); ok { + return cErr.Code == coreosclient.ErrorCodeKeyNotFound + } + return false +} diff --git a/controllers/warp/external_entry.go b/controllers/warp/external_entry.go new file mode 100644 index 0000000..dda452b --- /dev/null +++ b/controllers/warp/external_entry.go @@ -0,0 +1,70 @@ +package warp + +import ( + "encoding/json" + "github.com/cloudogu/cesapp-lib/registry" + + "github.com/pkg/errors" +) + +type externalEntry struct { + DisplayName string + URL string + Description string + Category string +} + +// EntryWithCategory is a dto for entries with a Category +type EntryWithCategory struct { + Entry Entry + Category string +} + +func readAndUnmarshalExternal(registry registry.WatchConfigurationContext, key string) (EntryWithCategory, error) { + externalBytes, err := readExternalAsBytes(registry, key) + if err != nil { + return EntryWithCategory{}, nil + } + + return unmarshalExternal(externalBytes) +} + +func readExternalAsBytes(registry registry.WatchConfigurationContext, key string) ([]byte, error) { + resp, err := registry.Get(key) + if err != nil { + return nil, errors.Wrapf(err, "failed to read key %s from etcd", key) + } + + return []byte(resp), nil +} + +func unmarshalExternal(externalBytes []byte) (EntryWithCategory, error) { + externalEntry := externalEntry{} + err := json.Unmarshal(externalBytes, &externalEntry) + if err != nil { + return EntryWithCategory{}, errors.Wrap(err, "failed to unmarshall external") + } + + return mapExternalEntry(externalEntry) +} + +func mapExternalEntry(entry externalEntry) (EntryWithCategory, error) { + if entry.DisplayName == "" { + return EntryWithCategory{}, errors.New("could not find DisplayName on external entry") + } + if entry.URL == "" { + return EntryWithCategory{}, errors.New("could not find URL on external entry") + } + if entry.Category == "" { + return EntryWithCategory{}, errors.New("could not find Category on external entry") + } + return EntryWithCategory{ + Entry: Entry{ + DisplayName: entry.DisplayName, + Title: entry.Description, + Href: entry.URL, + Target: TARGET_EXTERNAL, + }, + Category: entry.Category, + }, nil +} diff --git a/controllers/warp/link_entry.go b/controllers/warp/link_entry.go new file mode 100644 index 0000000..618954c --- /dev/null +++ b/controllers/warp/link_entry.go @@ -0,0 +1,51 @@ +package warp + +import "github.com/pkg/errors" + +// Entry link in the warp menu +type Entry struct { + DisplayName string + Href string + Title string + Target Target +} + +// Target defines the target of the link +type Target uint8 + +const ( + // TARGET_SELF means the link is part of the internal system + TARGET_SELF Target = iota + 1 + // TARGET_EXTERNAL link is outside from the system + TARGET_EXTERNAL +) + +func (target Target) MarshalJSON() ([]byte, error) { + switch target { + case TARGET_SELF: + return target.asJSONString("self"), nil + case TARGET_EXTERNAL: + return target.asJSONString("external"), nil + default: + return nil, errors.Errorf("unknow target type %d", target) + } +} + +func (target Target) asJSONString(value string) []byte { + return []byte("\"" + value + "\"") +} + +// Entries is a collection of warp entries +type Entries []Entry + +func (e Entries) Len() int { + return len(e) +} + +func (e Entries) Less(i, j int) bool { + return e[i].DisplayName < e[j].DisplayName +} + +func (e Entries) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go new file mode 100644 index 0000000..70ffd1d --- /dev/null +++ b/controllers/warp/warp.go @@ -0,0 +1,101 @@ +package warp + +import ( + "context" + "fmt" + "github.com/cloudogu/cesapp-lib/registry" + coreosclient "github.com/coreos/etcd/client" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/json" + "log" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// Order can be used to modify ordering via configuration +type Order map[string]int + +// Configuration for warp menu creation +type Configuration struct { + Sources []Source + Target string + Order Order + Support []SupportSource +} + +// Source in etcd +type Source struct { + Path string + Type string + Tag string +} + +// SupportSource for SupportEntries from yaml +type SupportSource struct { + Identifier string + External bool + Href string +} + +// Run creates the warp menu and update the menu whenever a relevant etcd key was changed +func Run(configuration *Configuration, registry registry.WatchConfigurationContext, k8sClient client.Client) { + log.Println("start watcher for warp entries") + warpChannel := make(chan *coreosclient.Response) + for _, source := range configuration.Sources { + go func(source Source) { + for { + execute(configuration, registry, k8sClient) + registry.Watch(source.Path, true, warpChannel) + } + }(source) + } + for range warpChannel { + execute(configuration, registry, k8sClient) + } +} + +func execute(configuration *Configuration, registry registry.WatchConfigurationContext, k8sClient client.Client) { + reader := ConfigReader{ + registry: registry, + configuration: configuration, + } + categories, err := reader.readFromConfig(configuration) + if err != nil { + log.Println("Error during read:", err) + return + } + log.Printf("all found Categories: %v", categories) + err = jsonWriter(categories, k8sClient) + if err != nil { + log.Printf("failed to write warp menu as json: %v", err) + } +} + +// JSONWriter converts the data to a json +func jsonWriter(data interface{}, client client.Client) error { + configmap, err := getMenuConfigMap(client) + if err != nil { + return fmt.Errorf("failed to get menu json config map: %w", err) + } + + jsonData, err := json.Marshal(data) + if err != nil { + return fmt.Errorf("failed to marshal warp data: %w", err) + } + + configmap.Data["menu.json"] = string(jsonData) + + err = client.Update(context.TODO(), configmap) + if err != nil { + return fmt.Errorf("failed to update menu json config map: %w", err) + } + + return nil +} + +func getMenuConfigMap(k8sClient client.Client) (*corev1.ConfigMap, error) { + configmap := &corev1.ConfigMap{} + objectKey := client.ObjectKey{Name: "menu-json", Namespace: "ecosystem"} + err := k8sClient.Get(context.Background(), objectKey, configmap) + + return configmap, err +} diff --git a/controllers/warp/warp_test.go b/controllers/warp/warp_test.go new file mode 100644 index 0000000..7fec3b4 --- /dev/null +++ b/controllers/warp/warp_test.go @@ -0,0 +1,44 @@ +package warp_test + +import ( + "encoding/json" + "github.com/cloudogu/k8s-service-discovery/controllers/warp" + "testing" + + "fmt" + "github.com/stretchr/testify/assert" +) + +func TestCategoryString(t *testing.T) { + category := warp.Category{Title: "Hitchhiker"} + assert.Equal(t, "Hitchhiker", fmt.Sprintf("%v", category)) +} + +func TestTarget_MarshalJSON(t *testing.T) { + testMarshalJSON(t, warp.TARGET_EXTERNAL, "{\"Target\":\"external\"}") + testMarshalJSON(t, warp.TARGET_SELF, "{\"Target\":\"self\"}") + + if _, err := json.Marshal(&targetStruct{12}); err == nil { + t.Errorf("marshal should fail because of an invalid value") + } +} + +func testMarshalJSON(t *testing.T, target warp.Target, expected string) { + value := marshal(t, target) + if value != expected { + t.Errorf("value %s is not the expected %s", value, expected) + } +} + +func marshal(t *testing.T, target warp.Target) string { + test := targetStruct{target} + json, err := json.Marshal(&test) + if err != nil { + t.Errorf("failed to marshal test struct: %v", err) + } + return string(json) +} + +type targetStruct struct { + Target warp.Target +} diff --git a/controllers/warp_menu_creator.go b/controllers/warp_menu_creator.go new file mode 100644 index 0000000..a615096 --- /dev/null +++ b/controllers/warp_menu_creator.go @@ -0,0 +1,134 @@ +package controllers + +import ( + "context" + "fmt" + "github.com/cloudogu/cesapp-lib/core" + "github.com/cloudogu/cesapp-lib/registry" + "github.com/cloudogu/k8s-service-discovery/controllers/warp" + "github.com/sirupsen/logrus" + "io/ioutil" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "os" + "sigs.k8s.io/yaml" + "sync" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const warpConfig = "k8s-ces-warp-config" +const EnvVarStage = "STAGE" +const StageDevelopment = "development" +const DevConfigPath = "k8s/dev-resources/k8s-ces-warp-config.yaml" + +// Configuration main configuration object +type Configuration struct { + Endpoint string + Warp warp.Configuration +} + +// WarpMenuCreator +type WarpMenuCreator struct { + Client client.Client `json:"client"` + Namespace string `json:"namespace"` + Endpoint string `json:"endpoint"` + Configuration *Configuration `json:"configuration"` +} + +// NewWarpMenuCreator +func NewWarpMenuCreator(client client.Client, namespace string) WarpMenuCreator { + endpoint := fmt.Sprintf("http://etcd.%s.svc.cluster.local:4001", namespace) + return WarpMenuCreator{ + Client: client, + Namespace: namespace, + Endpoint: endpoint, + } +} + +// Start starts the runnable. +func (wmc WarpMenuCreator) Start(ctx context.Context) error { + return wmc.CreateWarpMenu(ctx) +} + +// CreateWarpMenu reads the warp configuration and starts watchers to refresh the menu.json configmap +// in background. +func (wmc WarpMenuCreator) CreateWarpMenu(ctx context.Context) error { + config, err := wmc.ReadConfiguration(ctx, wmc.Client) + if err != nil { + return fmt.Errorf("failed to read configuration: %w", err) + } + + cesRegistry, err := wmc.createEtcdRegistry() + if err != nil { + return fmt.Errorf("failed to create etcd registry: %w", err) + } + + var syncWaitGroup sync.WaitGroup + syncWaitGroup.Add(1) + go func() { + warp.Run(config, cesRegistry.RootConfig(), wmc.Client) + syncWaitGroup.Done() + }() + syncWaitGroup.Wait() + + return nil +} + +func (wmc *WarpMenuCreator) ReadConfiguration(ctx context.Context, client client.Client) (*warp.Configuration, error) { + if os.Getenv(EnvVarStage) == StageDevelopment { + return wmc.ReadWarpConfigFromFile(DevConfigPath) + } + return wmc.ReadWarpConfigFromCluster(ctx, client) +} + +func (wmc *WarpMenuCreator) ReadWarpConfigFromCluster(ctx context.Context, client client.Client) (*warp.Configuration, error) { + configmap := &corev1.ConfigMap{} + objectKey := types.NamespacedName{ + Namespace: wmc.Namespace, + Name: warpConfig, + } + err := client.Get(ctx, objectKey, configmap) + if err != nil { + return nil, fmt.Errorf("failed to get warp menu configmap: %w", err) + } + + confStr := configmap.Data["warp"] + conf := &warp.Configuration{} + err = yaml.Unmarshal([]byte(confStr), conf) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal yaml from warp config: %w", err) + } + + return conf, nil +} + +func (wmc *WarpMenuCreator) createEtcdRegistry() (registry.Registry, error) { + r, err := registry.New(core.Registry{ + Type: "etcd", + Endpoints: []string{wmc.Endpoint}, + }) + + return r, err +} + +func (wmc *WarpMenuCreator) ReadWarpConfigFromFile(path string) (*warp.Configuration, error) { + config := &warp.Configuration{} + if _, err := os.Stat(path); os.IsNotExist(err) { + return config, fmt.Errorf("could not find configuration at %s", path) + } + + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("failed to read configuration %s: %w", path, err) + } + + err = yaml.Unmarshal(data, config) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal configuration %s: %w", path, err) + } + + logrus.Info(fmt.Sprintf("%v", config)) + + return config, nil +} diff --git a/go.mod b/go.mod index d6fe42d..8b16429 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,15 @@ module github.com/cloudogu/k8s-service-discovery go 1.17 require ( + github.com/cloudogu/cesapp-lib v0.0.0-20220531123943-5588ca2b40da + github.com/coreos/etcd v3.3.27+incompatible github.com/go-logr/logr v1.2.0 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.17.0 - github.com/stretchr/testify v1.7.0 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.7.1 + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 k8s.io/api v0.23.5 k8s.io/apimachinery v0.23.5 k8s.io/client-go v0.23.5 @@ -24,8 +29,11 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/coreos/go-semver v0.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/eapache/go-resiliency v1.2.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/zapr v1.2.0 // indirect @@ -36,28 +44,31 @@ require ( github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/googleapis/gnostic v0.5.5 // indirect + github.com/gosuri/uitable v0.0.4 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-isatty v0.0.14 // 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 github.com/nxadm/tail v1.4.8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.11.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.28.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.1.1 // indirect + github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect - golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 // indirect - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // 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-20210723032227-1f47c861a9ac // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect diff --git a/go.sum b/go.sum index bfaa028..fb717db 100644 --- a/go.sum +++ b/go.sum @@ -48,7 +48,6 @@ github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZ github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= 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 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 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= @@ -73,7 +72,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -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= @@ -86,7 +84,6 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -94,6 +91,10 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR 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/cloudogu/cesapp-lib v0.0.0-20220531111042-b618c3168653 h1:abFA0NQVXjrxvhD9LUCSAJB26vpc514bKn62yIy9pZ4= +github.com/cloudogu/cesapp-lib v0.0.0-20220531111042-b618c3168653/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= +github.com/cloudogu/cesapp-lib v0.0.0-20220531123943-5588ca2b40da h1:f2bEErsAKvI8ShdFdCYPk6SIFcvegeZfBOkDugc1Nzs= +github.com/cloudogu/cesapp-lib v0.0.0-20220531123943-5588ca2b40da/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= 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= @@ -103,7 +104,10 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA= +github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -118,6 +122,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -133,6 +139,8 @@ github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMi github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= @@ -252,6 +260,8 @@ github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97Dwqy github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -312,7 +322,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -320,7 +329,14 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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= @@ -348,7 +364,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -405,6 +420,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +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= @@ -416,6 +433,7 @@ 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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -438,15 +456,17 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26 h1:aJo09LP94Ty48u8vDMEox1NHwZ72vgRCw7PyVXc9E6k= +github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -490,8 +510,6 @@ 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-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -593,8 +611,9 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT 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-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -646,6 +665,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200116001909-b77594299b42/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= @@ -685,11 +705,12 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8 h1:M69LAlWZCshgp0QSzyDcSsSIejIEeuaCVpmwcKwyLMk= -golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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= @@ -768,7 +789,6 @@ golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpd golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= @@ -887,7 +907,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -959,7 +978,6 @@ sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtud sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/k8s/dev-resources/k8s-ces-warp-config.yaml b/k8s/dev-resources/k8s-ces-warp-config.yaml new file mode 100644 index 0000000..2d1db48 --- /dev/null +++ b/k8s/dev-resources/k8s-ces-warp-config.yaml @@ -0,0 +1,19 @@ +sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externalsss + type: externals +target: /var/www/html/warp/menu.json +order: + Development Apps: 100 +support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/k8s/k8s-ces-warp-config.yaml b/k8s/k8s-ces-warp-config.yaml new file mode 100644 index 0000000..547b645 --- /dev/null +++ b/k8s/k8s-ces-warp-config.yaml @@ -0,0 +1,30 @@ +# +# The default configuration map for the warp menu. +# +apiVersion: v1 +kind: ConfigMap +metadata: + name: k8s-ces-warp-config + labels: + app: cloudogu-ecosystem +data: + warp: | + sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externalsssssssss + type: externals + target: /var/www/html/warp/menu.json + order: + Development Apps: 100 + support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/main.go b/main.go index a6bb2b1..0b71cd8 100644 --- a/main.go +++ b/main.go @@ -73,6 +73,10 @@ func startManager() error { return fmt.Errorf("failed to create ingress class creator: %w", err) } + if err = handleWarpMenuCreation(k8sManager, options.Namespace); err != nil { + return fmt.Errorf("failed to create warp menu creator: %w", err) + } + if err := configureManager(k8sManager, options.Namespace); err != nil { return fmt.Errorf("failed to configure service discovery manager: %w", err) } @@ -173,6 +177,16 @@ func handleIngressClassCreation(k8sManager manager.Manager) error { return nil } +func handleWarpMenuCreation(k8sManager manager.Manager, namespace string) error { + warpMenuCreator := controllers.NewWarpMenuCreator(k8sManager.GetClient(), namespace) + + if err := k8sManager.Add(warpMenuCreator); err != nil { + return fmt.Errorf("failed to add warp menu creator as runnable to the manager: %w", err) + } + + return nil +} + func configureReconciler(k8sManager manager.Manager, namespace string) error { ingressGenerator := controllers.NewIngressGenerator(k8sManager.GetClient(), namespace, IngressClassName) From eaf0d46de8410bf752097a630369a2ac060e1216 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 1 Jun 2022 14:20:15 +0200 Subject: [PATCH 05/19] #3 Refactor warp --- controllers/config/config.go | 94 ++ controllers/ingress_class_creator.go | 2 +- controllers/ingress_class_creator_test.go | 2 +- controllers/warp/configReader.go | 39 +- controllers/warp/configReader_test.go | 15 +- controllers/warp/dogu_entry.go | 7 +- controllers/warp/external_entry.go | 5 +- controllers/warp/warp.go | 99 ++- controllers/warp_menu_creator.go | 112 +-- go.mod | 2 +- go.sum | 985 --------------------- k8s/dev-resources/k8s-ces-warp-config.yaml | 4 +- k8s/k8s-ces-warp-config.yaml | 4 +- 13 files changed, 206 insertions(+), 1164 deletions(-) create mode 100644 controllers/config/config.go delete mode 100644 go.sum diff --git a/controllers/config/config.go b/controllers/config/config.go new file mode 100644 index 0000000..4336e60 --- /dev/null +++ b/controllers/config/config.go @@ -0,0 +1,94 @@ +package config + +import ( + "context" + "fmt" + "io/ioutil" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "os" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" +) + +const ( + warpConfigMap = "k8s-ces-warp-config" + MenuConfigMap = "k8s-ces-menu-json" + EnvVarStage = "STAGE" + StageDevelopment = "development" + DevConfigPath = "k8s/dev-resources/k8s-ces-warp-config.yaml" +) + +// Order can be used to modify ordering via configuration +type Order map[string]int + +// Configuration for warp menu creation +type Configuration struct { + Sources []Source + Target string + Order Order + Support []SupportSource +} + +// Source in etcd +type Source struct { + Path string + Type string + Tag string +} + +// SupportSource for SupportEntries from yaml +type SupportSource struct { + Identifier string + External bool + Href string +} + +// ReadConfiguration reads the service discovery configuration. Either from file in development mode with environment +// variable stage=development or from the cluster state +func ReadConfiguration(ctx context.Context, client client.Client, namespace string) (*Configuration, error) { + if os.Getenv(EnvVarStage) == StageDevelopment { + return readWarpConfigFromFile(DevConfigPath) + } + return readWarpConfigFromCluster(ctx, client, namespace) +} + +func readWarpConfigFromFile(path string) (*Configuration, error) { + config := &Configuration{} + if _, err := os.Stat(path); os.IsNotExist(err) { + return nil, fmt.Errorf("could not find configuration at %s", path) + } + + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("failed to read configuration %s: %w", path, err) + } + + err = yaml.Unmarshal(data, config) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal configuration %s: %w", path, err) + } + + return config, nil +} + +func readWarpConfigFromCluster(ctx context.Context, client client.Client, namespace string) (*Configuration, error) { + configmap := &corev1.ConfigMap{} + objectKey := types.NamespacedName{ + Namespace: namespace, + Name: warpConfigMap, + } + err := client.Get(ctx, objectKey, configmap) + if err != nil { + return nil, fmt.Errorf("failed to get warp menu configmap: %w", err) + } + + data := configmap.Data["warp"] + conf := &Configuration{} + err = yaml.Unmarshal([]byte(data), conf) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal yaml from warp config: %w", err) + } + + return conf, nil +} diff --git a/controllers/ingress_class_creator.go b/controllers/ingress_class_creator.go index 9e5c04b..72e9ad2 100644 --- a/controllers/ingress_class_creator.go +++ b/controllers/ingress_class_creator.go @@ -48,7 +48,7 @@ func (icc IngressClassCreator) CreateIngressClass(logger logr.Logger) error { Name: icc.ClassName, }, Spec: networking.IngressClassSpec{ - Controller: "k8s.io/ingress-nginx", + Controller: "k8s.io/nginx-ingress", }, } diff --git a/controllers/ingress_class_creator_test.go b/controllers/ingress_class_creator_test.go index 7901813..b097a5e 100644 --- a/controllers/ingress_class_creator_test.go +++ b/controllers/ingress_class_creator_test.go @@ -56,7 +56,7 @@ func TestIngressClassCreator_CreateIngressClass(t *testing.T) { Name: "myIngressClass", }, Spec: networking.IngressClassSpec{ - Controller: "k8s.io/ingress-nginx", + Controller: "k8s.io/nginx-ingress", }, } clientMock := testclient.NewClientBuilder().WithScheme(getScheme()).WithObjects(ingressClass).Build() diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go index 110e01d..a428dc6 100644 --- a/controllers/warp/configReader.go +++ b/controllers/warp/configReader.go @@ -2,7 +2,9 @@ package warp import ( "encoding/json" + "fmt" "github.com/cloudogu/cesapp-lib/registry" + "github.com/cloudogu/k8s-service-discovery/controllers/config" "log" "sort" @@ -12,7 +14,7 @@ import ( // ConfigReader reads the configuration for the warp menu from etcd type ConfigReader struct { - configuration *Configuration + configuration *config.Configuration registry registry.WatchConfigurationContext } @@ -50,18 +52,16 @@ func (reader *ConfigReader) createCategories(entries []EntryWithCategory) Catego // dogusReader reads from etcd and converts the keys and values to a warp menu // conform structure -func (reader *ConfigReader) dogusReader(source Source) (Categories, error) { +func (reader *ConfigReader) dogusReader(source config.Source) (Categories, error) { log.Printf("read dogus from %s for warp menu", source.Path) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { - return nil, errors.Wrapf(err, "failed to read root entry %s from etcd", source.Path) + return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) } dogus := []EntryWithCategory{} for _, child := range resp { dogu, err := readAndUnmarshalDogu(reader.registry, child, source.Tag) - if err != nil { - log.Printf("failed to read and unmarshal dogu: %v", err) - } else if dogu.Entry.Title != "" { + if err == nil && dogu.Entry.Title != "" { dogus = append(dogus, dogu) } } @@ -69,25 +69,23 @@ func (reader *ConfigReader) dogusReader(source Source) (Categories, error) { return reader.createCategories(dogus), nil } -func (reader *ConfigReader) externalsReader(source Source) (Categories, error) { +func (reader *ConfigReader) externalsReader(source config.Source) (Categories, error) { log.Printf("read externals from %s for warp menu", source.Path) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { - return nil, errors.Wrapf(err, "failed to read root entry %s from etcd", source.Path) + return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) } externals := []EntryWithCategory{} for _, child := range resp { external, err := readAndUnmarshalExternal(reader.registry, child) - if err != nil { - log.Printf("failed to read and unmarshal external: %v", err) - } else { + if err == nil { externals = append(externals, external) } } return reader.createCategories(externals), nil } -func (reader *ConfigReader) readSource(source Source) (Categories, error) { +func (reader *ConfigReader) readSource(source config.Source) (Categories, error) { switch source.Type { case "dogus": return reader.dogusReader(source) @@ -100,19 +98,19 @@ func (reader *ConfigReader) readSource(source Source) (Categories, error) { func (reader *ConfigReader) getDisabledSupportIdentifiers() ([]string, error) { disabledSupportEntries, err := reader.registry.Get(disableWarpSupportEntriesConfigurationKey) if err != nil { - return []string{}, errors.Wrapf(err, "failed to read configuration entry %s from etcd", disableWarpSupportEntriesConfigurationKey) + return []string{}, fmt.Errorf("failed to read configuration entry %s from etcd: %w", disableWarpSupportEntriesConfigurationKey, err) } var disabledEntries []string err = json.Unmarshal([]byte(disabledSupportEntries), &disabledEntries) if err != nil { - return []string{}, errors.Wrapf(err, "failed to unmarshal etcd key") + return []string{}, fmt.Errorf("failed to unmarshal etcd key: %w", err) } return disabledEntries, nil } -func (reader *ConfigReader) readSupport(supportSources []SupportSource, disabledSupportEntries []string) (Categories, error) { +func (reader *ConfigReader) readSupport(supportSources []config.SupportSource, disabledSupportEntries []string) (Categories, error) { var supportEntries []EntryWithCategory for _, supportSource := range supportSources { @@ -132,10 +130,15 @@ func (reader *ConfigReader) readSupport(supportSources []SupportSource, disabled return reader.createCategories(supportEntries), nil } -func (reader *ConfigReader) readFromConfig(configuration *Configuration) (Categories, error) { +func (reader *ConfigReader) readFromConfig(configuration *config.Configuration) (Categories, error) { var data Categories for _, source := range configuration.Sources { + // Disabled support entries refresh every time + if source.Type == "disabled_support_entries" { + continue + } + categories, err := reader.readSource(source) if err != nil { log.Println("Error during read:", err) @@ -145,10 +148,6 @@ func (reader *ConfigReader) readFromConfig(configuration *Configuration) (Catego log.Println("read SupportEntries") disabledSupportEntries, err := reader.getDisabledSupportIdentifiers() - if err != nil { - log.Printf("Warning, could not read etcd Key: %v. Err: %v", disableWarpSupportEntriesConfigurationKey, err) - } - // add support Category supportCategory, err := reader.readSupport(configuration.Support, disabledSupportEntries) if err != nil { log.Println("Error during support read:", err) diff --git a/controllers/warp/configReader_test.go b/controllers/warp/configReader_test.go index 4fb71cc..db01a45 100644 --- a/controllers/warp/configReader_test.go +++ b/controllers/warp/configReader_test.go @@ -2,17 +2,18 @@ package warp import ( "github.com/cloudogu/cesapp-lib/registry/mocks" + "github.com/cloudogu/k8s-service-discovery/controllers/config" "github.com/stretchr/testify/assert" "testing" ) func TestConfigReader_readSupport(t *testing.T) { reader := &ConfigReader{ - configuration: &Configuration{Support: []SupportSource{}}, + configuration: &config.Configuration{Support: []config.SupportSource{}}, registry: nil, } - supportSources := []SupportSource{{Identifier: "aboutCloudoguToken", External: false, Href: "/local/href"}, {Identifier: "myCloudogu", External: true, Href: "https://ecosystem.cloudogu.com/"}, {Identifier: "docsCloudoguComUrl", External: true, Href: "https://docs.cloudogu.com/"}} + supportSources := []config.SupportSource{{Identifier: "aboutCloudoguToken", External: false, Href: "/local/href"}, {Identifier: "myCloudogu", External: true, Href: "https://ecosystem.cloudogu.com/"}, {Identifier: "docsCloudoguComUrl", External: true, Href: "https://docs.cloudogu.com/"}} actual, err := reader.readSupport(supportSources, []string{"docsCloudoguComUrl"}) @@ -47,7 +48,7 @@ func TestConfigReader_getDisabledSupportIdentifiers(t *testing.T) { mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) reader := &ConfigReader{ - configuration: &Configuration{Support: []SupportSource{}}, + configuration: &config.Configuration{Support: []config.SupportSource{}}, registry: mockRegistry, } @@ -63,14 +64,14 @@ func TestConfigReader_readFromConfig(t *testing.T) { mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) reader := &ConfigReader{ - configuration: &Configuration{Support: []SupportSource{}}, + configuration: &config.Configuration{Support: []config.SupportSource{}}, registry: mockRegistry, } - testSources := []Source{{Path: "/path/to/etcd/key", Type: "externals", Tag: "tag"}} - testSupportSoureces := []SupportSource{{Identifier: "supportSrc", External: true, Href: "path/to/external"}} + testSources := []config.Source{{Path: "/path/to/etcd/key", Type: "externals", Tag: "tag"}} + testSupportSoureces := []config.SupportSource{{Identifier: "supportSrc", External: true, Href: "path/to/external"}} - actual, err := reader.readFromConfig(&Configuration{Sources: testSources, Support: testSupportSoureces}) + actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) assert.Empty(t, err) assert.NotEmpty(t, actual) diff --git a/controllers/warp/dogu_entry.go b/controllers/warp/dogu_entry.go index e23b579..00a0932 100644 --- a/controllers/warp/dogu_entry.go +++ b/controllers/warp/dogu_entry.go @@ -2,6 +2,7 @@ package warp import ( "encoding/json" + "fmt" "github.com/cloudogu/cesapp-lib/registry" coreosclient "github.com/coreos/etcd/client" "strings" @@ -42,13 +43,13 @@ func readDoguAsBytes(registry registry.WatchConfigurationContext, key string) ([ if isKeyNotFound(err) { return nil, nil } - return nil, errors.Wrapf(err, "failed to read key %s from etcd", key) + return nil, fmt.Errorf("failed to read key %s from etcd: %w", key, err) } version := resp resp, err = registry.Get(key + "/" + version) if err != nil { - return nil, errors.Wrapf(err, "failed to read version child from key %s", key) + return nil, fmt.Errorf("failed to read version child from key %s: %w", key, err) } return []byte(resp), nil @@ -58,7 +59,7 @@ func unmarshalDogu(doguBytes []byte) (doguEntry, error) { doguEntry := doguEntry{} err := json.Unmarshal(doguBytes, &doguEntry) if err != nil { - return doguEntry, errors.Wrap(err, "failed to unmarshall json from etcd") + return doguEntry, fmt.Errorf("failed to unmarshall json from etcd: %w", err) } return doguEntry, nil } diff --git a/controllers/warp/external_entry.go b/controllers/warp/external_entry.go index dda452b..1e04976 100644 --- a/controllers/warp/external_entry.go +++ b/controllers/warp/external_entry.go @@ -2,6 +2,7 @@ package warp import ( "encoding/json" + "fmt" "github.com/cloudogu/cesapp-lib/registry" "github.com/pkg/errors" @@ -32,7 +33,7 @@ func readAndUnmarshalExternal(registry registry.WatchConfigurationContext, key s func readExternalAsBytes(registry registry.WatchConfigurationContext, key string) ([]byte, error) { resp, err := registry.Get(key) if err != nil { - return nil, errors.Wrapf(err, "failed to read key %s from etcd", key) + return nil, fmt.Errorf("failed to read key %s from etcd: %w", key, err) } return []byte(resp), nil @@ -42,7 +43,7 @@ func unmarshalExternal(externalBytes []byte) (EntryWithCategory, error) { externalEntry := externalEntry{} err := json.Unmarshal(externalBytes, &externalEntry) if err != nil { - return EntryWithCategory{}, errors.Wrap(err, "failed to unmarshall external") + return EntryWithCategory{}, fmt.Errorf("failed to unmarshall external: %w", err) } return mapExternalEntry(externalEntry) diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go index 70ffd1d..c4a9dcc 100644 --- a/controllers/warp/warp.go +++ b/controllers/warp/warp.go @@ -3,7 +3,9 @@ package warp import ( "context" "fmt" + "github.com/cloudogu/cesapp-lib/core" "github.com/cloudogu/cesapp-lib/registry" + "github.com/cloudogu/k8s-service-discovery/controllers/config" coreosclient "github.com/coreos/etcd/client" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/json" @@ -11,68 +13,87 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -// Order can be used to modify ordering via configuration -type Order map[string]int - -// Configuration for warp menu creation -type Configuration struct { - Sources []Source - Target string - Order Order - Support []SupportSource +// Watcher is used to watch a registry and for every change he reads from the registry a specific config path, +// build warp menu categories and writes them to a configmap. +type Watcher struct { + configuration *config.Configuration + registryToWatch registry.WatchConfigurationContext + k8sClient client.Client + configReader *ConfigReader + namespace string + doneChannel <-chan struct{} } -// Source in etcd -type Source struct { - Path string - Type string - Tag string +// NewWatcher creates a new Watcher instance to build the warp menu +func NewWatcher(ctx context.Context, k8sClient client.Client, namespace string) (*Watcher, error) { + warpConfig, err := config.ReadConfiguration(ctx, k8sClient, namespace) + if err != nil { + return nil, fmt.Errorf("failed to read configuration: %w", err) + } + + cesRegistry, err := createEtcdRegistry(namespace) + if err != nil { + return nil, fmt.Errorf("failed to create etcd registry: %w", err) + } + + reader := &ConfigReader{ + registry: cesRegistry.RootConfig(), + configuration: warpConfig, + } + + return &Watcher{ + configuration: warpConfig, + registryToWatch: cesRegistry.RootConfig(), + k8sClient: k8sClient, + namespace: namespace, + configReader: reader, + doneChannel: ctx.Done(), + }, nil } -// SupportSource for SupportEntries from yaml -type SupportSource struct { - Identifier string - External bool - Href string +func createEtcdRegistry(namespace string) (registry.Registry, error) { + r, err := registry.New(core.Registry{ + Type: "etcd", + Endpoints: []string{fmt.Sprintf("http://etcd.%s.svc.cluster.local:4001", namespace)}, + }) + + return r, err } // Run creates the warp menu and update the menu whenever a relevant etcd key was changed -func Run(configuration *Configuration, registry registry.WatchConfigurationContext, k8sClient client.Client) { +func (w *Watcher) Run() { log.Println("start watcher for warp entries") warpChannel := make(chan *coreosclient.Response) - for _, source := range configuration.Sources { - go func(source Source) { + + for _, source := range w.configuration.Sources { + go func(source config.Source) { for { - execute(configuration, registry, k8sClient) - registry.Watch(source.Path, true, warpChannel) + w.execute() + w.registryToWatch.Watch(source.Path, true, warpChannel, w.doneChannel) } }(source) } + for range warpChannel { - execute(configuration, registry, k8sClient) + w.execute() } } -func execute(configuration *Configuration, registry registry.WatchConfigurationContext, k8sClient client.Client) { - reader := ConfigReader{ - registry: registry, - configuration: configuration, - } - categories, err := reader.readFromConfig(configuration) +func (w *Watcher) execute() { + categories, err := w.configReader.readFromConfig(w.configuration) if err != nil { log.Println("Error during read:", err) return } log.Printf("all found Categories: %v", categories) - err = jsonWriter(categories, k8sClient) + err = w.jsonWriter(categories) if err != nil { log.Printf("failed to write warp menu as json: %v", err) } } -// JSONWriter converts the data to a json -func jsonWriter(data interface{}, client client.Client) error { - configmap, err := getMenuConfigMap(client) +func (w *Watcher) jsonWriter(data interface{}) error { + configmap, err := w.getMenuConfigMap() if err != nil { return fmt.Errorf("failed to get menu json config map: %w", err) } @@ -84,7 +105,7 @@ func jsonWriter(data interface{}, client client.Client) error { configmap.Data["menu.json"] = string(jsonData) - err = client.Update(context.TODO(), configmap) + err = w.k8sClient.Update(context.TODO(), configmap) if err != nil { return fmt.Errorf("failed to update menu json config map: %w", err) } @@ -92,10 +113,10 @@ func jsonWriter(data interface{}, client client.Client) error { return nil } -func getMenuConfigMap(k8sClient client.Client) (*corev1.ConfigMap, error) { +func (w *Watcher) getMenuConfigMap() (*corev1.ConfigMap, error) { configmap := &corev1.ConfigMap{} - objectKey := client.ObjectKey{Name: "menu-json", Namespace: "ecosystem"} - err := k8sClient.Get(context.Background(), objectKey, configmap) + objectKey := client.ObjectKey{Name: config.MenuConfigMap, Namespace: w.namespace} + err := w.k8sClient.Get(context.Background(), objectKey, configmap) return configmap, err } diff --git a/controllers/warp_menu_creator.go b/controllers/warp_menu_creator.go index a615096..76da86a 100644 --- a/controllers/warp_menu_creator.go +++ b/controllers/warp_menu_creator.go @@ -3,46 +3,21 @@ package controllers import ( "context" "fmt" - "github.com/cloudogu/cesapp-lib/core" - "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/warp" - "github.com/sirupsen/logrus" - "io/ioutil" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "os" - "sigs.k8s.io/yaml" - "sync" - "sigs.k8s.io/controller-runtime/pkg/client" ) -const warpConfig = "k8s-ces-warp-config" -const EnvVarStage = "STAGE" -const StageDevelopment = "development" -const DevConfigPath = "k8s/dev-resources/k8s-ces-warp-config.yaml" - -// Configuration main configuration object -type Configuration struct { - Endpoint string - Warp warp.Configuration -} - -// WarpMenuCreator +// WarpMenuCreator used to create warp menu type WarpMenuCreator struct { - Client client.Client `json:"client"` - Namespace string `json:"namespace"` - Endpoint string `json:"endpoint"` - Configuration *Configuration `json:"configuration"` + client client.Client + namespace string } -// NewWarpMenuCreator +// NewWarpMenuCreator initialises a creator object to start the warp menu creation func NewWarpMenuCreator(client client.Client, namespace string) WarpMenuCreator { - endpoint := fmt.Sprintf("http://etcd.%s.svc.cluster.local:4001", namespace) return WarpMenuCreator{ - Client: client, - Namespace: namespace, - Endpoint: endpoint, + client: client, + namespace: namespace, } } @@ -54,81 +29,12 @@ func (wmc WarpMenuCreator) Start(ctx context.Context) error { // CreateWarpMenu reads the warp configuration and starts watchers to refresh the menu.json configmap // in background. func (wmc WarpMenuCreator) CreateWarpMenu(ctx context.Context) error { - config, err := wmc.ReadConfiguration(ctx, wmc.Client) + warpWatcher, err := warp.NewWatcher(ctx, wmc.client, wmc.namespace) if err != nil { - return fmt.Errorf("failed to read configuration: %w", err) + return fmt.Errorf("failed to create warp menu watcher: %w", err) } - cesRegistry, err := wmc.createEtcdRegistry() - if err != nil { - return fmt.Errorf("failed to create etcd registry: %w", err) - } - - var syncWaitGroup sync.WaitGroup - syncWaitGroup.Add(1) - go func() { - warp.Run(config, cesRegistry.RootConfig(), wmc.Client) - syncWaitGroup.Done() - }() - syncWaitGroup.Wait() + warpWatcher.Run() return nil } - -func (wmc *WarpMenuCreator) ReadConfiguration(ctx context.Context, client client.Client) (*warp.Configuration, error) { - if os.Getenv(EnvVarStage) == StageDevelopment { - return wmc.ReadWarpConfigFromFile(DevConfigPath) - } - return wmc.ReadWarpConfigFromCluster(ctx, client) -} - -func (wmc *WarpMenuCreator) ReadWarpConfigFromCluster(ctx context.Context, client client.Client) (*warp.Configuration, error) { - configmap := &corev1.ConfigMap{} - objectKey := types.NamespacedName{ - Namespace: wmc.Namespace, - Name: warpConfig, - } - err := client.Get(ctx, objectKey, configmap) - if err != nil { - return nil, fmt.Errorf("failed to get warp menu configmap: %w", err) - } - - confStr := configmap.Data["warp"] - conf := &warp.Configuration{} - err = yaml.Unmarshal([]byte(confStr), conf) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal yaml from warp config: %w", err) - } - - return conf, nil -} - -func (wmc *WarpMenuCreator) createEtcdRegistry() (registry.Registry, error) { - r, err := registry.New(core.Registry{ - Type: "etcd", - Endpoints: []string{wmc.Endpoint}, - }) - - return r, err -} - -func (wmc *WarpMenuCreator) ReadWarpConfigFromFile(path string) (*warp.Configuration, error) { - config := &warp.Configuration{} - if _, err := os.Stat(path); os.IsNotExist(err) { - return config, fmt.Errorf("could not find configuration at %s", path) - } - - data, err := ioutil.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("failed to read configuration %s: %w", path, err) - } - - err = yaml.Unmarshal(data, config) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal configuration %s: %w", path, err) - } - - logrus.Info(fmt.Sprintf("%v", config)) - - return config, nil -} diff --git a/go.mod b/go.mod index 8b16429..735728e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cloudogu/k8s-service-discovery go 1.17 require ( - github.com/cloudogu/cesapp-lib v0.0.0-20220531123943-5588ca2b40da + github.com/cloudogu/cesapp-lib v0.0.0-20220601121439-08fb7d1213c0 github.com/coreos/etcd v3.3.27+incompatible github.com/go-logr/logr v1.2.0 github.com/onsi/ginkgo v1.16.5 diff --git a/go.sum b/go.sum deleted file mode 100644 index fb717db..0000000 --- a/go.sum +++ /dev/null @@ -1,985 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -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 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -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.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -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/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/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/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/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/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/cloudogu/cesapp-lib v0.0.0-20220531111042-b618c3168653 h1:abFA0NQVXjrxvhD9LUCSAJB26vpc514bKn62yIy9pZ4= -github.com/cloudogu/cesapp-lib v0.0.0-20220531111042-b618c3168653/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= -github.com/cloudogu/cesapp-lib v0.0.0-20220531123943-5588ca2b40da h1:f2bEErsAKvI8ShdFdCYPk6SIFcvegeZfBOkDugc1Nzs= -github.com/cloudogu/cesapp-lib v0.0.0-20220531123943-5588ca2b40da/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= -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/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA= -github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/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 v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -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 v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= -github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/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/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -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/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -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= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -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-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -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 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/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.28.0 h1:vGVfV9KrDTvWt5boZO0I19g2E3CsWfpPPKZM9dt3mEw= -github.com/prometheus/common v0.28.0/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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -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 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -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= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -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= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26 h1:aJo09LP94Ty48u8vDMEox1NHwZ72vgRCw7PyVXc9E6k= -github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -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/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -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-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-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-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-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-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-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-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -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-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= -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.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 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-20200423170343-7949de9c1215/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-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -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.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -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.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= -k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= -k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= -k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= -k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4= -k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= -k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= -k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4= -k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA= -k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= -k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= -k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE= -k8s.io/component-base v0.23.0 h1:UAnyzjvVZ2ZR1lF35YwtNY6VMN94WtOnArcXBu34es8= -k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/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/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I= -sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= -sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/k8s/dev-resources/k8s-ces-warp-config.yaml b/k8s/dev-resources/k8s-ces-warp-config.yaml index 2d1db48..20bd3ea 100644 --- a/k8s/dev-resources/k8s-ces-warp-config.yaml +++ b/k8s/dev-resources/k8s-ces-warp-config.yaml @@ -2,8 +2,10 @@ sources: - path: /dogu type: dogus tag: warp - - path: /config/nginx/externalsss + - path: /config/nginx/externals type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries target: /var/www/html/warp/menu.json order: Development Apps: 100 diff --git a/k8s/k8s-ces-warp-config.yaml b/k8s/k8s-ces-warp-config.yaml index 547b645..4804113 100644 --- a/k8s/k8s-ces-warp-config.yaml +++ b/k8s/k8s-ces-warp-config.yaml @@ -13,8 +13,10 @@ data: - path: /dogu type: dogus tag: warp - - path: /config/nginx/externalsssssssss + - path: /config/nginx/externals type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries target: /var/www/html/warp/menu.json order: Development Apps: 100 From 7166541a7300683af5df81a3780e04bed5f7c533 Mon Sep 17 00:00:00 2001 From: Joshua Sprey Date: Thu, 2 Jun 2022 10:29:56 +0200 Subject: [PATCH 06/19] #3 fix golangci-lint issues --- controllers/warp/configReader.go | 9 +- go.mod | 4 +- go.sum | 158 +++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 go.sum diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go index a428dc6..e89f95b 100644 --- a/controllers/warp/configReader.go +++ b/controllers/warp/configReader.go @@ -3,9 +3,10 @@ package warp import ( "encoding/json" "fmt" + "log" + "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/config" - "log" "sort" @@ -18,10 +19,6 @@ type ConfigReader struct { registry registry.WatchConfigurationContext } -type DisabledSupportEntries struct { - name []string -} - const disableWarpSupportEntriesConfigurationKey = "/config/_global/disabled_warpmenu_support_entries" func (reader *ConfigReader) createCategories(entries []EntryWithCategory) Categories { @@ -116,7 +113,7 @@ func (reader *ConfigReader) readSupport(supportSources []config.SupportSource, d for _, supportSource := range supportSources { // supportSource -> EntryWithCategory if !StringInSlice(supportSource.Identifier, disabledSupportEntries) { - entry := Entry{} + var entry Entry if supportSource.External { entry = Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: TARGET_EXTERNAL} } else { diff --git a/go.mod b/go.mod index 735728e..03d5a7f 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,7 @@ require ( github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.17.0 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.1 - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 k8s.io/api v0.23.5 k8s.io/apimachinery v0.23.5 k8s.io/client-go v0.23.5 @@ -60,12 +58,14 @@ require ( github.com/prometheus/common v0.28.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..508c92d --- /dev/null +++ b/go.sum @@ -0,0 +1,158 @@ +cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +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.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +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/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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudogu/cesapp-lib v0.0.0-20220601121439-08fb7d1213c0 h1:F9rCf/1jw+o39lL6iv4zPoAkXaYdmUwVNb29PlsR9IM= +github.com/cloudogu/cesapp-lib v0.0.0-20220601121439-08fb7d1213c0/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= +github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA= +github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +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/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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.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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +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 v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +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.28.0 h1:vGVfV9KrDTvWt5boZO0I19g2E3CsWfpPPKZM9dt3mEw= +github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +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/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26 h1:aJo09LP94Ty48u8vDMEox1NHwZ72vgRCw7PyVXc9E6k= +github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +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.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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.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-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= +k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= +k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= +k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4= +k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= +k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= +k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= +k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= +k8s.io/component-base v0.23.0 h1:UAnyzjvVZ2ZR1lF35YwtNY6VMN94WtOnArcXBu34es8= +k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE= +k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= +sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From b0aaf743d94ffa19aa9bbb2716efb72466358d83 Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 2 Jun 2022 14:34:24 +0200 Subject: [PATCH 07/19] #3 Use interfaces in configReader.go. Add unit tests --- controllers/config/config_test.go | 142 +++++++ controllers/config/testdata/config.yaml | 21 ++ .../config/testdata/invalid_config.yaml | 22 ++ .../config/testdata/invalid_k8s_config.yaml | 36 ++ controllers/config/testdata/k8s_config.yaml | 33 ++ controllers/warp/configReader.go | 164 ++++---- controllers/warp/configReader_test.go | 351 +++++++++++++++--- controllers/warp/{ => types}/category.go | 10 +- controllers/warp/types/category_test.go | 129 +++++++ controllers/warp/{ => types}/dogu_entry.go | 17 +- controllers/warp/types/dogu_entry_test.go | 348 +++++++++++++++++ .../warp/{ => types}/external_entry.go | 6 +- controllers/warp/types/external_entry_test.go | 156 ++++++++ controllers/warp/{ => types}/link_entry.go | 2 +- controllers/warp/types/link_entry_test.go | 82 ++++ controllers/warp/types/mocks/DoguConverter.go | 35 ++ .../warp/types/mocks/ExternalConverter.go | 35 ++ controllers/warp/warp.go | 16 +- controllers/warp/warp_test.go | 43 --- controllers/warp_menu_creator_test.go | 18 + main_internal_test.go | 4 +- 21 files changed, 1484 insertions(+), 186 deletions(-) create mode 100644 controllers/config/config_test.go create mode 100644 controllers/config/testdata/config.yaml create mode 100644 controllers/config/testdata/invalid_config.yaml create mode 100644 controllers/config/testdata/invalid_k8s_config.yaml create mode 100644 controllers/config/testdata/k8s_config.yaml rename controllers/warp/{ => types}/category.go (75%) create mode 100644 controllers/warp/types/category_test.go rename controllers/warp/{ => types}/dogu_entry.go (81%) create mode 100644 controllers/warp/types/dogu_entry_test.go rename controllers/warp/{ => types}/external_entry.go (90%) create mode 100644 controllers/warp/types/external_entry_test.go rename controllers/warp/{ => types}/link_entry.go (98%) create mode 100644 controllers/warp/types/link_entry_test.go create mode 100644 controllers/warp/types/mocks/DoguConverter.go create mode 100644 controllers/warp/types/mocks/ExternalConverter.go create mode 100644 controllers/warp_menu_creator_test.go diff --git a/controllers/config/config_test.go b/controllers/config/config_test.go new file mode 100644 index 0000000..b710697 --- /dev/null +++ b/controllers/config/config_test.go @@ -0,0 +1,142 @@ +package config + +import ( + "context" + _ "embed" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/yaml" + "os" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "testing" +) + +//go:embed testdata/k8s_config.yaml +var configBytes []byte +var k8sConfig corev1.ConfigMap + +//go:embed testdata/invalid_k8s_config.yaml +var invalidConfigBytes []byte +var invalidK8sConfig corev1.ConfigMap + +func init() { + err := yaml.Unmarshal(configBytes, &k8sConfig) + if err != nil { + panic(err) + } + + err = yaml.Unmarshal(invalidConfigBytes, &invalidK8sConfig) + if err != nil { + panic(err) + } +} + +func TestReadConfiguration(t *testing.T) { + t.Run("read from file", func(t *testing.T) { + // given + client := fake.NewClientBuilder().Build() + err := os.Setenv("STAGE", "development") + require.NoError(t, err) + defer func() { + err := os.Unsetenv("STAGE") + if err != nil { + panic(err) + } + }() + + // when + _, err = ReadConfiguration(context.TODO(), client, "test") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "could not find configuration at k8s/dev-resources/k8s-ces-warp-config.yaml") + }) + + t.Run("read from cluster", func(t *testing.T) { + // given + client := fake.NewClientBuilder().Build() + ctx := context.TODO() + err := client.Create(ctx, &k8sConfig) + require.NoError(t, err) + + // when + config, err := ReadConfiguration(ctx, client, "test") + + // then + require.NoError(t, err) + assert.NotNil(t, config) + }) +} + +func Test_readWarpConfigFromCluster(t *testing.T) { + namespace := "test" + ctx := context.TODO() + t.Run("success", func(t *testing.T) { + // given + client := fake.NewClientBuilder().Build() + k8sConfig.ResourceVersion = "" + err := client.Create(ctx, &k8sConfig) + require.NoError(t, err) + + // when + config, err := readWarpConfigFromCluster(ctx, client, namespace) + + // then + require.NoError(t, err) + assert.NotNil(t, config) + }) + t.Run("failed to get configmap", func(t *testing.T) { + // given + client := fake.NewClientBuilder().Build() + + // when + _, err := readWarpConfigFromCluster(ctx, client, namespace) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to get warp menu configmap") + }) + + t.Run("failed to unmarshal yaml from configmap", func(t *testing.T) { + // given + client := fake.NewClientBuilder().Build() + err := client.Create(ctx, &invalidK8sConfig) + require.NoError(t, err) + + // when + _, err = readWarpConfigFromCluster(ctx, client, namespace) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to unmarshal yaml from warp config") + }) +} + +func Test_readWarpConfigFromFile(t *testing.T) { + t.Run("success", func(t *testing.T) { + // when + config, err := readWarpConfigFromFile("testdata/config.yaml") + + // then + require.NoError(t, err) + assert.NotNil(t, config) + }) + + t.Run("config does not exists", func(t *testing.T) { + // when + _, err := readWarpConfigFromFile("testdata/doesnotexists.yaml") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "could not find configuration at") + }) + + t.Run("fail because of invalid yaml", func(t *testing.T) { + // when + _, err := readWarpConfigFromFile("testdata/invalid_config.yaml") + + // then + require.Error(t, err) + }) +} diff --git a/controllers/config/testdata/config.yaml b/controllers/config/testdata/config.yaml new file mode 100644 index 0000000..20bd3ea --- /dev/null +++ b/controllers/config/testdata/config.yaml @@ -0,0 +1,21 @@ +sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries +target: /var/www/html/warp/menu.json +order: + Development Apps: 100 +support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/controllers/config/testdata/invalid_config.yaml b/controllers/config/testdata/invalid_config.yaml new file mode 100644 index 0000000..49a2f36 --- /dev/null +++ b/controllers/config/testdata/invalid_config.yaml @@ -0,0 +1,22 @@ +sources: + - pathdsf: /dogu + type: dogus + tag: warp + - patsdfh: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + typsdfe: disabled_support_entries +tardddddget: /var/www/html/warp/menu.json +order: + Development Apps: 100 +suppyourares +omethingelseort: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/controllers/config/testdata/invalid_k8s_config.yaml b/controllers/config/testdata/invalid_k8s_config.yaml new file mode 100644 index 0000000..ba0c9a2 --- /dev/null +++ b/controllers/config/testdata/invalid_k8s_config.yaml @@ -0,0 +1,36 @@ +# +# The default configuration map for the warp menu. +# +apiVersion: v1 +kind: ConfigMap +metadata: + name: k8s-ces-warp-config + namespace: test + labels: + app: cloudogu-ecosystem +data: + warp: | + sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries + target: /var/www/html/warp/menu.json + order: + Development Apps: 100 + supp + + + ort: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/controllers/config/testdata/k8s_config.yaml b/controllers/config/testdata/k8s_config.yaml new file mode 100644 index 0000000..044d957 --- /dev/null +++ b/controllers/config/testdata/k8s_config.yaml @@ -0,0 +1,33 @@ +# +# The default configuration map for the warp menu. +# +apiVersion: v1 +kind: ConfigMap +metadata: + name: k8s-ces-warp-config + namespace: test + labels: + app: cloudogu-ecosystem +data: + warp: | + sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries + target: /var/www/html/warp/menu.json + order: + Development Apps: 100 + support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go index a428dc6..6a9e499 100644 --- a/controllers/warp/configReader.go +++ b/controllers/warp/configReader.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/config" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" "log" "sort" @@ -14,70 +15,69 @@ import ( // ConfigReader reads the configuration for the warp menu from etcd type ConfigReader struct { - configuration *config.Configuration - registry registry.WatchConfigurationContext + configuration *config.Configuration + registry registry.WatchConfigurationContext + doguConverter DoguConverter + externalConverter ExternalConverter } -type DisabledSupportEntries struct { - name []string +// DoguConverter is used to read dogus from the registry and convert them to objects fitting in the warp menu +type DoguConverter interface { + ReadAndUnmarshalDogu(registry registry.WatchConfigurationContext, key string, tag string) (types.EntryWithCategory, error) +} + +// ExternalConverter is used to read external links from the registry and convert them to objects fitting in the warp menu +type ExternalConverter interface { + ReadAndUnmarshalExternal(registry registry.WatchConfigurationContext, key string) (types.EntryWithCategory, error) } const disableWarpSupportEntriesConfigurationKey = "/config/_global/disabled_warpmenu_support_entries" -func (reader *ConfigReader) createCategories(entries []EntryWithCategory) Categories { - categories := map[string]*Category{} +func (reader *ConfigReader) readFromConfig(configuration *config.Configuration) (types.Categories, error) { + var data types.Categories - for _, entry := range entries { - categoryName := entry.Category - category := categories[categoryName] - if category == nil { - category = &Category{ - Title: categoryName, - Entries: Entries{}, - Order: reader.configuration.Order[categoryName], - } - categories[categoryName] = category + for _, source := range configuration.Sources { + // Disabled support entries refresh every time + if source.Type == "disabled_support_entries" { + continue } - category.Entries = append(category.Entries, entry.Entry) - } - result := Categories{} - for _, cat := range categories { - sort.Sort(cat.Entries) - result = append(result, cat) + categories, err := reader.readSource(source) + if err != nil { + log.Println("Error during read:", err) + } + data.InsertCategories(categories) } - sort.Sort(result) - return result -} -// dogusReader reads from etcd and converts the keys and values to a warp menu -// conform structure -func (reader *ConfigReader) dogusReader(source config.Source) (Categories, error) { - log.Printf("read dogus from %s for warp menu", source.Path) - resp, err := reader.registry.GetChildrenPaths(source.Path) + log.Println("read SupportEntries") + disabledSupportEntries, err := reader.getDisabledSupportIdentifiers() if err != nil { - return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) - } - dogus := []EntryWithCategory{} - for _, child := range resp { - dogu, err := readAndUnmarshalDogu(reader.registry, child, source.Tag) - if err == nil && dogu.Entry.Title != "" { - dogus = append(dogus, dogu) - } + log.Println("Error during support read:", err) } + supportCategory := reader.readSupport(configuration.Support, disabledSupportEntries) + data.InsertCategories(supportCategory) + return data, nil +} - return reader.createCategories(dogus), nil +func (reader *ConfigReader) readSource(source config.Source) (types.Categories, error) { + switch source.Type { + case "dogus": + return reader.dogusReader(source) + case "externals": + return reader.externalsReader(source) + } + return nil, errors.Errorf("wrong source type: %v", source.Type) } -func (reader *ConfigReader) externalsReader(source config.Source) (Categories, error) { +func (reader *ConfigReader) externalsReader(source config.Source) (types.Categories, error) { log.Printf("read externals from %s for warp menu", source.Path) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) } - externals := []EntryWithCategory{} + externals := []types.EntryWithCategory{} for _, child := range resp { - external, err := readAndUnmarshalExternal(reader.registry, child) + external, err := reader.externalConverter.ReadAndUnmarshalExternal(reader.registry, child) if err == nil { externals = append(externals, external) } @@ -85,14 +85,23 @@ func (reader *ConfigReader) externalsReader(source config.Source) (Categories, e return reader.createCategories(externals), nil } -func (reader *ConfigReader) readSource(source config.Source) (Categories, error) { - switch source.Type { - case "dogus": - return reader.dogusReader(source) - case "externals": - return reader.externalsReader(source) +// dogusReader reads from etcd and converts the keys and values to a warp menu +// conform structure +func (reader *ConfigReader) dogusReader(source config.Source) (types.Categories, error) { + log.Printf("read dogus from %s for warp menu", source.Path) + resp, err := reader.registry.GetChildrenPaths(source.Path) + if err != nil { + return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) } - return nil, errors.Errorf("wrong source type: %v", source.Type) + dogus := []types.EntryWithCategory{} + for _, path := range resp { + dogu, err := reader.doguConverter.ReadAndUnmarshalDogu(reader.registry, path, source.Tag) + if err == nil && dogu.Entry.Title != "" { + dogus = append(dogus, dogu) + } + } + + return reader.createCategories(dogus), nil } func (reader *ConfigReader) getDisabledSupportIdentifiers() ([]string, error) { @@ -110,55 +119,50 @@ func (reader *ConfigReader) getDisabledSupportIdentifiers() ([]string, error) { return disabledEntries, nil } -func (reader *ConfigReader) readSupport(supportSources []config.SupportSource, disabledSupportEntries []string) (Categories, error) { - var supportEntries []EntryWithCategory +func (reader *ConfigReader) readSupport(supportSources []config.SupportSource, disabledSupportEntries []string) types.Categories { + var supportEntries []types.EntryWithCategory for _, supportSource := range supportSources { // supportSource -> EntryWithCategory if !StringInSlice(supportSource.Identifier, disabledSupportEntries) { - entry := Entry{} + var entry types.Entry if supportSource.External { - entry = Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: TARGET_EXTERNAL} + entry = types.Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: types.TARGET_EXTERNAL} } else { - entry = Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: TARGET_SELF} + entry = types.Entry{Title: supportSource.Identifier, Href: supportSource.Href, Target: types.TARGET_SELF} } - entryWithCategory := EntryWithCategory{Entry: entry, Category: "Support"} + entryWithCategory := types.EntryWithCategory{Entry: entry, Category: "Support"} supportEntries = append(supportEntries, entryWithCategory) } } - return reader.createCategories(supportEntries), nil + return reader.createCategories(supportEntries) } -func (reader *ConfigReader) readFromConfig(configuration *config.Configuration) (Categories, error) { - var data Categories - - for _, source := range configuration.Sources { - // Disabled support entries refresh every time - if source.Type == "disabled_support_entries" { - continue - } +func (reader *ConfigReader) createCategories(entries []types.EntryWithCategory) types.Categories { + categories := map[string]*types.Category{} - categories, err := reader.readSource(source) - if err != nil { - log.Println("Error during read:", err) + for _, entry := range entries { + categoryName := entry.Category + category := categories[categoryName] + if category == nil { + category = &types.Category{ + Title: categoryName, + Entries: types.Entries{}, + Order: reader.configuration.Order[categoryName], + } + categories[categoryName] = category } - data.insertCategories(categories) + category.Entries = append(category.Entries, entry.Entry) } - log.Println("read SupportEntries") - disabledSupportEntries, err := reader.getDisabledSupportIdentifiers() - supportCategory, err := reader.readSupport(configuration.Support, disabledSupportEntries) - if err != nil { - log.Println("Error during support read:", err) - } - if supportCategory.Len() == 0 { - log.Printf("support Category is empty, no support Category will be added to menu.json") - return data, nil + result := types.Categories{} + for _, cat := range categories { + sort.Sort(cat.Entries) + result = append(result, cat) } - - data.insertCategories(supportCategory) - return data, nil + sort.Sort(result) + return result } func StringInSlice(a string, list []string) bool { diff --git a/controllers/warp/configReader_test.go b/controllers/warp/configReader_test.go index db01a45..2455e6f 100644 --- a/controllers/warp/configReader_test.go +++ b/controllers/warp/configReader_test.go @@ -1,78 +1,345 @@ package warp import ( - "github.com/cloudogu/cesapp-lib/registry/mocks" + cesmocks "github.com/cloudogu/cesapp-lib/registry/mocks" "github.com/cloudogu/k8s-service-discovery/controllers/config" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types/mocks" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "testing" ) func TestConfigReader_readSupport(t *testing.T) { + supportSources := []config.SupportSource{{Identifier: "aboutCloudoguToken", External: false, Href: "/local/href"}, {Identifier: "myCloudogu", External: true, Href: "https://ecosystem.cloudogu.com/"}, {Identifier: "docsCloudoguComUrl", External: true, Href: "https://docs.cloudogu.com/"}} reader := &ConfigReader{ configuration: &config.Configuration{Support: []config.SupportSource{}}, registry: nil, } + t.Run("success with one disabled entry", func(t *testing.T) { + // given + expectedCategories := types.Categories{{Title: "Support", Entries: []types.Entry{ + {Title: "aboutCloudoguToken", Target: types.TARGET_SELF, Href: "/local/href"}, + {Title: "myCloudogu", Target: types.TARGET_EXTERNAL, Href: "https://ecosystem.cloudogu.com/"}}}} - supportSources := []config.SupportSource{{Identifier: "aboutCloudoguToken", External: false, Href: "/local/href"}, {Identifier: "myCloudogu", External: true, Href: "https://ecosystem.cloudogu.com/"}, {Identifier: "docsCloudoguComUrl", External: true, Href: "https://docs.cloudogu.com/"}} + // when + actual := reader.readSupport(supportSources, []string{"docsCloudoguComUrl"}) - actual, err := reader.readSupport(supportSources, []string{"docsCloudoguComUrl"}) + // then + assert.Equal(t, expectedCategories, actual, "readSupport did not return the correct Category of two entries") + }) - if err != nil { - t.Fail() - } + t.Run("success with empty filter", func(t *testing.T) { + // given + expectedCategories := types.Categories{ + {Title: "Support", Entries: []types.Entry{ + {Title: "aboutCloudoguToken", Target: types.TARGET_SELF, Href: "/local/href"}, + {Title: "myCloudogu", Target: types.TARGET_EXTERNAL, Href: "https://ecosystem.cloudogu.com/"}, + {Title: "docsCloudoguComUrl", Target: types.TARGET_EXTERNAL, Href: "https://docs.cloudogu.com/"}}}} - expectedCategories := Categories{{Title: "Support", Entries: []Entry{ - {Title: "aboutCloudoguToken", Target: TARGET_SELF, Href: "/local/href"}, - {Title: "myCloudogu", Target: TARGET_EXTERNAL, Href: "https://ecosystem.cloudogu.com/"}}}} + // when + actual := reader.readSupport(supportSources, []string{}) - assert.Equal(t, expectedCategories, actual, "readSupport did not return the correct Category of two entries") + // then + assert.Equal(t, expectedCategories, actual) + }) - // test with empty filter - actual, err = reader.readSupport(supportSources, []string{}) - expectedCategories = Categories{ - {Title: "Support", Entries: []Entry{ - {Title: "aboutCloudoguToken", Target: TARGET_SELF, Href: "/local/href"}, - {Title: "myCloudogu", Target: TARGET_EXTERNAL, Href: "https://ecosystem.cloudogu.com/"}, - {Title: "docsCloudoguComUrl", Target: TARGET_EXTERNAL, Href: "https://docs.cloudogu.com/"}}}} + t.Run("success with complete filter", func(t *testing.T) { + // given + expectedCategories := types.Categories{} - // test with complete filter - actual, err = reader.readSupport(supportSources, []string{"myCloudogu", "aboutCloudoguToken", "docsCloudoguComUrl"}) - expectedCategories = Categories{} + // when + actual := reader.readSupport(supportSources, []string{"myCloudogu", "aboutCloudoguToken", "docsCloudoguComUrl"}) - assert.Equal(t, 0, expectedCategories.Len()) - assert.Equal(t, expectedCategories, actual, "readSupport did not return the correct Category of three entries") + // then + assert.Equal(t, 0, expectedCategories.Len()) + assert.Equal(t, expectedCategories, actual, "readSupport did not return the correct Category of three entries") + }) } func TestConfigReader_getDisabledSupportIdentifiers(t *testing.T) { - mockRegistry := &mocks.WatchConfigurationContext{} - mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) + t.Run("success", func(t *testing.T) { + // given + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + } - reader := &ConfigReader{ - configuration: &config.Configuration{Support: []config.SupportSource{}}, - registry: mockRegistry, - } + // when + identifiers, err := reader.getDisabledSupportIdentifiers() + + // then + assert.Empty(t, err) + assert.Equal(t, []string{"lorem", "ipsum"}, identifiers) + mock.AssertExpectationsForObjects(t, mockRegistry) + }) + + t.Run("failed to get disabled support entries", func(t *testing.T) { + // given + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("", assert.AnError) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + } + + // when + _, err := reader.getDisabledSupportIdentifiers() + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read configuration entry /config/_global/disabled_warpmenu_support_entries from etcd") + mock.AssertExpectationsForObjects(t, mockRegistry) + }) - identifiers, err := reader.getDisabledSupportIdentifiers() - assert.Empty(t, err) - assert.Equal(t, []string{"lorem", "ipsum"}, identifiers) + t.Run("failed to unmarshal disabled support entries", func(t *testing.T) { + // given + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("{\"lorem\": \"ipsum\"}", nil) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + } + + // when + _, err := reader.getDisabledSupportIdentifiers() + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to unmarshal etcd key") + mock.AssertExpectationsForObjects(t, mockRegistry) + }) } func TestConfigReader_readFromConfig(t *testing.T) { - mockRegistry := &mocks.WatchConfigurationContext{} + mockRegistry := &cesmocks.WatchConfigurationContext{} mockRegistry.On("GetChildrenPaths", "/path/to/etcd/key").Return([]string{"/path/to/etcd/key"}, nil) - mockRegistry.On("Get", "/path/to/etcd/key").Return("", nil) mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) + testSources := []config.Source{{Path: "/path/to/etcd/key", Type: "externals", Tag: "tag"}, {Path: "/path", Type: "disabled_support_entries"}} + testSupportSoureces := []config.SupportSource{{Identifier: "supportSrc", External: true, Href: "path/to/external"}} + mockDoguConverter := &mocks.DoguConverter{} - reader := &ConfigReader{ - configuration: &config.Configuration{Support: []config.SupportSource{}}, - registry: mockRegistry, - } + t.Run("success with one external and support link", func(t *testing.T) { + // given + cloudoguEntryWithCategory := getEntryWithCategory("Cloudogu", "www.cloudogu.com", "Cloudogu", "External", types.TARGET_EXTERNAL) + mockExternalConverter := &mocks.ExternalConverter{} + mockExternalConverter.On("ReadAndUnmarshalExternal", mockRegistry, mock.Anything).Return(cloudoguEntryWithCategory, nil) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + doguConverter: mockDoguConverter, + externalConverter: mockExternalConverter, + } - testSources := []config.Source{{Path: "/path/to/etcd/key", Type: "externals", Tag: "tag"}} - testSupportSoureces := []config.SupportSource{{Identifier: "supportSrc", External: true, Href: "path/to/external"}} + // when + actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + + // then + assert.Empty(t, err) + assert.NotEmpty(t, actual) + assert.Equal(t, 2, len(actual)) + mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) + }) + + t.Run("success with one dogu and support link", func(t *testing.T) { + // given + mockDoguConverter := &mocks.DoguConverter{} + mockExternalConverter := &mocks.ExternalConverter{} + doguSource := config.Source{ + Path: "/dogu", + Type: "dogus", + Tag: "warp", + } + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", mock.Anything).Return([]string{}, nil) + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", nil) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + doguConverter: mockDoguConverter, + externalConverter: mockExternalConverter, + } + + // when + actual, err := reader.readFromConfig(&config.Configuration{Sources: []config.Source{doguSource}, Support: testSupportSoureces}) + + // then + assert.Empty(t, err) + assert.NotEmpty(t, actual) + assert.Equal(t, 1, len(actual)) + mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) + }) + + t.Run("error during external read should not result in an error", func(t *testing.T) { + // given + mockExternalConverter := &mocks.ExternalConverter{} + mockExternalConverter.On("ReadAndUnmarshalExternal", mockRegistry, mock.Anything).Return(types.EntryWithCategory{}, assert.AnError) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + doguConverter: mockDoguConverter, + externalConverter: mockExternalConverter, + } + + // when + actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + + // then + assert.Empty(t, err) + assert.NotEmpty(t, actual) + assert.Equal(t, 1, len(actual)) + mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) + }) + + t.Run("error during support read should not result in an error", func(t *testing.T) { + // given + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", "/path/to/etcd/key").Return([]string{"/path/to/etcd/key"}, nil) + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[\"lorem\", \"ipsum\"]", assert.AnError) + mockExternalConverter := &mocks.ExternalConverter{} + mockExternalConverter.On("ReadAndUnmarshalExternal", mockRegistry, mock.Anything).Return(types.EntryWithCategory{}, assert.AnError) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + doguConverter: mockDoguConverter, + externalConverter: mockExternalConverter, + } + + // when + actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + + // then + assert.Empty(t, err) + assert.NotEmpty(t, actual) + assert.Equal(t, 1, len(actual)) + mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) + }) + + t.Run("empty support category should not result in an error", func(t *testing.T) { + // given + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", "/path/to/etcd/key").Return([]string{"/path/to/etcd/key"}, nil) + mockRegistry.On("Get", "/config/_global/disabled_warpmenu_support_entries").Return("[]", nil) + mockExternalConverter := &mocks.ExternalConverter{} + mockExternalConverter.On("ReadAndUnmarshalExternal", mockRegistry, mock.Anything).Return(types.EntryWithCategory{}, assert.AnError) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + doguConverter: mockDoguConverter, + externalConverter: mockExternalConverter, + } + + // when + actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + + // then + assert.Empty(t, err) + assert.NotEmpty(t, actual) + assert.Equal(t, 1, len(actual)) + mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) + }) + + t.Run("skip wrong source type", func(t *testing.T) { + // given + testSources := []config.Source{{Path: "/path/to/etcd/key", Type: "fjkhsdfjh", Tag: "tag"}} + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + } + // when + _, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + + // then + require.NoError(t, err) + }) +} + +func TestConfigReader_dogusReader(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + source := config.Source{ + Path: "/dogu", + Type: "dogus", + Tag: "warp", + } + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", "/dogu").Return([]string{"/dogu/redmine", "/dogu/jenkins"}, nil) + redmineEntryWithCategory := getEntryWithCategory("Redmine", "/redmine", "Redmine", "Development Apps", types.TARGET_SELF) + jenkinsEntryWithCategory := getEntryWithCategory("Jenkins", "/jenkins", "Jenkins", "Development Apps", types.TARGET_SELF) + mockDoguConverter := &mocks.DoguConverter{} + mockDoguConverter.On("ReadAndUnmarshalDogu", mockRegistry, "/dogu/redmine", "warp").Return(redmineEntryWithCategory, nil) + mockDoguConverter.On("ReadAndUnmarshalDogu", mockRegistry, "/dogu/jenkins", "warp").Return(jenkinsEntryWithCategory, nil) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + doguConverter: mockDoguConverter, + } + + // when + categories, err := reader.dogusReader(source) + + // then + require.NoError(t, err) + assert.Equal(t, 1, categories.Len()) + assert.Equal(t, 2, len(categories[0].Entries)) + mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter) + }) + + t.Run("failed to get children of /dogu path", func(t *testing.T) { + // given + source := config.Source{ + Path: "/dogu", + Type: "dogus", + Tag: "warp", + } + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", "/dogu").Return([]string{}, assert.AnError) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + } + + // when + _, err := reader.dogusReader(source) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read root entry /dogu from etcd") + mock.AssertExpectationsForObjects(t, mockRegistry) + }) +} + +func getEntryWithCategory(displayName string, href string, title string, category string, target types.Target) types.EntryWithCategory { + return types.EntryWithCategory{Entry: types.Entry{ + DisplayName: displayName, + Href: href, + Title: title, + Target: target, + }, Category: category} +} + +func TestConfigReader_externalsReader(t *testing.T) { + t.Run("fail to get children paths", func(t *testing.T) { + // given + source := config.Source{ + Path: "/path", + Type: "externals", + } + mockRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("GetChildrenPaths", "/path").Return([]string{}, assert.AnError) + reader := &ConfigReader{ + configuration: &config.Configuration{Support: []config.SupportSource{}}, + registry: mockRegistry, + } - actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + // when + _, err := reader.externalsReader(source) - assert.Empty(t, err) - assert.NotEmpty(t, actual) + // then + require.Error(t, err) + mock.AssertExpectationsForObjects(t, mockRegistry) + }) } diff --git a/controllers/warp/category.go b/controllers/warp/types/category.go similarity index 75% rename from controllers/warp/category.go rename to controllers/warp/types/category.go index 1c2e6ce..8bb576b 100644 --- a/controllers/warp/category.go +++ b/controllers/warp/types/category.go @@ -1,6 +1,6 @@ -package warp +package types -// Category Category of multiple entries in the warp menu +// Category categories multiple entries in the warp menu type Category struct { Title string Order int @@ -29,13 +29,13 @@ func (c Categories) Swap(i, j int) { c[i], c[j] = c[j], c[i] } -func (c *Categories) insertCategories(newCategories Categories) { +func (c *Categories) InsertCategories(newCategories Categories) { for _, newCategory := range newCategories { - c.insertCategory(newCategory) + c.InsertCategory(newCategory) } } -func (c *Categories) insertCategory(newCategory *Category) { +func (c *Categories) InsertCategory(newCategory *Category) { for _, category := range *c { if category.Title == newCategory.Title { category.Entries = append(category.Entries, newCategory.Entries...) diff --git a/controllers/warp/types/category_test.go b/controllers/warp/types/category_test.go new file mode 100644 index 0000000..37bccba --- /dev/null +++ b/controllers/warp/types/category_test.go @@ -0,0 +1,129 @@ +package types + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestCategories_Len(t *testing.T) { + // given + a := &Category{} + b := &Category{} + categories := Categories{a, b} + + // when + length := categories.Len() + + // then + assert.Equal(t, 2, length) +} + +func TestCategories_Less(t *testing.T) { + t.Run("less with different orders", func(t *testing.T) { + // given + a := &Category{Order: 1} + b := &Category{Order: 100} + categories := Categories{a, b} + + // when + isLess := categories.Less(0, 1) + + // then + assert.Equal(t, false, isLess) + }) + + t.Run("should orientate on title with same orders", func(t *testing.T) { + // given + a := &Category{Order: 100, Title: "A"} + b := &Category{Order: 100, Title: "B"} + categories := Categories{a, b} + + // when + isLess := categories.Less(0, 1) + + // then + assert.Equal(t, true, isLess) + }) +} + +func TestCategories_Swap(t *testing.T) { + // given + a := &Category{Order: 1} + b := &Category{Order: 100} + categories := Categories{a, b} + + // when + categories.Swap(0, 1) + + // then + assert.Equal(t, categories[0], b) + assert.Equal(t, categories[1], a) +} + +func TestCategories_insertCategories(t *testing.T) { + // given + a := &Category{Order: 1, Title: "a"} + b := &Category{Order: 100, Title: "b"} + categories := Categories{a, b} + aa := &Category{Order: 1, Title: "aa"} + bb := &Category{Order: 100, Title: "bb"} + addCategories := Categories{aa, bb} + + // when + categories.InsertCategories(addCategories) + + // then + assert.Equal(t, 4, len(categories)) + assert.Equal(t, a, categories[0]) + assert.Equal(t, b, categories[1]) + assert.Equal(t, aa, categories[2]) + assert.Equal(t, bb, categories[3]) +} + +func TestCategories_insertCategory(t *testing.T) { + t.Run("new title does not exists", func(t *testing.T) { + // given + a := &Category{Order: 1, Title: "a"} + b := &Category{Order: 100, Title: "b"} + categories := Categories{a, b} + add := &Category{Order: 50, Title: "c"} + + // when + categories.InsertCategory(add) + + // then + assert.Equal(t, 3, len(categories)) + assert.Equal(t, add, categories[2]) + }) + + t.Run("add entries on same title", func(t *testing.T) { + // given + aEntry := Entry{Title: "a"} + aEntries := Entries{aEntry} + a := &Category{Order: 1, Title: "a", Entries: aEntries} + b := &Category{Order: 100, Title: "b"} + categories := Categories{a, b} + addEntry := Entry{Title: "add"} + addEntries := Entries{addEntry} + add := &Category{Order: 50, Title: "a", Entries: addEntries} + + // when + categories.InsertCategory(add) + + // then + assert.Equal(t, 2, len(categories)) + assert.Equal(t, aEntry, categories[0].Entries[0]) + assert.Equal(t, addEntry, categories[0].Entries[1]) + }) +} + +func TestCategory_String(t *testing.T) { + // given + a := &Category{Order: 1, Title: "title"} + + // when + str := a.String() + + // then + assert.Equal(t, "title", str) +} diff --git a/controllers/warp/dogu_entry.go b/controllers/warp/types/dogu_entry.go similarity index 81% rename from controllers/warp/dogu_entry.go rename to controllers/warp/types/dogu_entry.go index 00a0932..597788f 100644 --- a/controllers/warp/dogu_entry.go +++ b/controllers/warp/types/dogu_entry.go @@ -1,4 +1,4 @@ -package warp +package types import ( "encoding/json" @@ -18,7 +18,9 @@ type doguEntry struct { Tags []string } -func readAndUnmarshalDogu(registry registry.WatchConfigurationContext, key string, tag string) (EntryWithCategory, error) { +type DoguConverter struct{} + +func (dc *DoguConverter) ReadAndUnmarshalDogu(registry registry.WatchConfigurationContext, key string, tag string) (EntryWithCategory, error) { doguBytes, err := readDoguAsBytes(registry, key) if err != nil { return EntryWithCategory{}, err @@ -37,22 +39,21 @@ func readAndUnmarshalDogu(registry registry.WatchConfigurationContext, key strin } func readDoguAsBytes(registry registry.WatchConfigurationContext, key string) ([]byte, error) { - resp, err := registry.Get(key + "/current") + version, err := registry.Get(key + "/current") if err != nil { // the dogu seems to be unregistered if isKeyNotFound(err) { return nil, nil } - return nil, fmt.Errorf("failed to read key %s from etcd: %w", key, err) + return nil, fmt.Errorf("failed to read key %s/current from etcd: %w", key, err) } - version := resp - resp, err = registry.Get(key + "/" + version) + dogu, err := registry.Get(key + "/" + version) if err != nil { - return nil, fmt.Errorf("failed to read version child from key %s: %w", key, err) + return nil, fmt.Errorf("failed to read %s with version %s: %w", key, version, err) } - return []byte(resp), nil + return []byte(dogu), nil } func unmarshalDogu(doguBytes []byte) (doguEntry, error) { diff --git a/controllers/warp/types/dogu_entry_test.go b/controllers/warp/types/dogu_entry_test.go new file mode 100644 index 0000000..f5a7717 --- /dev/null +++ b/controllers/warp/types/dogu_entry_test.go @@ -0,0 +1,348 @@ +package types + +import ( + "github.com/cloudogu/cesapp-lib/registry/mocks" + coreosclient "github.com/coreos/etcd/client" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "testing" +) + +func Test_containsString(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + s := "s" + slice := []string{"s", "sp"} + + // when + result := containsString(slice, s) + + // then + assert.True(t, result) + }) + + t.Run("not in slive", func(t *testing.T) { + // given + s := "s" + slice := []string{"a", "sp"} + + // when + result := containsString(slice, s) + + // then + assert.False(t, result) + }) +} + +func Test_createDoguHref(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + doguStr := "namespace/redmine" + + // when + result := createDoguHref(doguStr) + + // then + assert.Equal(t, "/redmine", result) + }) +} + +func Test_isKeyNotFound(t *testing.T) { + t.Run("return true on code key not found", func(t *testing.T) { + // given + err := coreosclient.Error{Code: coreosclient.ErrorCodeKeyNotFound} + + // when + result := isKeyNotFound(err) + + // then + assert.True(t, result) + }) + + t.Run("return false on wrong error type", func(t *testing.T) { + // given + err := assert.AnError + + // when + result := isKeyNotFound(err) + + // then + assert.False(t, result) + }) + + tests := []struct { + name string + key int + want bool + }{ + {name: "error on !key not found", key: coreosclient.ErrorCodeDirNotEmpty, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeTestFailed, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeNotFile, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeNotDir, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeNodeExist, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeRootROnly, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeUnauthorized, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodePrevValueRequired, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeTTLNaN, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeIndexNaN, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeInvalidField, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeInvalidForm, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeRaftInternal, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeLeaderElect, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeWatcherCleared, want: false}, + {name: "error on !key not found", key: coreosclient.ErrorCodeEventIndexCleared, want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := coreosclient.Error{Code: tt.key} + assert.Equalf(t, tt.want, isKeyNotFound(err), "isKeyNotFound(%v)") + }) + } +} + +func Test_mapDoguEntry(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + doguEntry := doguEntry{Name: "redmine", DisplayName: "HD", Category: "Dev"} + + // when + entryWithCategory, err := mapDoguEntry(doguEntry) + + // then + require.NoError(t, err) + assert.Equal(t, doguEntry.Category, entryWithCategory.Category) + assert.Equal(t, doguEntry.Description, entryWithCategory.Entry.Title) + assert.Equal(t, doguEntry.DisplayName, entryWithCategory.Entry.DisplayName) + assert.Equal(t, TARGET_SELF, entryWithCategory.Entry.Target) + assert.Equal(t, "/redmine", entryWithCategory.Entry.Href) + }) + + t.Run("error on nameless entry", func(t *testing.T) { + // given + doguEntry := doguEntry{Name: "", DisplayName: "HD", Category: "Dev"} + + // when + _, err := mapDoguEntry(doguEntry) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "name is required for dogu entries") + }) + + t.Run("user name on empty displayname", func(t *testing.T) { + // given + doguEntry := doguEntry{Name: "redmine", DisplayName: "", Category: "Dev"} + + // when + entryWithCategory, err := mapDoguEntry(doguEntry) + + // then + require.NoError(t, err) + assert.Equal(t, doguEntry.Category, entryWithCategory.Category) + assert.Equal(t, doguEntry.Description, entryWithCategory.Entry.Title) + assert.Equal(t, doguEntry.Name, entryWithCategory.Entry.DisplayName) + assert.Equal(t, TARGET_SELF, entryWithCategory.Entry.Target) + assert.Equal(t, "/redmine", entryWithCategory.Entry.Href) + }) +} + +func Test_readAndUnmarshalDogu(t *testing.T) { + doguEntry := doguEntry{Name: "official/redmine", DisplayName: "Redmine", Category: "Development Apps", Description: "Redmine"} + doguStr := "{\n \"Name\": \"official/redmine\",\n \"Version\": \"1.0.0-1\",\n \"DisplayName\": \"Redmine\",\n \"Description\": \"Redmine\",\n \"Category\": \"Development Apps\",\n \"Tags\": [\n \"warp\",\n \"pm\",\n \"projectmanagement\",\n \"issue\",\n \"task\"\n ],\n \"Logo\": \"https://cloudogu.com/images/dogus/redmine.png\",\n \"Url\": \"http://www.redmine.org\",\n \"Image\": \"registry.cloudogu.com/official/redmine\",\n \"Dependencies\": [\n {\n \"type\": \"dogu\",\n \"name\": \"postgresql\"\n },\n {\n \"type\": \"dogu\",\n \"name\": \"cas\"\n },\n {\n \"type\": \"dogu\",\n \"name\": \"nginx\"\n },\n {\n \"type\": \"dogu\",\n \"name\": \"postfix\"\n }\n ],\n \"Configuration\": [\n {\n \"Name\": \"logging/root\",\n \"Description\": \"Set the root log level to one of ERROR, WARN, INFO, DEBUG.\",\n \"Optional\": true,\n \"Default\": \"INFO\",\n \"Validation\": {\n \"Type\": \"ONE_OF\",\n \"Values\": [\n \"WARN\",\n \"DEBUG\",\n \"INFO\",\n \"ERROR\"\n ]\n }\n },\n {\n \"Name\": \"container_config/memory_limit\",\n \"Description\": \"Limits the container's memory usage. Use a positive integer value followed by one of these units [b,k,m,g] (byte, kibibyte, mebibyte, gibibyte).\",\n \"Optional\": true,\n \"Validation\": {\n \"Type\": \"BINARY_MEASUREMENT\"\n }\n },\n {\n \"Name\": \"container_config/swap_limit\",\n \"Description\": \"Limits the container's swap memory usage. Use zero or a positive integer value followed by one of these units [b,k,m,g] (byte, kibibyte, mebibyte, gibibyte). 0 will disable swapping.\",\n \"Optional\": true,\n \"Validation\": {\n \"Type\": \"BINARY_MEASUREMENT\"\n }\n },\n {\n \"Name\": \"etcd_redmine_config\",\n \"Description\": \"Applies default configuration to redmine.\",\n \"Optional\": true\n }\n ],\n \"Volumes\": [\n {\n \"Name\": \"files\",\n \"Path\": \"/usr/share/webapps/redmine/files\",\n \"Owner\": \"1000\",\n \"Group\": \"1000\",\n \"NeedsBackup\": true\n },\n {\n \"Name\": \"plugins\",\n \"Path\": \"/var/tmp/redmine/plugins\",\n \"Owner\": \"1000\",\n \"Group\": \"1000\",\n \"NeedsBackup\": false\n },\n {\n \"Name\": \"logs\",\n \"Path\": \"/usr/share/webapps/redmine/log\",\n \"Owner\": \"1000\",\n \"Group\": \"1000\",\n \"NeedsBackup\": false\n }\n ],\n \"ServiceAccounts\": [\n {\n \"Type\": \"postgresql\"\n }\n ],\n \"HealthChecks\": [\n {\n \"Type\": \"tcp\",\n \"Port\": 3000\n },\n {\n \"Type\": \"state\"\n }\n ],\n \"ExposedCommands\": [\n {\n \"Name\": \"post-upgrade\",\n \"Command\": \"/post-upgrade.sh\"\n },\n {\n \"Name\": \"upgrade-notification\",\n \"Command\": \"/upgrade-notification.sh\"\n },\n {\n \"Name\": \"pre-upgrade\",\n \"Command\": \"/pre-upgrade.sh\"\n },\n {\n \"Name\": \"delete-plugin\",\n \"Command\": \"/delete-plugin.sh\"\n }\n ]\n}" + t.Run("success with specific tag (warp)", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("1.0.0-1", nil) + registryMock.On("Get", "dogu/redmine/1.0.0-1").Return(doguStr, nil) + converter := DoguConverter{} + + // when + entryWithCategory, err := converter.ReadAndUnmarshalDogu(registryMock, "dogu/redmine", "warp") + + // then + require.NoError(t, err) + assert.Equal(t, doguEntry.Category, entryWithCategory.Category) + assert.Equal(t, doguEntry.Description, entryWithCategory.Entry.Title) + assert.Equal(t, doguEntry.DisplayName, entryWithCategory.Entry.DisplayName) + assert.Equal(t, TARGET_SELF, entryWithCategory.Entry.Target) + assert.Equal(t, "/redmine", entryWithCategory.Entry.Href) + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("success without specific tag", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("1.0.0-1", nil) + registryMock.On("Get", "dogu/redmine/1.0.0-1").Return(doguStr, nil) + converter := DoguConverter{} + + // when + entryWithCategory, err := converter.ReadAndUnmarshalDogu(registryMock, "dogu/redmine", "") + + // then + require.NoError(t, err) + assert.Equal(t, doguEntry.Category, entryWithCategory.Category) + assert.Equal(t, doguEntry.Description, entryWithCategory.Entry.Title) + assert.Equal(t, doguEntry.DisplayName, entryWithCategory.Entry.DisplayName) + assert.Equal(t, TARGET_SELF, entryWithCategory.Entry.Target) + assert.Equal(t, "/redmine", entryWithCategory.Entry.Href) + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("failed to read dogu as bytes", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("", assert.AnError) + converter := DoguConverter{} + + // when + _, err := converter.ReadAndUnmarshalDogu(registryMock, "dogu/redmine", "warp") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read key dogu/redmine/current from etcd") + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("failed to unmarshal dogu", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("1.0.0-1", nil) + registryMock.On("Get", "dogu/redmine/1.0.0-1").Return("fdsfsdf", nil) + converter := DoguConverter{} + + // when + _, err := converter.ReadAndUnmarshalDogu(registryMock, "dogu/redmine", "warp") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to unmarshall json from etcd") + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("return empty entry with category if the tag is not found", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("1.0.0-1", nil) + registryMock.On("Get", "dogu/redmine/1.0.0-1").Return(doguStr, nil) + converter := DoguConverter{} + + // when + entryWithCategory, err := converter.ReadAndUnmarshalDogu(registryMock, "dogu/redmine", "dontbethere") + + // then + require.NoError(t, err) + assert.Equal(t, EntryWithCategory{}, entryWithCategory) + mock.AssertExpectationsForObjects(t, registryMock) + }) +} + +func Test_readDoguAsBytes(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + doguStr := "{\n \"Name\": \"official/redmine\",\n \"Version\": \"1.0.0-1\",\n \"DisplayName\": \"Redmine\",\n \"Description\": \"Redmine is a flexible project management web application\",\n \"Category\": \"Development Apps\",\n \"Tags\": [\n \"warp\",\n \"pm\",\n \"projectmanagement\",\n \"issue\",\n \"task\"\n ],\n \"Logo\": \"https://cloudogu.com/images/dogus/redmine.png\",\n \"Url\": \"http://www.redmine.org\",\n \"Image\": \"registry.cloudogu.com/official/redmine\",\n \"Dependencies\": [\n {\n \"type\": \"dogu\",\n \"name\": \"postgresql\"\n },\n {\n \"type\": \"dogu\",\n \"name\": \"cas\"\n },\n {\n \"type\": \"dogu\",\n \"name\": \"nginx\"\n },\n {\n \"type\": \"dogu\",\n \"name\": \"postfix\"\n }\n ],\n \"Configuration\": [\n {\n \"Name\": \"logging/root\",\n \"Description\": \"Set the root log level to one of ERROR, WARN, INFO, DEBUG.\",\n \"Optional\": true,\n \"Default\": \"INFO\",\n \"Validation\": {\n \"Type\": \"ONE_OF\",\n \"Values\": [\n \"WARN\",\n \"DEBUG\",\n \"INFO\",\n \"ERROR\"\n ]\n }\n },\n {\n \"Name\": \"container_config/memory_limit\",\n \"Description\": \"Limits the container's memory usage. Use a positive integer value followed by one of these units [b,k,m,g] (byte, kibibyte, mebibyte, gibibyte).\",\n \"Optional\": true,\n \"Validation\": {\n \"Type\": \"BINARY_MEASUREMENT\"\n }\n },\n {\n \"Name\": \"container_config/swap_limit\",\n \"Description\": \"Limits the container's swap memory usage. Use zero or a positive integer value followed by one of these units [b,k,m,g] (byte, kibibyte, mebibyte, gibibyte). 0 will disable swapping.\",\n \"Optional\": true,\n \"Validation\": {\n \"Type\": \"BINARY_MEASUREMENT\"\n }\n },\n {\n \"Name\": \"etcd_redmine_config\",\n \"Description\": \"Applies default configuration to redmine.\",\n \"Optional\": true\n }\n ],\n \"Volumes\": [\n {\n \"Name\": \"files\",\n \"Path\": \"/usr/share/webapps/redmine/files\",\n \"Owner\": \"1000\",\n \"Group\": \"1000\",\n \"NeedsBackup\": true\n },\n {\n \"Name\": \"plugins\",\n \"Path\": \"/var/tmp/redmine/plugins\",\n \"Owner\": \"1000\",\n \"Group\": \"1000\",\n \"NeedsBackup\": false\n },\n {\n \"Name\": \"logs\",\n \"Path\": \"/usr/share/webapps/redmine/log\",\n \"Owner\": \"1000\",\n \"Group\": \"1000\",\n \"NeedsBackup\": false\n }\n ],\n \"ServiceAccounts\": [\n {\n \"Type\": \"postgresql\"\n }\n ],\n \"HealthChecks\": [\n {\n \"Type\": \"tcp\",\n \"Port\": 3000\n },\n {\n \"Type\": \"state\"\n }\n ],\n \"ExposedCommands\": [\n {\n \"Name\": \"post-upgrade\",\n \"Command\": \"/post-upgrade.sh\"\n },\n {\n \"Name\": \"upgrade-notification\",\n \"Command\": \"/upgrade-notification.sh\"\n },\n {\n \"Name\": \"pre-upgrade\",\n \"Command\": \"/pre-upgrade.sh\"\n },\n {\n \"Name\": \"delete-plugin\",\n \"Command\": \"/delete-plugin.sh\"\n }\n ]\n}" + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("1.0.0-1", nil) + registryMock.On("Get", "dogu/redmine/1.0.0-1").Return(doguStr, nil) + + // when + bytes, err := readDoguAsBytes(registryMock, "dogu/redmine") + + // then + require.NoError(t, err) + assert.Equal(t, []byte(doguStr), bytes) + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("no version should not return an error", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + testErr := coreosclient.Error{Code: coreosclient.ErrorCodeKeyNotFound} + registryMock.On("Get", "dogu/redmine/current").Return("", testErr) + + // when + bytes, err := readDoguAsBytes(registryMock, "dogu/redmine") + + // then + require.NoError(t, err) + require.Nil(t, bytes) + require.Nil(t, err) + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("no version should not return an error", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + testErr := coreosclient.Error{Code: coreosclient.ErrorCodeKeyNotFound} + registryMock.On("Get", "dogu/redmine/current").Return("", testErr) + + // when + bytes, err := readDoguAsBytes(registryMock, "dogu/redmine") + + // then + require.NoError(t, err) + require.Nil(t, bytes) + require.Nil(t, err) + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("failed to get version", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("", assert.AnError) + + // when + _, err := readDoguAsBytes(registryMock, "dogu/redmine") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read key dogu/redmine/current from etcd") + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("failed to get dogu with version", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "dogu/redmine/current").Return("1.0.0-1", nil) + registryMock.On("Get", "dogu/redmine/1.0.0-1").Return("", assert.AnError) + + // when + _, err := readDoguAsBytes(registryMock, "dogu/redmine") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read dogu/redmine with version 1.0.0-1") + mock.AssertExpectationsForObjects(t, registryMock) + }) +} + +func Test_unmarshalDogu(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + doguJsonStr := "{\n \"Name\": \"redmine\",\n \"DisplayName\": \"display\",\n \"Description\": \"desc\",\n \"Category\": \"category\",\n \"Tags\": [\n \"warp\",\n \"test\"\n ]\n}" + + // when + result, err := unmarshalDogu([]byte(doguJsonStr)) + + // then + require.NoError(t, err) + assert.Equal(t, "redmine", result.Name) + assert.Equal(t, "display", result.DisplayName) + assert.Equal(t, "desc", result.Description) + assert.Equal(t, "category", result.Category) + assert.Equal(t, "warp", result.Tags[0]) + assert.Equal(t, "test", result.Tags[1]) + }) + + t.Run("fail on wrong struct", func(t *testing.T) { + // given + doguJsonStr := "fsdfsd" + + // when + _, err := unmarshalDogu([]byte(doguJsonStr)) + + // then + require.Error(t, err) + }) +} diff --git a/controllers/warp/external_entry.go b/controllers/warp/types/external_entry.go similarity index 90% rename from controllers/warp/external_entry.go rename to controllers/warp/types/external_entry.go index 1e04976..a8ba7fd 100644 --- a/controllers/warp/external_entry.go +++ b/controllers/warp/types/external_entry.go @@ -1,4 +1,4 @@ -package warp +package types import ( "encoding/json" @@ -21,7 +21,9 @@ type EntryWithCategory struct { Category string } -func readAndUnmarshalExternal(registry registry.WatchConfigurationContext, key string) (EntryWithCategory, error) { +type ExternalConverter struct{} + +func (ec *ExternalConverter) ReadAndUnmarshalExternal(registry registry.WatchConfigurationContext, key string) (EntryWithCategory, error) { externalBytes, err := readExternalAsBytes(registry, key) if err != nil { return EntryWithCategory{}, nil diff --git a/controllers/warp/types/external_entry_test.go b/controllers/warp/types/external_entry_test.go new file mode 100644 index 0000000..0567f74 --- /dev/null +++ b/controllers/warp/types/external_entry_test.go @@ -0,0 +1,156 @@ +package types + +import ( + "github.com/cloudogu/cesapp-lib/registry/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "testing" +) + +func Test_mapExternalEntry(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + externalEntry := externalEntry{ + DisplayName: "HD-Display", + URL: "URL", + Description: "Desc", + Category: "Category", + } + + // when + entryWithCategory, err := mapExternalEntry(externalEntry) + + // then + require.NoError(t, err) + require.NotNil(t, entryWithCategory) + assert.Equal(t, TARGET_EXTERNAL, entryWithCategory.Entry.Target) + assert.Equal(t, externalEntry.Category, entryWithCategory.Category) + }) + + t.Run("error because displayname is not set", func(t *testing.T) { + // given + externalEntry := externalEntry{} + + // when + _, err := mapExternalEntry(externalEntry) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "could not find DisplayName on external entry") + }) + + t.Run("error because url is not set", func(t *testing.T) { + // given + externalEntry := externalEntry{ + DisplayName: "HD-Display", + } + + // when + _, err := mapExternalEntry(externalEntry) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "could not find URL on external entry") + }) + + t.Run("error because category is not set", func(t *testing.T) { + // given + externalEntry := externalEntry{ + DisplayName: "HD-Display", + URL: "URL", + } + + // when + _, err := mapExternalEntry(externalEntry) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "could not find Category on external entry") + }) +} + +func Test_readExternalAsBytes(t *testing.T) { + t.Run("fail on registry get", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "/key").Return("", assert.AnError) + + // when + _, err := readExternalAsBytes(registryMock, "/key") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read key /key from etcd") + mock.AssertExpectationsForObjects(t, registryMock) + }) +} + +func Test_readAndUnmarshalExternal(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + entryStr := "{\n \"DisplayName\": \"display\",\n \"URL\": \"url\",\n \"Description\": \"desc\",\n \"Category\": \"category\"\n}" + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "key").Return(entryStr, nil) + expectedEntryWithCategory := EntryWithCategory{ + Entry: Entry{ + DisplayName: "display", + Href: "url", + Title: "desc", + Target: TARGET_EXTERNAL, + }, + Category: "category", + } + externalConverter := ExternalConverter{} + + // when + result, err := externalConverter.ReadAndUnmarshalExternal(registryMock, "key") + + // then + require.NoError(t, err) + assert.Equal(t, expectedEntryWithCategory, result) + mock.AssertExpectationsForObjects(t, registryMock) + }) + + t.Run("return empty category on error", func(t *testing.T) { + // given + registryMock := &mocks.WatchConfigurationContext{} + registryMock.On("Get", "/key").Return("", assert.AnError) + converter := ExternalConverter{} + + // when + entryWithCategory, err := converter.ReadAndUnmarshalExternal(registryMock, "/key") + + // then + require.NoError(t, err) + assert.Equal(t, EntryWithCategory{}, entryWithCategory) + mock.AssertExpectationsForObjects(t, registryMock) + }) +} + +func Test_unmarshalExternal(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + externalEntryStr := "{\n \"DisplayName\": \"Redmine\",\n \"URL\": \"/redmine\",\n \"Description\": \"Redmine\",\n \"Category\": \"Development Apps\"\n}" + + // when + entryWithCategory, err := unmarshalExternal([]byte(externalEntryStr)) + + // then + require.NoError(t, err) + assert.Equal(t, TARGET_EXTERNAL, entryWithCategory.Entry.Target) + assert.Equal(t, "Development Apps", entryWithCategory.Category) + }) + + t.Run("failed to unmarshal external entry", func(t *testing.T) { + // given + externalEntryStr := "fdksjf" + + // when + _, err := unmarshalExternal([]byte(externalEntryStr)) + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to unmarshall external") + }) +} diff --git a/controllers/warp/link_entry.go b/controllers/warp/types/link_entry.go similarity index 98% rename from controllers/warp/link_entry.go rename to controllers/warp/types/link_entry.go index 618954c..825451b 100644 --- a/controllers/warp/link_entry.go +++ b/controllers/warp/types/link_entry.go @@ -1,4 +1,4 @@ -package warp +package types import "github.com/pkg/errors" diff --git a/controllers/warp/types/link_entry_test.go b/controllers/warp/types/link_entry_test.go new file mode 100644 index 0000000..b4da40c --- /dev/null +++ b/controllers/warp/types/link_entry_test.go @@ -0,0 +1,82 @@ +package types + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/json" + "testing" +) + +func TestCategoryString(t *testing.T) { + category := Category{Title: "Hitchhiker"} + assert.Equal(t, "Hitchhiker", fmt.Sprintf("%v", category)) +} + +func TestTarget_MarshalJSON(t *testing.T) { + testMarshalJSON(t, TARGET_EXTERNAL, "{\"Target\":\"external\"}") + testMarshalJSON(t, TARGET_SELF, "{\"Target\":\"self\"}") + + if _, err := json.Marshal(&targetStruct{12}); err == nil { + t.Errorf("marshal should fail because of an invalid value") + } +} + +func TestEntries_Swap(t *testing.T) { + // given + entry1 := Entry{Title: "1"} + entry2 := Entry{Title: "2"} + entries := Entries{entry1, entry2} + + // when + entries.Swap(0, 1) + + // then + assert.Equal(t, "2", entries[0].Title) + assert.Equal(t, "1", entries[1].Title) +} + +func testMarshalJSON(t *testing.T, target Target, expected string) { + value := marshal(t, target) + if value != expected { + t.Errorf("value %s is not the expected %s", value, expected) + } +} + +func marshal(t *testing.T, target Target) string { + test := targetStruct{target} + json, err := json.Marshal(&test) + if err != nil { + t.Errorf("failed to marshal test struct: %v", err) + } + return string(json) +} + +type targetStruct struct { + Target Target +} + +func TestEntries_Len(t *testing.T) { + // given + entryA := Entry{} + entryB := Entry{} + entries := Entries{entryA, entryB} + + // when + length := entries.Len() + + // then + assert.Equal(t, 2, length) +} + +func TestEntries_Less(t *testing.T) { + // given + entryA := Entry{DisplayName: "A"} + entryB := Entry{DisplayName: "B"} + entries := Entries{entryA, entryB} + + // when + result := entries.Less(0, 1) + + // then + assert.True(t, result) +} diff --git a/controllers/warp/types/mocks/DoguConverter.go b/controllers/warp/types/mocks/DoguConverter.go new file mode 100644 index 0000000..4c69629 --- /dev/null +++ b/controllers/warp/types/mocks/DoguConverter.go @@ -0,0 +1,35 @@ +// Code generated by mockery v2.10.2. DO NOT EDIT. + +package mocks + +import ( + registry "github.com/cloudogu/cesapp-lib/registry" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" + mock "github.com/stretchr/testify/mock" +) + +// DoguConverter is an autogenerated mock type for the DoguConverter type +type DoguConverter struct { + mock.Mock +} + +// readAndUnmarshalDogu provides a mock function with given fields: _a0, key, tag +func (_m *DoguConverter) ReadAndUnmarshalDogu(_a0 registry.WatchConfigurationContext, key string, tag string) (types.EntryWithCategory, error) { + ret := _m.Called(_a0, key, tag) + + var r0 types.EntryWithCategory + if rf, ok := ret.Get(0).(func(registry.WatchConfigurationContext, string, string) types.EntryWithCategory); ok { + r0 = rf(_a0, key, tag) + } else { + r0 = ret.Get(0).(types.EntryWithCategory) + } + + var r1 error + if rf, ok := ret.Get(1).(func(registry.WatchConfigurationContext, string, string) error); ok { + r1 = rf(_a0, key, tag) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/controllers/warp/types/mocks/ExternalConverter.go b/controllers/warp/types/mocks/ExternalConverter.go new file mode 100644 index 0000000..db71946 --- /dev/null +++ b/controllers/warp/types/mocks/ExternalConverter.go @@ -0,0 +1,35 @@ +// Code generated by mockery v2.10.2. DO NOT EDIT. + +package mocks + +import ( + registry "github.com/cloudogu/cesapp-lib/registry" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" + mock "github.com/stretchr/testify/mock" +) + +// ExternalConverter is an autogenerated mock type for the ExternalConverter type +type ExternalConverter struct { + mock.Mock +} + +// ReadAndUnmarshalExternal provides a mock function with given fields: _a0, key +func (_m *ExternalConverter) ReadAndUnmarshalExternal(_a0 registry.WatchConfigurationContext, key string) (types.EntryWithCategory, error) { + ret := _m.Called(_a0, key) + + var r0 types.EntryWithCategory + if rf, ok := ret.Get(0).(func(registry.WatchConfigurationContext, string) types.EntryWithCategory); ok { + r0 = rf(_a0, key) + } else { + r0 = ret.Get(0).(types.EntryWithCategory) + } + + var r1 error + if rf, ok := ret.Get(1).(func(registry.WatchConfigurationContext, string) error); ok { + r1 = rf(_a0, key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go index c4a9dcc..7523260 100644 --- a/controllers/warp/warp.go +++ b/controllers/warp/warp.go @@ -6,6 +6,7 @@ import ( "github.com/cloudogu/cesapp-lib/core" "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/config" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" coreosclient "github.com/coreos/etcd/client" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/json" @@ -37,8 +38,10 @@ func NewWatcher(ctx context.Context, k8sClient client.Client, namespace string) } reader := &ConfigReader{ - registry: cesRegistry.RootConfig(), - configuration: warpConfig, + registry: cesRegistry.RootConfig(), + configuration: warpConfig, + doguConverter: &types.DoguConverter{}, + externalConverter: &types.ExternalConverter{}, } return &Watcher{ @@ -74,8 +77,13 @@ func (w *Watcher) Run() { }(source) } - for range warpChannel { - w.execute() + for { + select { + case <-w.doneChannel: + return + case <-warpChannel: + w.execute() + } } } diff --git a/controllers/warp/warp_test.go b/controllers/warp/warp_test.go index 7fec3b4..d0fd9e9 100644 --- a/controllers/warp/warp_test.go +++ b/controllers/warp/warp_test.go @@ -1,44 +1 @@ package warp_test - -import ( - "encoding/json" - "github.com/cloudogu/k8s-service-discovery/controllers/warp" - "testing" - - "fmt" - "github.com/stretchr/testify/assert" -) - -func TestCategoryString(t *testing.T) { - category := warp.Category{Title: "Hitchhiker"} - assert.Equal(t, "Hitchhiker", fmt.Sprintf("%v", category)) -} - -func TestTarget_MarshalJSON(t *testing.T) { - testMarshalJSON(t, warp.TARGET_EXTERNAL, "{\"Target\":\"external\"}") - testMarshalJSON(t, warp.TARGET_SELF, "{\"Target\":\"self\"}") - - if _, err := json.Marshal(&targetStruct{12}); err == nil { - t.Errorf("marshal should fail because of an invalid value") - } -} - -func testMarshalJSON(t *testing.T, target warp.Target, expected string) { - value := marshal(t, target) - if value != expected { - t.Errorf("value %s is not the expected %s", value, expected) - } -} - -func marshal(t *testing.T, target warp.Target) string { - test := targetStruct{target} - json, err := json.Marshal(&test) - if err != nil { - t.Errorf("failed to marshal test struct: %v", err) - } - return string(json) -} - -type targetStruct struct { - Target warp.Target -} diff --git a/controllers/warp_menu_creator_test.go b/controllers/warp_menu_creator_test.go new file mode 100644 index 0000000..e69384f --- /dev/null +++ b/controllers/warp_menu_creator_test.go @@ -0,0 +1,18 @@ +package controllers + +import ( + "github.com/stretchr/testify/require" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "testing" +) + +func TestNewWarpMenuCreator(t *testing.T) { + // given + client := fake.NewClientBuilder().Build() + + // when + underTest := NewWarpMenuCreator(client, "test") + + // then + require.NotNil(t, underTest) +} diff --git a/main_internal_test.go b/main_internal_test.go index a82ba4c..9e7a7a9 100644 --- a/main_internal_test.go +++ b/main_internal_test.go @@ -95,10 +95,12 @@ func Test_startManager(t *testing.T) { t.Run("Error on missing namespace environment variable", func(t *testing.T) { // given + err := os.Unsetenv("WATCH_NAMESPACE") + require.NoError(t, err) getNewMockManager(nil, defaultMockDefinitions) // when - err := startManager() + err = startManager() // then require.Error(t, err) From dd44975da5ab1f8b3f866a97244b39d6e25c2884 Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 2 Jun 2022 16:28:34 +0200 Subject: [PATCH 08/19] #3 More unit tests --- controllers/warp/configReader.go | 23 ++-- controllers/warp/configReader_test.go | 20 +-- controllers/warp/mocks/Reader.go | 38 +++++ controllers/warp/testdata/k8s_config.yaml | 33 +++++ controllers/warp/testdata/k8s_menu_cm.yaml | 7 + controllers/warp/warp.go | 59 ++++---- controllers/warp/warp_test.go | 153 ++++++++++++++++++++- controllers/warp_menu_creator.go | 9 +- controllers/warp_menu_creator_test.go | 2 +- go.mod | 2 +- main.go | 22 ++- 11 files changed, 302 insertions(+), 66 deletions(-) create mode 100644 controllers/warp/mocks/Reader.go create mode 100644 controllers/warp/testdata/k8s_config.yaml create mode 100644 controllers/warp/testdata/k8s_menu_cm.yaml diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go index 6a9e499..e907e6c 100644 --- a/controllers/warp/configReader.go +++ b/controllers/warp/configReader.go @@ -7,7 +7,6 @@ import ( "github.com/cloudogu/k8s-service-discovery/controllers/config" "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" "log" - "sort" "github.com/pkg/errors" @@ -21,19 +20,19 @@ type ConfigReader struct { externalConverter ExternalConverter } -// DoguConverter is used to read dogus from the registry and convert them to objects fitting in the warp menu +// DoguConverter is used to Read dogus from the registry and convert them to objects fitting in the warp menu type DoguConverter interface { ReadAndUnmarshalDogu(registry registry.WatchConfigurationContext, key string, tag string) (types.EntryWithCategory, error) } -// ExternalConverter is used to read external links from the registry and convert them to objects fitting in the warp menu +// ExternalConverter is used to Read external links from the registry and convert them to objects fitting in the warp menu type ExternalConverter interface { ReadAndUnmarshalExternal(registry registry.WatchConfigurationContext, key string) (types.EntryWithCategory, error) } const disableWarpSupportEntriesConfigurationKey = "/config/_global/disabled_warpmenu_support_entries" -func (reader *ConfigReader) readFromConfig(configuration *config.Configuration) (types.Categories, error) { +func (reader *ConfigReader) Read(configuration *config.Configuration) (types.Categories, error) { var data types.Categories for _, source := range configuration.Sources { @@ -44,15 +43,15 @@ func (reader *ConfigReader) readFromConfig(configuration *config.Configuration) categories, err := reader.readSource(source) if err != nil { - log.Println("Error during read:", err) + log.Println("Error during Read:", err) } data.InsertCategories(categories) } - log.Println("read SupportEntries") + log.Println("Read SupportEntries") disabledSupportEntries, err := reader.getDisabledSupportIdentifiers() if err != nil { - log.Println("Error during support read:", err) + log.Println("Error during support Read:", err) } supportCategory := reader.readSupport(configuration.Support, disabledSupportEntries) data.InsertCategories(supportCategory) @@ -70,10 +69,10 @@ func (reader *ConfigReader) readSource(source config.Source) (types.Categories, } func (reader *ConfigReader) externalsReader(source config.Source) (types.Categories, error) { - log.Printf("read externals from %s for warp menu", source.Path) + log.Printf("Read externals from %s for warp menu", source.Path) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { - return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) + return nil, fmt.Errorf("failed to Read root entry %s from etcd: %w", source.Path, err) } externals := []types.EntryWithCategory{} for _, child := range resp { @@ -88,10 +87,10 @@ func (reader *ConfigReader) externalsReader(source config.Source) (types.Categor // dogusReader reads from etcd and converts the keys and values to a warp menu // conform structure func (reader *ConfigReader) dogusReader(source config.Source) (types.Categories, error) { - log.Printf("read dogus from %s for warp menu", source.Path) + log.Printf("Read dogus from %s for warp menu", source.Path) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { - return nil, fmt.Errorf("failed to read root entry %s from etcd: %w", source.Path, err) + return nil, fmt.Errorf("failed to Read root entry %s from etcd: %w", source.Path, err) } dogus := []types.EntryWithCategory{} for _, path := range resp { @@ -107,7 +106,7 @@ func (reader *ConfigReader) dogusReader(source config.Source) (types.Categories, func (reader *ConfigReader) getDisabledSupportIdentifiers() ([]string, error) { disabledSupportEntries, err := reader.registry.Get(disableWarpSupportEntriesConfigurationKey) if err != nil { - return []string{}, fmt.Errorf("failed to read configuration entry %s from etcd: %w", disableWarpSupportEntriesConfigurationKey, err) + return []string{}, fmt.Errorf("failed to Read configuration entry %s from etcd: %w", disableWarpSupportEntriesConfigurationKey, err) } var disabledEntries []string diff --git a/controllers/warp/configReader_test.go b/controllers/warp/configReader_test.go index 2455e6f..b89f87d 100644 --- a/controllers/warp/configReader_test.go +++ b/controllers/warp/configReader_test.go @@ -91,7 +91,7 @@ func TestConfigReader_getDisabledSupportIdentifiers(t *testing.T) { // then require.Error(t, err) - assert.Contains(t, err.Error(), "failed to read configuration entry /config/_global/disabled_warpmenu_support_entries from etcd") + assert.Contains(t, err.Error(), "failed to Read configuration entry /config/_global/disabled_warpmenu_support_entries from etcd") mock.AssertExpectationsForObjects(t, mockRegistry) }) @@ -135,7 +135,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { } // when - actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + actual, err := reader.Read(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) // then assert.Empty(t, err) @@ -164,7 +164,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { } // when - actual, err := reader.readFromConfig(&config.Configuration{Sources: []config.Source{doguSource}, Support: testSupportSoureces}) + actual, err := reader.Read(&config.Configuration{Sources: []config.Source{doguSource}, Support: testSupportSoureces}) // then assert.Empty(t, err) @@ -173,7 +173,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) }) - t.Run("error during external read should not result in an error", func(t *testing.T) { + t.Run("error during external Read should not result in an error", func(t *testing.T) { // given mockExternalConverter := &mocks.ExternalConverter{} mockExternalConverter.On("ReadAndUnmarshalExternal", mockRegistry, mock.Anything).Return(types.EntryWithCategory{}, assert.AnError) @@ -185,7 +185,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { } // when - actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + actual, err := reader.Read(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) // then assert.Empty(t, err) @@ -194,7 +194,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { mock.AssertExpectationsForObjects(t, mockRegistry, mockDoguConverter, mockExternalConverter) }) - t.Run("error during support read should not result in an error", func(t *testing.T) { + t.Run("error during support Read should not result in an error", func(t *testing.T) { // given mockRegistry := &cesmocks.WatchConfigurationContext{} mockRegistry.On("GetChildrenPaths", "/path/to/etcd/key").Return([]string{"/path/to/etcd/key"}, nil) @@ -209,7 +209,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { } // when - actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + actual, err := reader.Read(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) // then assert.Empty(t, err) @@ -233,7 +233,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { } // when - actual, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + actual, err := reader.Read(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) // then assert.Empty(t, err) @@ -250,7 +250,7 @@ func TestConfigReader_readFromConfig(t *testing.T) { registry: mockRegistry, } // when - _, err := reader.readFromConfig(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) + _, err := reader.Read(&config.Configuration{Sources: testSources, Support: testSupportSoureces}) // then require.NoError(t, err) @@ -307,7 +307,7 @@ func TestConfigReader_dogusReader(t *testing.T) { // then require.Error(t, err) - assert.Contains(t, err.Error(), "failed to read root entry /dogu from etcd") + assert.Contains(t, err.Error(), "failed to Read root entry /dogu from etcd") mock.AssertExpectationsForObjects(t, mockRegistry) }) } diff --git a/controllers/warp/mocks/Reader.go b/controllers/warp/mocks/Reader.go new file mode 100644 index 0000000..b7082e1 --- /dev/null +++ b/controllers/warp/mocks/Reader.go @@ -0,0 +1,38 @@ +// Code generated by mockery v2.10.2. DO NOT EDIT. + +package mocks + +import ( + config "github.com/cloudogu/k8s-service-discovery/controllers/config" + mock "github.com/stretchr/testify/mock" + + types "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" +) + +// Reader is an autogenerated mock type for the Reader type +type Reader struct { + mock.Mock +} + +// read provides a mock function with given fields: configuration +func (_m *Reader) Read(configuration *config.Configuration) (types.Categories, error) { + ret := _m.Called(configuration) + + var r0 types.Categories + if rf, ok := ret.Get(0).(func(*config.Configuration) types.Categories); ok { + r0 = rf(configuration) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Categories) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*config.Configuration) error); ok { + r1 = rf(configuration) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/controllers/warp/testdata/k8s_config.yaml b/controllers/warp/testdata/k8s_config.yaml new file mode 100644 index 0000000..044d957 --- /dev/null +++ b/controllers/warp/testdata/k8s_config.yaml @@ -0,0 +1,33 @@ +# +# The default configuration map for the warp menu. +# +apiVersion: v1 +kind: ConfigMap +metadata: + name: k8s-ces-warp-config + namespace: test + labels: + app: cloudogu-ecosystem +data: + warp: | + sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries + target: /var/www/html/warp/menu.json + order: + Development Apps: 100 + support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ diff --git a/controllers/warp/testdata/k8s_menu_cm.yaml b/controllers/warp/testdata/k8s_menu_cm.yaml new file mode 100644 index 0000000..b359715 --- /dev/null +++ b/controllers/warp/testdata/k8s_menu_cm.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: k8s-ces-menu-json + namespace: test +data: + menu.json: '[]' diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go index 7523260..7602ba5 100644 --- a/controllers/warp/warp.go +++ b/controllers/warp/warp.go @@ -3,14 +3,15 @@ package warp import ( "context" "fmt" - "github.com/cloudogu/cesapp-lib/core" + + ctrl "sigs.k8s.io/controller-runtime" + "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/config" "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" coreosclient "github.com/coreos/etcd/client" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/json" - "log" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -20,25 +21,24 @@ type Watcher struct { configuration *config.Configuration registryToWatch registry.WatchConfigurationContext k8sClient client.Client - configReader *ConfigReader + ConfigReader Reader namespace string - doneChannel <-chan struct{} +} + +// Reader is used to fetch warp categories with a configuration +type Reader interface { + Read(configuration *config.Configuration) (types.Categories, error) } // NewWatcher creates a new Watcher instance to build the warp menu -func NewWatcher(ctx context.Context, k8sClient client.Client, namespace string) (*Watcher, error) { +func NewWatcher(ctx context.Context, k8sClient client.Client, registry registry.Registry, namespace string) (*Watcher, error) { warpConfig, err := config.ReadConfiguration(ctx, k8sClient, namespace) if err != nil { - return nil, fmt.Errorf("failed to read configuration: %w", err) - } - - cesRegistry, err := createEtcdRegistry(namespace) - if err != nil { - return nil, fmt.Errorf("failed to create etcd registry: %w", err) + return nil, fmt.Errorf("failed to Read configuration: %w", err) } reader := &ConfigReader{ - registry: cesRegistry.RootConfig(), + registry: registry.RootConfig(), configuration: warpConfig, doguConverter: &types.DoguConverter{}, externalConverter: &types.ExternalConverter{}, @@ -46,40 +46,29 @@ func NewWatcher(ctx context.Context, k8sClient client.Client, namespace string) return &Watcher{ configuration: warpConfig, - registryToWatch: cesRegistry.RootConfig(), + registryToWatch: registry.RootConfig(), k8sClient: k8sClient, namespace: namespace, - configReader: reader, - doneChannel: ctx.Done(), + ConfigReader: reader, }, nil } -func createEtcdRegistry(namespace string) (registry.Registry, error) { - r, err := registry.New(core.Registry{ - Type: "etcd", - Endpoints: []string{fmt.Sprintf("http://etcd.%s.svc.cluster.local:4001", namespace)}, - }) - - return r, err -} - // Run creates the warp menu and update the menu whenever a relevant etcd key was changed -func (w *Watcher) Run() { - log.Println("start watcher for warp entries") +func (w *Watcher) Run(ctx context.Context) { warpChannel := make(chan *coreosclient.Response) for _, source := range w.configuration.Sources { go func(source config.Source) { - for { - w.execute() - w.registryToWatch.Watch(source.Path, true, warpChannel, w.doneChannel) - } + w.execute() + ctrl.LoggerFrom(ctx).Info(fmt.Sprintf("start etcd watcher for source [%s]", source)) + w.registryToWatch.Watch(ctx, source.Path, true, warpChannel) + ctrl.LoggerFrom(ctx).Info(fmt.Sprintf("stop etcd watcher for source [%s]", source)) }(source) } for { select { - case <-w.doneChannel: + case <-ctx.Done(): return case <-warpChannel: w.execute() @@ -88,15 +77,15 @@ func (w *Watcher) Run() { } func (w *Watcher) execute() { - categories, err := w.configReader.readFromConfig(w.configuration) + categories, err := w.ConfigReader.Read(w.configuration) if err != nil { - log.Println("Error during read:", err) + ctrl.Log.Info("Error during Read:", err) return } - log.Printf("all found Categories: %v", categories) + ctrl.Log.Info("all found Categories: %v", categories) err = w.jsonWriter(categories) if err != nil { - log.Printf("failed to write warp menu as json: %v", err) + ctrl.Log.Info("failed to write warp menu as json: %v", err) } } diff --git a/controllers/warp/warp_test.go b/controllers/warp/warp_test.go index d0fd9e9..2388cd7 100644 --- a/controllers/warp/warp_test.go +++ b/controllers/warp/warp_test.go @@ -1 +1,152 @@ -package warp_test +package warp + +import ( + "context" + _ "embed" + cesmocks "github.com/cloudogu/cesapp-lib/registry/mocks" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/mocks" + "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" + coreosclient "github.com/coreos/etcd/client" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "os" + client2 "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/yaml" + "testing" + "time" +) + +//go:embed testdata/k8s_config.yaml +var configBytes []byte +var k8sConfig corev1.ConfigMap + +//go:embed testdata/k8s_menu_cm.yaml +var menuConfigMapBytes []byte +var menuConfigMap corev1.ConfigMap + +func init() { + err := yaml.Unmarshal(configBytes, &k8sConfig) + if err != nil { + panic(err) + } + + err = yaml.Unmarshal(menuConfigMapBytes, &menuConfigMap) + if err != nil { + panic(err) + } +} + +func TestNewWatcher(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + ctx := context.TODO() + client := fake.NewClientBuilder().Build() + err := client.Create(ctx, &k8sConfig) + require.NoError(t, err) + namespace := "test" + mockRegistry := &cesmocks.Registry{} + watchRegistry := &cesmocks.WatchConfigurationContext{} + mockRegistry.On("RootConfig").Return(watchRegistry) + err = os.Unsetenv("STAGE") + require.NoError(t, err) + + // when + watcher, err := NewWatcher(ctx, client, mockRegistry, namespace) + + // then + require.NoError(t, err) + assert.NotNil(t, watcher) + }) + + t.Run("fail to create configuration", func(t *testing.T) { + // given + err := os.Setenv("STAGE", "development") + require.NoError(t, err) + defer func() { + err := os.Unsetenv("STAGE") + if err != nil { + panic(err) + } + }() + client := fake.NewClientBuilder().Build() + + // when + _, err = NewWatcher(context.TODO(), client, nil, "test") + + // then + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to Read configuration") + }) +} + +func TestWatcher_Run(t *testing.T) { + t.Run("success with 3 sources and one refresh", func(t *testing.T) { + // given + // prepare deadline for watch + timeout := time.Second * 2 + ctx, cancel := context.WithTimeout(context.TODO(), timeout) + timer := time.NewTimer(timeout) + go func() { + if <-timer.C; true { + cancel() + } + }() + + // create the config with 3 sources and an empty menu json configmap + client := fake.NewClientBuilder().Build() + k8sConfig.ResourceVersion = "" + err := client.Create(ctx, &k8sConfig) + require.NoError(t, err) + err = client.Create(ctx, &menuConfigMap) + require.NoError(t, err) + err = os.Unsetenv("STAGE") + require.NoError(t, err) + + // prepare mocks + namespace := "test" + mockRegistry := &cesmocks.Registry{} + watchRegistry := &cesmocks.WatchConfigurationContext{} + watchEvent := &coreosclient.Response{} + mockRegistry.On("RootConfig").Return(watchRegistry) + watchRegistry.On("Watch", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + warpChannel := args.Get(3).(chan *coreosclient.Response) + warpChannel <- watchEvent + }).Times(3) + + watcher, err := NewWatcher(ctx, client, mockRegistry, namespace) + require.NoError(t, err) + + // prepare result categories + expectedEntry := types.Entry{ + DisplayName: "Redmine", + Href: "/redmine", + Title: "Redmine", + Target: types.TARGET_SELF, + } + expectedEntries := types.Entries{expectedEntry} + expectedCategory := &types.Category{ + Title: "Development Apps", + Order: 100, + Entries: expectedEntries, + } + expectedCategories := types.Categories{expectedCategory} + expectedMenuJSON := "[{\"Title\":\"Development Apps\",\"Order\":100,\"Entries\":[{\"DisplayName\":\"Redmine\",\"Href\":\"/redmine\",\"Title\":\"Redmine\",\"Target\":\"self\"}]}]" + + readerMock := &mocks.Reader{} + readerMock.On("Read", mock.Anything).Return(expectedCategories, nil) + watcher.ConfigReader = readerMock + + // when + watcher.Run(ctx) + + // then + mock.AssertExpectationsForObjects(t, mockRegistry, watchRegistry, readerMock) + menuCm := &corev1.ConfigMap{} + err = client.Get(ctx, client2.ObjectKey{Name: "k8s-ces-menu-json", Namespace: "test"}, menuCm) + require.NoError(t, err) + assert.Equal(t, expectedMenuJSON, menuCm.Data["menu.json"]) + }) +} diff --git a/controllers/warp_menu_creator.go b/controllers/warp_menu_creator.go index 76da86a..405c262 100644 --- a/controllers/warp_menu_creator.go +++ b/controllers/warp_menu_creator.go @@ -3,6 +3,7 @@ package controllers import ( "context" "fmt" + "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/warp" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -10,13 +11,15 @@ import ( // WarpMenuCreator used to create warp menu type WarpMenuCreator struct { client client.Client + registry registry.Registry namespace string } // NewWarpMenuCreator initialises a creator object to start the warp menu creation -func NewWarpMenuCreator(client client.Client, namespace string) WarpMenuCreator { +func NewWarpMenuCreator(client client.Client, registry registry.Registry, namespace string) WarpMenuCreator { return WarpMenuCreator{ client: client, + registry: registry, namespace: namespace, } } @@ -29,12 +32,12 @@ func (wmc WarpMenuCreator) Start(ctx context.Context) error { // CreateWarpMenu reads the warp configuration and starts watchers to refresh the menu.json configmap // in background. func (wmc WarpMenuCreator) CreateWarpMenu(ctx context.Context) error { - warpWatcher, err := warp.NewWatcher(ctx, wmc.client, wmc.namespace) + warpWatcher, err := warp.NewWatcher(ctx, wmc.client, wmc.registry, wmc.namespace) if err != nil { return fmt.Errorf("failed to create warp menu watcher: %w", err) } - warpWatcher.Run() + warpWatcher.Run(ctx) return nil } diff --git a/controllers/warp_menu_creator_test.go b/controllers/warp_menu_creator_test.go index e69384f..474b2f2 100644 --- a/controllers/warp_menu_creator_test.go +++ b/controllers/warp_menu_creator_test.go @@ -11,7 +11,7 @@ func TestNewWarpMenuCreator(t *testing.T) { client := fake.NewClientBuilder().Build() // when - underTest := NewWarpMenuCreator(client, "test") + underTest := NewWarpMenuCreator(client, nil, "test") // then require.NotNil(t, underTest) diff --git a/go.mod b/go.mod index 735728e..46ef457 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cloudogu/k8s-service-discovery go 1.17 require ( - github.com/cloudogu/cesapp-lib v0.0.0-20220601121439-08fb7d1213c0 + github.com/cloudogu/cesapp-lib v0.0.0-20220602091030-2cc085a8c1b6 github.com/coreos/etcd v3.3.27+incompatible github.com/go-logr/logr v1.2.0 github.com/onsi/ginkgo v1.16.5 diff --git a/main.go b/main.go index 0b71cd8..8265776 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "flag" "fmt" + "github.com/cloudogu/cesapp-lib/core" + "github.com/cloudogu/cesapp-lib/registry" "os" "strconv" @@ -73,7 +75,12 @@ func startManager() error { return fmt.Errorf("failed to create ingress class creator: %w", err) } - if err = handleWarpMenuCreation(k8sManager, options.Namespace); err != nil { + registry, err := createEtcdRegistry(options.Namespace) + if err != nil { + return fmt.Errorf("failed to create registry: %w", err) + } + + if err = handleWarpMenuCreation(k8sManager, registry, options.Namespace); err != nil { return fmt.Errorf("failed to create warp menu creator: %w", err) } @@ -88,6 +95,15 @@ func startManager() error { return nil } +func createEtcdRegistry(namespace string) (registry.Registry, error) { + r, err := registry.New(core.Registry{ + Type: "etcd", + Endpoints: []string{fmt.Sprintf("http://etcd.%s.svc.cluster.local:4001", namespace)}, + }) + + return r, err +} + func configureManager(k8sManager manager.Manager, namespace string) error { if err := configureReconciler(k8sManager, namespace); err != nil { return fmt.Errorf("failed to configure reconciler: %w", err) @@ -177,8 +193,8 @@ func handleIngressClassCreation(k8sManager manager.Manager) error { return nil } -func handleWarpMenuCreation(k8sManager manager.Manager, namespace string) error { - warpMenuCreator := controllers.NewWarpMenuCreator(k8sManager.GetClient(), namespace) +func handleWarpMenuCreation(k8sManager manager.Manager, registry registry.Registry, namespace string) error { + warpMenuCreator := controllers.NewWarpMenuCreator(k8sManager.GetClient(), registry, namespace) if err := k8sManager.Add(warpMenuCreator); err != nil { return fmt.Errorf("failed to add warp menu creator as runnable to the manager: %w", err) From c7d430b68b6f5545e6387ef1054e090ae2224d6e Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 2 Jun 2022 16:33:05 +0200 Subject: [PATCH 09/19] #3 Refactor configReader.go --- controllers/warp/configReader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go index e907e6c..1336f74 100644 --- a/controllers/warp/configReader.go +++ b/controllers/warp/configReader.go @@ -74,7 +74,7 @@ func (reader *ConfigReader) externalsReader(source config.Source) (types.Categor if err != nil { return nil, fmt.Errorf("failed to Read root entry %s from etcd: %w", source.Path, err) } - externals := []types.EntryWithCategory{} + var externals []types.EntryWithCategory for _, child := range resp { external, err := reader.externalConverter.ReadAndUnmarshalExternal(reader.registry, child) if err == nil { @@ -92,7 +92,7 @@ func (reader *ConfigReader) dogusReader(source config.Source) (types.Categories, if err != nil { return nil, fmt.Errorf("failed to Read root entry %s from etcd: %w", source.Path, err) } - dogus := []types.EntryWithCategory{} + var dogus []types.EntryWithCategory for _, path := range resp { dogu, err := reader.doguConverter.ReadAndUnmarshalDogu(reader.registry, path, source.Tag) if err == nil && dogu.Entry.Title != "" { From 9d438138ad654d4f2c260c4974da9e2a17d17fbd Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 2 Jun 2022 16:34:19 +0200 Subject: [PATCH 10/19] #3 Add go.sum --- go.sum | 987 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 987 insertions(+) create mode 100644 go.sum diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b21c005 --- /dev/null +++ b/go.sum @@ -0,0 +1,987 @@ +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 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +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.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +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/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/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/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/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/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/cloudogu/cesapp-lib v0.0.0-20220601115748-e019150488aa h1:rYwF1EJajxwgkJnSG9QUiyvCRHzoYR/OFCgI3z6lE9E= +github.com/cloudogu/cesapp-lib v0.0.0-20220601115748-e019150488aa/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= +github.com/cloudogu/cesapp-lib v0.0.0-20220601121439-08fb7d1213c0 h1:F9rCf/1jw+o39lL6iv4zPoAkXaYdmUwVNb29PlsR9IM= +github.com/cloudogu/cesapp-lib v0.0.0-20220601121439-08fb7d1213c0/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= +github.com/cloudogu/cesapp-lib v0.0.0-20220602091030-2cc085a8c1b6 h1:88eqwzO2M5pTwTA2Zuv1d7vN+nAfa1lIth48FFlVZxs= +github.com/cloudogu/cesapp-lib v0.0.0-20220602091030-2cc085a8c1b6/go.mod h1:0MyWjwl7lkhvAfOOyC/PDrhnS73q6r+BPYNDhGMLkok= +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/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA= +github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/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 v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +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 v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= +github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/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/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +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/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= +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-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +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 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/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.28.0 h1:vGVfV9KrDTvWt5boZO0I19g2E3CsWfpPPKZM9dt3mEw= +github.com/prometheus/common v0.28.0/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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +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 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +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= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +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= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26 h1:aJo09LP94Ty48u8vDMEox1NHwZ72vgRCw7PyVXc9E6k= +github.com/stretchr/objx v0.2.1-0.20190415111823-35313a95ee26/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= +go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= +go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= +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/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +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-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-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-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= +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.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 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-20200423170343-7949de9c1215/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-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +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.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +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.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= +k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA= +k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= +k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= +k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4= +k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= +k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0= +k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= +k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4= +k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA= +k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8= +k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= +k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE= +k8s.io/component-base v0.23.0 h1:UAnyzjvVZ2ZR1lF35YwtNY6VMN94WtOnArcXBu34es8= +k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= +k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE= +k8s.io/utils v0.0.0-20211116205334-6203023598ed/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/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I= +sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= +sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= +sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From d904fe3339162d271eae03ea3c6fa851970401e8 Mon Sep 17 00:00:00 2001 From: Niklas Date: Fri, 3 Jun 2022 08:24:07 +0200 Subject: [PATCH 11/19] #3 Fix log of found categories --- controllers/warp/warp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go index 7602ba5..abc7c4c 100644 --- a/controllers/warp/warp.go +++ b/controllers/warp/warp.go @@ -82,7 +82,7 @@ func (w *Watcher) execute() { ctrl.Log.Info("Error during Read:", err) return } - ctrl.Log.Info("all found Categories: %v", categories) + ctrl.Log.Info(fmt.Sprintf("all found Categories: %v", categories)) err = w.jsonWriter(categories) if err != nil { ctrl.Log.Info("failed to write warp menu as json: %v", err) From 91a3dc5ffe3eab6250e1cf8ca0c14669d98af0fc Mon Sep 17 00:00:00 2001 From: Niklas Date: Fri, 3 Jun 2022 10:23:15 +0200 Subject: [PATCH 12/19] #3 Use k8s-create-temporary-resource instead of k8s-generate in jenkins pipeline because it doesn't delete the namespace template from the clusterrolebinding. --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2bbf56f..e8bd468 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -53,7 +53,7 @@ node('docker') { } stage('Generate k8s Resources') { - make 'k8s-generate' + make 'k8s-create-temporary-resource' archiveArtifacts 'target/*.yaml' } } From 2b7400b91e628316cf0de3c14b5ef9cf0f415019 Mon Sep 17 00:00:00 2001 From: Niklas Date: Fri, 3 Jun 2022 10:57:01 +0200 Subject: [PATCH 13/19] #3 Refactoring --- config/manager/manager.yaml | 1 - controllers/warp/configReader.go | 13 +++++++------ controllers/warp/types/category.go | 2 ++ controllers/warp/types/dogu_entry.go | 6 +++++- controllers/warp/types/external_entry.go | 2 ++ controllers/warp/warp.go | 2 +- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 412f6dd..75dee01 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -24,7 +24,6 @@ spec: args: - --leader-elect image: controller:latest - imagePullPolicy: Always env: - name: WATCH_NAMESPACE valueFrom: diff --git a/controllers/warp/configReader.go b/controllers/warp/configReader.go index 1336f74..30fc0a5 100644 --- a/controllers/warp/configReader.go +++ b/controllers/warp/configReader.go @@ -6,7 +6,7 @@ import ( "github.com/cloudogu/cesapp-lib/registry" "github.com/cloudogu/k8s-service-discovery/controllers/config" "github.com/cloudogu/k8s-service-discovery/controllers/warp/types" - "log" + ctrl "sigs.k8s.io/controller-runtime" "sort" "github.com/pkg/errors" @@ -32,6 +32,7 @@ type ExternalConverter interface { const disableWarpSupportEntriesConfigurationKey = "/config/_global/disabled_warpmenu_support_entries" +// Read reads sources specified in a configuration and build warp menu categories for them. func (reader *ConfigReader) Read(configuration *config.Configuration) (types.Categories, error) { var data types.Categories @@ -43,15 +44,15 @@ func (reader *ConfigReader) Read(configuration *config.Configuration) (types.Cat categories, err := reader.readSource(source) if err != nil { - log.Println("Error during Read:", err) + ctrl.Log.Info(fmt.Sprintf("Error during Read: %s", err.Error())) } data.InsertCategories(categories) } - log.Println("Read SupportEntries") + ctrl.Log.Info("Read SupportEntries") disabledSupportEntries, err := reader.getDisabledSupportIdentifiers() if err != nil { - log.Println("Error during support Read:", err) + ctrl.Log.Info(fmt.Sprintf("Error during support Read: %s", err.Error())) } supportCategory := reader.readSupport(configuration.Support, disabledSupportEntries) data.InsertCategories(supportCategory) @@ -69,7 +70,7 @@ func (reader *ConfigReader) readSource(source config.Source) (types.Categories, } func (reader *ConfigReader) externalsReader(source config.Source) (types.Categories, error) { - log.Printf("Read externals from %s for warp menu", source.Path) + ctrl.Log.Info(fmt.Sprintf("Read externals from %s for warp menu", source.Path)) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { return nil, fmt.Errorf("failed to Read root entry %s from etcd: %w", source.Path, err) @@ -87,7 +88,7 @@ func (reader *ConfigReader) externalsReader(source config.Source) (types.Categor // dogusReader reads from etcd and converts the keys and values to a warp menu // conform structure func (reader *ConfigReader) dogusReader(source config.Source) (types.Categories, error) { - log.Printf("Read dogus from %s for warp menu", source.Path) + ctrl.Log.Info(fmt.Sprintf("Read dogus from %s for warp menu", source.Path)) resp, err := reader.registry.GetChildrenPaths(source.Path) if err != nil { return nil, fmt.Errorf("failed to Read root entry %s from etcd: %w", source.Path, err) diff --git a/controllers/warp/types/category.go b/controllers/warp/types/category.go index 8bb576b..d19746e 100644 --- a/controllers/warp/types/category.go +++ b/controllers/warp/types/category.go @@ -29,12 +29,14 @@ func (c Categories) Swap(i, j int) { c[i], c[j] = c[j], c[i] } +// InsertCategories adds new categories to the slice. func (c *Categories) InsertCategories(newCategories Categories) { for _, newCategory := range newCategories { c.InsertCategory(newCategory) } } +// InsertCategory adds a new category to the slice. If the title are same the entries will be merged. func (c *Categories) InsertCategory(newCategory *Category) { for _, category := range *c { if category.Title == newCategory.Title { diff --git a/controllers/warp/types/dogu_entry.go b/controllers/warp/types/dogu_entry.go index 597788f..f097001 100644 --- a/controllers/warp/types/dogu_entry.go +++ b/controllers/warp/types/dogu_entry.go @@ -18,8 +18,11 @@ type doguEntry struct { Tags []string } +// DoguConverter converts dogus from the configuration to a warp menu category object type DoguConverter struct{} +// ReadAndUnmarshalDogu reads the dogu from the configuration. If it has the specific tag (or no tag) it will be +// converted to entry with a category for the warp menu func (dc *DoguConverter) ReadAndUnmarshalDogu(registry registry.WatchConfigurationContext, key string, tag string) (EntryWithCategory, error) { doguBytes, err := readDoguAsBytes(registry, key) if err != nil { @@ -103,7 +106,8 @@ func containsString(slice []string, item string) bool { } func isKeyNotFound(err error) bool { - if cErr, ok := err.(coreosclient.Error); ok { + var cErr coreosclient.Error + if ok := errors.Is(err, cErr); ok { return cErr.Code == coreosclient.ErrorCodeKeyNotFound } return false diff --git a/controllers/warp/types/external_entry.go b/controllers/warp/types/external_entry.go index a8ba7fd..9bb86c1 100644 --- a/controllers/warp/types/external_entry.go +++ b/controllers/warp/types/external_entry.go @@ -21,8 +21,10 @@ type EntryWithCategory struct { Category string } +// ExternalConverter is used to read external links from the configuration and convert them to a warp menu category object. type ExternalConverter struct{} +// ReadAndUnmarshalExternal reads a specific external link from the configuration and converts it to an entry with a category. func (ec *ExternalConverter) ReadAndUnmarshalExternal(registry registry.WatchConfigurationContext, key string) (EntryWithCategory, error) { externalBytes, err := readExternalAsBytes(registry, key) if err != nil { diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go index abc7c4c..4412bf2 100644 --- a/controllers/warp/warp.go +++ b/controllers/warp/warp.go @@ -82,7 +82,7 @@ func (w *Watcher) execute() { ctrl.Log.Info("Error during Read:", err) return } - ctrl.Log.Info(fmt.Sprintf("all found Categories: %v", categories)) + ctrl.Log.Info(fmt.Sprintf("All found Categories: %v", categories)) err = w.jsonWriter(categories) if err != nil { ctrl.Log.Info("failed to write warp menu as json: %v", err) From e737816e86197ccc43f58bc5f89937cd5a7a7642 Mon Sep 17 00:00:00 2001 From: Niklas Date: Fri, 3 Jun 2022 11:48:11 +0200 Subject: [PATCH 14/19] #3 Add documentation --- CHANGELOG.md | 7 +- docs/operations/warp_menu_generation_de.md | 92 ++++++++++++++++++++++ docs/operations/warp_menu_generation_en.md | 91 +++++++++++++++++++++ 3 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 docs/operations/warp_menu_generation_de.md create mode 100644 docs/operations/warp_menu_generation_en.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 547fb92..953af4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Warp menu generation + - Add runnable to the controller which observes keys in the etcd specified in a configmap `k8s-ces-warp-config` + and creates warp menu entries in `k8s-ces-menu-json` for the nginx-ingress dogu [#3]. + ### Changed -- Update makefiles to version 5.1.0 +- Update makefiles to version 6.0.1 [#3] ## [v0.1.0] - 2022-04-20 ### Added diff --git a/docs/operations/warp_menu_generation_de.md b/docs/operations/warp_menu_generation_de.md new file mode 100644 index 0000000..1ce5cbf --- /dev/null +++ b/docs/operations/warp_menu_generation_de.md @@ -0,0 +1,92 @@ +# Generierung der Einträge des Warp-Menüs + +Die `k8s-service-discovery` ist zuständig für die Generierung der `menu.json` des Warp-Menüs. +Dazu implementiert sie, ähnlich wie es `ces-confd` gemacht hat, einen Watch auf bestimmte Pfade im Etcd. +Bei einer Änderung z.B. einer Dogu-Installation generiert die `k8s-service-discovery` neue Einträge +und schreibt diese in die Configmap `k8s-ces-menu-json`. Diese Configmap wird von dem `nginx-ingress` +eingebunden und verwendet. + +## Konfiguration + +### Quellen + +Es ist möglich 3 Arten von Quellen für den Etcd-Watch anzugeben. + +#### Dogus +```yaml +sources: + - path: /dogu + type: dogus + tag: warp +``` + +#### Externe Links +```yaml +sources: + - path: /config/nginx/externals + type: externals +``` + +Externe Links müssen folgender Struktur (JSON-String) im Etcd entsprechen: + +``` +{ + "cloudogu": "{ + \"DisplayName\": \"Cloudogu\", + \"Description\": \"Beschreibungstext für Cloudogu Webseite\", + \"Category\": \"External Links\", + \"URL\": \"https://www.cloudogu.com/\" +}" +} +``` + +#### Einträge, die ausgeblendet werden sollen +```yaml +sources: + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries +``` + +Die Konfiguration beinhaltet außerdem Support-Links, die jedoch mit dem Typ `disabled_support_entries` ausgeblendet +werden können. Dazu muss im angegebenen Pfad ein String-Array im JSON-Format abgelegt werden. Die Einträge entsprechen +den Keys der Links. Zum Beispiel: +`'["docsCloudoguComUrl", "aboutCloudoguToken"]'` + +### Order +Mit der Kategorie `order` lassen sich die bestimmten Dogu-Kategorien aus der `dogu.json` im Warp-Menü sortieren. +Ein höherer Wert wird im Warp-Menü weiter oben angezeigt. + +### Support +Support Links stellen feste Links, welche im unteren Teil des Warp-Menüs angezeigt werden, dar. + +```yaml +support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ +``` + +### Standardkonfiguration +```yaml +sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries +target: /var/www/html/warp/menu.json +order: + Development Apps: 100 +support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ +``` \ No newline at end of file diff --git a/docs/operations/warp_menu_generation_en.md b/docs/operations/warp_menu_generation_en.md new file mode 100644 index 0000000..3d5a965 --- /dev/null +++ b/docs/operations/warp_menu_generation_en.md @@ -0,0 +1,91 @@ +# Generation of the entries of the warp menu + +The `k8s-service-discovery` is responsible for generating the `menu.json` of the warp menu. +For this it implements, similar to what `ces-confd` did, a watch on certain paths in the etcd. +When changing e.g. a Dogu installation the `k8s-service-discovery` generates new entries +and writes them to the configmap `k8s-ces-menu-json`. This configmap is included and used by the `nginx-ingress`. + +## Configuration + +### Sources + +It is possible to specify 3 types of sources for the etcd-watch. + +#### Dogus +```yaml +sources: +- path: /dogu + type: dogus + tag: warp +``` + +#### External links +```yaml +sources: + - path: /config/nginx/externals + type: externals +``` + +External links must match the following structure (JSON-String) in the etcd: + +``` +{ + "cloudogu": "{ + \"DisplayName\": \"Cloudogu\", + \"Description\": \"Beschreibungstext für Cloudogu Webseite\", + \"Category\": \"External Links\", + \"URL\": \"https://www.cloudogu.com/\" +}" +} +``` + +#### Entries to be hidden +```yaml +sources: + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries +``` + +The configuration also includes support links, but these can be hidden with the `disabled_support_entries` type. +To do this, a string array in JSON format must be stored in the specified path. The entries correspond to +the keys of the links. For example: +`'["docsCloudoguComUrl", "aboutCloudoguToken"]'` + +### Order +The `order` category can be used to sort the specific Dogu categories from the `dogu.json` in the warp menu. +A higher value will be displayed higher up in the warp menu. + +### Support +Support links represent fixed links that are displayed in the lower part of the warp menu. + +```yaml +support: +- identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ +``` + +### Default configuration +```yaml +sources: + - path: /dogu + type: dogus + tag: warp + - path: /config/nginx/externals + type: externals + - path: /config/_global/disabled_warpmenu_support_entries + type: disabled_support_entries +target: /var/www/html/warp/menu.json +order: + Development Apps: 100 +support: + - identifier: docsCloudoguComUrl + external: true + href: https://docs.cloudogu.com/ + - identifier: aboutCloudoguToken + external: false + href: /info/about + - identifier: myCloudogu + external: true + href: https://my.cloudogu.com/ +``` From c2a6e2ed8f4bb8d39488f9dc3ac9f5885cf18119 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 7 Jun 2022 08:27:42 +0200 Subject: [PATCH 15/19] #3 Fix KeyNotFound method --- controllers/warp/types/dogu_entry.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/controllers/warp/types/dogu_entry.go b/controllers/warp/types/dogu_entry.go index f097001..2020cf6 100644 --- a/controllers/warp/types/dogu_entry.go +++ b/controllers/warp/types/dogu_entry.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" "github.com/cloudogu/cesapp-lib/registry" - coreosclient "github.com/coreos/etcd/client" + "github.com/coreos/etcd/client" "strings" "github.com/pkg/errors" @@ -106,9 +106,8 @@ func containsString(slice []string, item string) bool { } func isKeyNotFound(err error) bool { - var cErr coreosclient.Error - if ok := errors.Is(err, cErr); ok { - return cErr.Code == coreosclient.ErrorCodeKeyNotFound + if cErr, ok := err.(client.Error); ok { + return cErr.Code == client.ErrorCodeKeyNotFound } return false } From e0b50aff4b1691369b6487247f5e908da9e45cbc Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 8 Jun 2022 10:45:02 +0200 Subject: [PATCH 16/19] #3 Add menu json configmap --- Makefile | 8 +++++++- k8s/k8s-ces-menu-json.yaml | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 k8s/k8s-ces-menu-json.yaml diff --git a/Makefile b/Makefile index 40f6049..4defec2 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ ADDITIONAL_CLEAN=dist-clean K8S_RESOURCE_DIR=${WORKDIR}/k8s K8S_WARP_CONFIG_RESOURCE_YAML=${K8S_RESOURCE_DIR}/k8s-ces-warp-config.yaml +K8S_WARP_MENU_JSON_YAML=${K8S_RESOURCE_DIR}/k8s-ces-menu-json.yaml include build/make/variables.mk include build/make/self-update.mk @@ -26,7 +27,7 @@ include build/make/digital-signature.mk K8S_RUN_PRE_TARGETS=setup-etcd-port-forward PRE_COMPILE=generate vet -K8S_PRE_GENERATE_TARGETS=k8s-create-temporary-resource generate-warp-config +K8S_PRE_GENERATE_TARGETS=k8s-create-temporary-resource generate-warp-config generate-menu-json include build/make/k8s-controller.mk @@ -52,4 +53,9 @@ generate-warp-config: @echo "---" >> $(K8S_RESOURCE_TEMP_YAML) @cat $(K8S_WARP_CONFIG_RESOURCE_YAML) >> $(K8S_RESOURCE_TEMP_YAML) +.PHONY: generate-menu-json +generate-menu-json: + @echo "---" >> $(K8S_RESOURCE_TEMP_YAML) + @cat $(K8S_WARP_MENU_JSON_YAML) >> $(K8S_RESOURCE_TEMP_YAML) + diff --git a/k8s/k8s-ces-menu-json.yaml b/k8s/k8s-ces-menu-json.yaml new file mode 100644 index 0000000..5311a89 --- /dev/null +++ b/k8s/k8s-ces-menu-json.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app: cloudogu-ecosystem + name: k8s-ces-menu-json +data: + menu.json: |- + [ + + ] From 9664d466c8559705c7ed6420c8d82458ba82b137 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 8 Jun 2022 18:51:31 +0200 Subject: [PATCH 17/19] #3 Use background context when updating the menu json configmap --- controllers/warp/warp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/warp/warp.go b/controllers/warp/warp.go index 4412bf2..d01b122 100644 --- a/controllers/warp/warp.go +++ b/controllers/warp/warp.go @@ -102,7 +102,7 @@ func (w *Watcher) jsonWriter(data interface{}) error { configmap.Data["menu.json"] = string(jsonData) - err = w.k8sClient.Update(context.TODO(), configmap) + err = w.k8sClient.Update(context.Background(), configmap) if err != nil { return fmt.Errorf("failed to update menu json config map: %w", err) } From a19adb1e0d010d94f26d3cd38026d88b2341a0bf Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 8 Jun 2022 19:12:05 +0200 Subject: [PATCH 18/19] Bump version --- Dockerfile | 2 +- Makefile | 2 +- config/manager/kustomization.yaml | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index fdf771c..594520c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN make compile-generic FROM gcr.io/distroless/static:nonroot LABEL maintainer="hello@cloudogu.com" \ NAME="k8s-service-discovery" \ - VERSION="0.1.0" + VERSION="0.2.0" WORKDIR / diff --git a/Makefile b/Makefile index 4defec2..5c97e47 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Set these to the desired values ARTIFACT_ID=k8s-service-discovery -VERSION=0.1.0 +VERSION=0.2.0 ## Image URL to use all building/pushing image targets IMAGE_DEV=${K3CES_REGISTRY_URL_PREFIX}/${ARTIFACT_ID}:${VERSION} diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 02823ce..d8a48a1 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,14 +1,14 @@ resources: -- manager.yaml + - manager.yaml generatorOptions: disableNameSuffixHash: true configMapGenerator: -- files: - - controller_manager_config.yaml - name: manager-config + - files: + - controller_manager_config.yaml + name: manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: -- name: controller - newName: cloudogu/k8s-service-discovery - newTag: 0.1.0 + - name: controller + newName: cloudogu/k8s-service-discovery + newTag: 0.2.0 From c52085a9532c372b6cb8753bea82284b776cf786 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 8 Jun 2022 19:12:08 +0200 Subject: [PATCH 19/19] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 953af4b..9a508fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [v0.2.0] - 2022-06-08 ### Added - Warp menu generation - Add runnable to the controller which observes keys in the etcd specified in a configmap `k8s-ces-warp-config`