diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index e33f0e70..00000000 --- a/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM golang:1.18-alpine AS builder - -ARG BUILD_ENVIRONMENT -ARG WEBHOOK_VERSION - -WORKDIR /go/src/app - -# download dependencies in a separate step to allow caching -COPY go.* . -RUN go mod download - -COPY . . -# cache the build -RUN --mount=type=cache,target=/root/.cache/go-build go build -tags $BUILD_ENVIRONMENT -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=$WEBHOOK_VERSION" -o webhook-datree - -FROM alpine:3.14 -COPY --from=builder /go/src/app/webhook-datree / -EXPOSE 8443 -ENTRYPOINT ["/webhook-datree"] diff --git a/Makefile b/Makefile index 4899dd82..dbc0899b 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,135 @@ -start-watch: - gow run -tags $(or $(datree_build_env),staging) -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=0.0.1" main.go - -start: - go run -tags $(or $(datree_build_env),staging) -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=0.0.1" main.go -start-dev: - make datree_build_env=dev start -start-staging: - make datree_build_env=staging start -start-production: - make datree_build_env=main start - -build: - go build -tags $(or $(datree_build_env),staging) -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=0.0.1" -o webhook-datree -build-dev: - make datree_build_env=dev build -build-staging: - make datree_build_env=staging build -build-production: - make datree_build_env=main build -test: - DATREE_ENFORCE="true" go test ./... +################# +# DEFAULTS # +################# -deploy-in-minikube: - bash ./scripts/deploy-in-minikube.sh +CMD_DIR := ./cmd +INIT_WEBHOOK_DIR := $(CMD_DIR)/init-webhook +CERT_GENERATOR_DIR := $(CMD_DIR)/cert-generator +WEBHOOK_SERVER_DIR := $(CMD_DIR)/webhook-server +WEBHOOK_VERSION := 0.0.1 +LD_FLAGS := "-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=$(WEBHOOK_VERSION)" +BUILD_ARGS_ENV ?= staging +BUILD_ARGS_DIR ?= $(WEBHOOK_SERVER_DIR) +BUILD_ARGS_OUTPUT ?= webhook-server + + +################# +# RUN # +################# + +_runner: + go run -tags ${BUILD_ARGS_ENV} -ldflags=$(LD_FLAGS) $(BUILD_ARGS_DIR) + +run-cert-generator-%: + $(MAKE) _runner \ + -e BUILD_ARGS_DIR=$(CERT_GENERATOR_DIR) \ + -e BUILD_ARGS_ENV="$*" + +run-init-webhook-%: + $(MAKE) _runner \ + -e BUILD_ARGS_DIR=$(INIT_WEBHOOK_DIR) \ + -e BUILD_ARGS_ENV="$*" + +run-webhook-server-%: + $(MAKE) _runner \ + -e BUILD_ARGS_DIR=$(WEBHOOK_SERVER_DIR) \ + -e BUILD_ARGS_ENV="$*" + +.PHONY: run-in-minikube run-in-minikube: bash ./scripts/run-in-minikube.sh + +################# +# TEST # +################# + +test: + DATREE_ENFORCE="true" go test ./... + +.PHONY: test-in-minikube test-in-minikube: bash ./scripts/test-in-minikube.sh + +################## +# BUILD # +################## +_builder: + docker build -t ${BUILD_ARGS_OUTPUT} -f $(BUILD_ARGS_DIR)/Dockerfile . --build-arg BUILD_ENVIRONMENT=${BUILD_ARGS_ENV} + +build-cert-generator-%: + $(MAKE) _builder \ + -e BUILD_ARGS_DIR=$(CERT_GENERATOR_DIR) \ + -e BUILD_ARGS_ENV="$*" \ + -e BUILD_ARGS_OUTPUT="cert-generator" + +build-init-webhook-%: + $(MAKE) _builder \ + -e BUILD_ARGS_DIR=$(INIT_WEBHOOK_DIR) \ + -e BUILD_ARGS_ENV="$*" \ + -e BUILD_ARGS_OUTPUT="init-webhook" + +build-webhook-server-%: + $(MAKE) _builder \ + -e BUILD_ARGS_DIR=$(WEBHOOK_SERVER_DIR) \ + -e BUILD_ARGS_ENV="$*" \ + -e BUILD_ARGS_OUTPUT="webhook-server" + +################# +# DEPLOY # +################# + +.PHONY: deploy-in-minikube +deploy-in-minikube: + bash ./scripts/deploy-in-minikube.sh + + +deploy-webhook-server: + $(eval IMAGE_TAG := $(shell yq '.image.tag' ./charts/datree-admission-webhook-awsmp/values.yaml | awk -F. '{$$NF = $$NF + 1;} 1' | sed 's/ /./g')) + $(MAKE) build-webhook-server-awsmp + docker tag webhook-server 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-admission-webhook:$(IMAGE_TAG) + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 709825985650.dkr.ecr.us-east-1.amazonaws.com + docker push 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-admission-webhook:${IMAGE_TAG} + tag=${IMAGE_TAG} yq e --inplace '.image."tag" |= strenv(tag)' ./charts/datree-admission-webhook-awsmp/values.yaml + + +deploy-init-webhook: + $(eval IMAGE_TAG := $(shell yq '.imageWebhook.tag' ./charts/datree-admission-webhook-awsmp/values.yaml | awk -F. '{$$NF = $$NF + 1;} 1' | sed 's/ /./g')) + $(MAKE) build-init-webhook-awsmp + docker tag init-webhook 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/init-webhook:$(IMAGE_TAG) + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 709825985650.dkr.ecr.us-east-1.amazonaws.com + docker push 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/init-webhook:${IMAGE_TAG} + tag=${IMAGE_TAG} yq e --inplace '.imageWebhook."tag" |= strenv(tag)' ./charts/datree-admission-webhook-awsmp/values.yaml + +deploy-cert-generator: + $(eval IMAGE_TAG := $(shell yq '.initContainer.tag' ./charts/datree-admission-webhook-awsmp/values.yaml | awk -F. '{$$NF = $$NF + 1;} 1' | sed 's/ /./g')) + $(MAKE) build-cert-generator-awsmp + docker tag cert-generator 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/cert-generator:$(IMAGE_TAG) + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 709825985650.dkr.ecr.us-east-1.amazonaws.com + docker push 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/cert-generator:${IMAGE_TAG} + tag=${IMAGE_TAG} yq e --inplace '.initContainer."tag" |= strenv(tag)' ./charts/datree-admission-webhook-awsmp/values.yaml + +deploy-datree-awsmp: + $(MAKE) deploy-cert-generator + $(MAKE) deploy-init-webhook + $(MAKE) deploy-webhook-server + +# verify that the chart is valid + helm lint ./charts/datree-admission-webhook-awsmp + +# bump Helm Chart version + $(eval HELM_CHART_VERSION=$(shell yq '.version' ./charts/datree-admission-webhook-awsmp/Chart.yaml | awk -F. '{$$NF = $$NF + 1;} 1' | sed 's/ /./g')) + version=${HELM_CHART_VERSION} yq e --inplace '."version" |= strenv(version)' ./charts/datree-admission-webhook-awsmp/Chart.yaml + +# helm push chart to ECR + helm package ./charts/datree-admission-webhook-awsmp + aws ecr get-login-password --region us-east-1 | helm login --username AWS --password-stdin 000000000000.dkr.ecr.us-east-1.amazonaws.com + helm push datree-admission-webhook-awsmp-${HELM_CHART_VERSION}.tgz 000000000000.dkr.ecr.us-east-1.amazonaws.com + + +################# +# HELM # +################# helm-install-local-in-minikube: eval $(minikube docker-env) && \ @@ -42,4 +144,4 @@ helm-uninstall: helm-install-staging: helm install -n datree datree-webhook ./charts/datree-admission-webhook --set datree.token="${DATREE_TOKEN}" --set scan_job.image.repository="datree/scan-job-staging" \ - --set scan_job.image.tag="latest" --set image.repository="datree/webhook-staging" --set image.tag="latest" + --set scan_job.image.tag="latest" --set image.repository="datree/webhook-staging" --set image.tag="latest" \ No newline at end of file diff --git a/charts/datree-admission-webhook-awsmp/Chart.lock b/charts/datree-admission-webhook-awsmp/Chart.lock new file mode 100644 index 00000000..bf37aaf0 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: datree-lib + repository: file://../datree-lib + version: 0.1.1 +digest: sha256:a65b157f4e3e466bfc9a80df08a319a3f61abdb84e4a29dfbe18127dd11edac6 +generated: "2022-10-18T15:06:55.309464+03:00" diff --git a/charts/datree-admission-webhook-awsmp/Chart.yaml b/charts/datree-admission-webhook-awsmp/Chart.yaml new file mode 100644 index 00000000..4f20ea58 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/Chart.yaml @@ -0,0 +1,31 @@ +apiVersion: v2 +name: datree-admission-webhook-awsmp +description: A Helm chart for Datree admission webhook for Kubernetes clusters +icon: https://github.com/datreeio/admission-webhook-datree/blob/main/internal/images/diagram.png +type: application +keywords: + - datree-admission-webhook + - policy agent + - validating webhook + - admissions controller +home: datree.io +sources: + - https://github.com/datreeio/admission-webhook-datree + +kubeVersion: ">=1.16.0-0" + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 1.0.2-rc.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.1.24-rc.2" + +dependencies: + - name: datree-lib + version: 0.1.1 + repository: file://../datree-lib diff --git a/charts/datree-admission-webhook-awsmp/charts/datree-lib-0.1.1.tgz b/charts/datree-admission-webhook-awsmp/charts/datree-lib-0.1.1.tgz new file mode 100644 index 00000000..6f151868 Binary files /dev/null and b/charts/datree-admission-webhook-awsmp/charts/datree-lib-0.1.1.tgz differ diff --git a/charts/datree-admission-webhook-awsmp/templates/_helpers.tpl b/charts/datree-admission-webhook-awsmp/templates/_helpers.tpl new file mode 100644 index 00000000..c1070895 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/_helpers.tpl @@ -0,0 +1,25 @@ +{{/* Create chart name and version as used by the chart label. */}} +{{- define "datree.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{/* Helm and Kubernetes required labels */}} +{{- define "datree.labels" -}} +app.kubernetes.io/name: {{.Chart.Name}} +app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +app.kubernetes.io/instance: {{ .Release.Name | quote }} +app.kubernetes.io/version: {{ .Chart.AppVersion }} +app.kubernetes.io/part-of: "datree" +meta.helm.sh/release-name: "{{ .Chart.Name }}" +meta.helm.sh/release-namespace: "{{ .Release.Namespace}}" +helm.sh/chart: {{ template "datree.chart" . }} + {{- if .Values.customLabels -}} + {{ toYaml .Values.customLabels }} + {{- end -}} +{{- end -}} + +{{/* The namespace name. */}} +{{- define "datree.namespace" -}} +{{- default .Release.Namespace .Values.namespace -}} +{{- end -}} \ No newline at end of file diff --git a/charts/datree-admission-webhook-awsmp/templates/cleanup-pre-delete.yaml b/charts/datree-admission-webhook-awsmp/templates/cleanup-pre-delete.yaml new file mode 100644 index 00000000..1f05f7f3 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/cleanup-pre-delete.yaml @@ -0,0 +1,35 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: datree-cleanup-namespaces-hook-pre-delete + labels: {{include "datree.labels" . | nindent 4}} + namespace: {{template "datree.namespace" .}} + annotations: + "helm.sh/hook": pre-delete, pre-upgrade + "helm.sh/hook-delete-policy": hook-succeeded, hook-failed + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations }} + {{- end }} +spec: + template: + metadata: + labels: {{include "datree.labels" . | nindent 8}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} + spec: + restartPolicy: OnFailure + serviceAccount: datree-cleanup-namespaces-hook-pre-delete + nodeSelector: + kubernetes.io/os: linux + containers: + - name: kubectl-label + image: "{{ .Values.hooks.image.repository }}@{{ .Values.hooks.image.sha }}" + imagePullPolicy: {{.Values.hooks.image.pullPolicy}} + command: + - sh + - "-c" + - >- + kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io datree-webhook -n {{template "datree.namespace" .}}; + kubectl delete deployment datree-webhook-server -n {{template "datree.namespace" .}}; + kubectl label ns kube-system {{template "datree.namespace" .}} datree.io/skip-; \ No newline at end of file diff --git a/charts/datree-admission-webhook-awsmp/templates/clusterrole.yaml b/charts/datree-admission-webhook-awsmp/templates/clusterrole.yaml new file mode 100644 index 00000000..e6a522f6 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/clusterrole.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.clusterrole" .}} diff --git a/charts/datree-admission-webhook-awsmp/templates/clusterrolebinding.yaml b/charts/datree-admission-webhook-awsmp/templates/clusterrolebinding.yaml new file mode 100644 index 00000000..266fd9e9 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/clusterrolebinding.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.clusterrolebinding" .}} diff --git a/charts/datree-admission-webhook-awsmp/templates/deployment.yaml b/charts/datree-admission-webhook-awsmp/templates/deployment.yaml new file mode 100644 index 00000000..8065f9c7 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/deployment.yaml @@ -0,0 +1,141 @@ +{{- $uuidv4RegexPattern := "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: datree-webhook-server + namespace: {{ template "datree.namespace" . }} + labels: {{ include "datree.labels" . | nindent 4 }} + owner: datree + app: "datree-webhook-server" + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-weight": "5" + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: "datree-webhook-server" + template: + metadata: + labels: {{ include "datree.labels" . | nindent 8 }} + app: "datree-webhook-server" + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} + spec: + serviceAccountName: {{.Values.rbac.serviceAccount.name}} + containers: + - name: webhook-init + image: "{{ .Values.imageWebhook.repository }}:{{ .Values.imageWebhook.tag | default .Chart.AppVersion }}" + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + env: + - name: WEBHOOK_NAMESPACE + value: {{ template "datree.namespace" . }} + - name: WEBHOOK_SERVICE + value: "datree-webhook-server" + - name: WEBHOOK_POD_SELECTOR + value: "app=datree-webhook-server" + - name: WEBHOOK_SELECTOR + value: "admission.datree/validate" + - name: WEBHOOK_SERVER_REPLICAS + value: "{{ .Values.replicaCount }}" + - name: server + securityContext: {{- toYaml .Values.securityContext | nindent 12 }} + livenessProbe: + httpGet: + path: /health + port: 8443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 10 + resources: {{- toYaml .Values.resources | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{.Values.image.pullPolicy}} + ports: + - containerPort: 8443 + name: webhook-api + # caution: don't change the order of the environment variables + # changing the order will harm resource patching + env: + - name: DEBUG + value: "{{ .Values.debug }}" + # Datree webhook varaibles + - name: DATREE_TOKEN + value: {{.Values.datree.token | regexFind $uuidv4RegexPattern}} + - name: DATREE_POLICY + value: {{.Values.datree.policy | default "Starter" }} + - name: DATREE_VERBOSE + value: {{.Values.datree.verbose}} + - name: DATREE_OUTPUT + value: {{.Values.datree.output}} + - name: DATREE_NO_RECORD + value: {{.Values.datree.noRecord}} + - name: DATREE_ENFORCE + value: "{{.Values.datree.enforce}}" + # AWS Marketplace varaibles + - name: AWS_MP_PRODUCT_ID + value: {{ .Values.aws.productId }} + - name: AWS_MP_KEY_FINGERPRINT + value: {{ .Values.aws.issuerKey }} + - name: AWS_MP_ENABLE_CHECK_ENTITLEMENT + value: "{{.Values.aws.enableCheckEntitlement}}" + - name: AWS_MP_REGION + value: {{.Values.aws.region}} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/awsmp-product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end}} + # add aws marketplace license config to the licensed container application env + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + readOnly: true + - name: webhook-config + mountPath: /config + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/awsmp-product-license" + {{- end}} + volumes: + - name: webhook-certs + emptyDir: {} + - name: webhook-config + configMap: + name: webhook-scanning-filters + optional: true + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + secret: + secretName: {{ .Values.aws.licenseConfigSecretName }} + {{- end}} + initContainers: + - name: generate-cert + image: "{{ .Values.initContainer.repository }}:{{ .Values.initContainer.tag }}" + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + env: + - name: WEBHOOK_CERTS_DIR + value: /etc/webhook/certs + - name: WEBHOOK_SERVER_DNS + value: {{ printf "datree-webhook-server.%s.svc" (include "datree.namespace" .)}} + \ No newline at end of file diff --git a/charts/datree-admission-webhook-awsmp/templates/namespace-post-install.yaml b/charts/datree-admission-webhook-awsmp/templates/namespace-post-install.yaml new file mode 100644 index 00000000..435f7e9d --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/namespace-post-install.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.namespace-post-install" .}} diff --git a/charts/datree-admission-webhook-awsmp/templates/role.yaml b/charts/datree-admission-webhook-awsmp/templates/role.yaml new file mode 100644 index 00000000..73cad508 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/role.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.role" .}} diff --git a/charts/datree-admission-webhook-awsmp/templates/rolebinding.yaml b/charts/datree-admission-webhook-awsmp/templates/rolebinding.yaml new file mode 100644 index 00000000..952ef5ae --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/rolebinding.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.rolebinding" .}} diff --git a/charts/datree-admission-webhook-awsmp/templates/service.yaml b/charts/datree-admission-webhook-awsmp/templates/service.yaml new file mode 100644 index 00000000..9de309d0 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/service.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.service" .}} diff --git a/charts/datree-admission-webhook-awsmp/templates/serviceaccount.yaml b/charts/datree-admission-webhook-awsmp/templates/serviceaccount.yaml new file mode 100644 index 00000000..6b118144 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/templates/serviceaccount.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.serviceaccount" .}} diff --git a/charts/datree-admission-webhook-awsmp/values.schema.json b/charts/datree-admission-webhook-awsmp/values.schema.json new file mode 100644 index 00000000..a247e008 --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/values.schema.json @@ -0,0 +1,238 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "The name of the namespace all resources will be created in, if not specified in the release." + }, + "replicaCount": { + "type": "integer", + "description": "The number of Datree webhook-server replicas to deploy for the webhook.", + "minimum": 2 + }, + "customLabels": { + "type": "object", + "description": "Additional labels to add to all resources." + }, + "customAnnotations": { + "type": "object", + "description": "Additional annotations to add to all resources." + }, + "debug": { + "type": "boolean", + "description": "Run the webhook-server in debug mode, this will log debug information to the console." + }, + "rbac": { + "type": "object", + "description": "Required ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server, If not created they should be provided.", + "properties": { + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Create or not the ServiceAccount for the webhook-server. If not created it should be provided." + }, + "name": { + "type": "string", + "description": "The ServiceAccount name" + } + }, + "required": ["create", "name"] + }, + "clusterRole": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Create or not the ClusterRole for the webhook-server. If not created it should be provided." + }, + "name": { + "type": "string", + "description": "The ClusterRole name" + } + }, + "required": ["create", "name"] + } + }, + "required": ["serviceAccount", "clusterRole"] + }, + "datree": { + "type": "object", + "description": "Datree webhook configuration. (Checkout more details at htttps://hub.datree.com)", + "properties": { + "token": { + "type": "string", + "description": "The token used to link the CLI to your dashboard." + }, + "policy": { + "type": "string", + "description": "The name of the policy to check, e.g: staging." + }, + "verbose": { + "type": "string", + "description": "Display 'How to Fix' link for failed rules in output." + }, + "output": { + "type": "string", + "description": "The format output of the policy check results: yaml, json, xml, simple, JUnit." + }, + "noRecord": { + "type": "string", + "description": "Don't record the results of the policy check." + } + }, + "required": ["token", "policy", "verbose", "output", "noRecord"] + }, + "image": { + "type": "object", + "description": "The Datree webhook-server image to use.", + "properties": { + "repository": { + "type": "string", + "description": "Image repository" + }, + "tag": { + "type": "string", + "description": "Image tag" + }, + "pullPolicy": { + "type": "string", + "description": "Image pull policy", + "pattern": "^(Always|Never|IfNotPresent)$" + } + }, + "required": ["repository", "tag", "pullPolicy"] + }, + "securityContext": { + "type": "object", + "description": "Security context for the webhook-server containers", + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean" + }, + "readOnlyRootFilesystem": { + "type": "boolean" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "integer" + } + }, + "required": [ + "allowPrivilegeEscalation", + "readOnlyRootFilesystem", + "runAsNonRoot", + "runAsUser" + ] + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "memory": { + "type": "string" + } + }, + "required": ["memory"] + }, + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": ["string", "integer"] + }, + "memory": { + "type": ["string", "integer"] + } + }, + "required": ["cpu", "memory"] + } + }, + "required": ["limits", "requests"] + }, + "hooks": { + "type": "object", + "description": "Helm Hooks: pre-install and pre-delete that the chart run during install", + "properties": { + "timeoutTime": { + "type": ["integer", "null"], + "description": "The timeout time the hook will wait for the webhook-server is ready." + }, + "image": { + "type": "object", + "description": "The image for running kubectl commands", + "properties": { + "repository": { + "type": "string" + }, + "sha": { + "type": "string", + "pattern": "^sha256\\W[a-f0-9]{64}$" + }, + "pullPolicy": { + "type": "string", + "pattern": "^(Always|Never|IfNotPresent)$" + } + }, + "required": ["repository", "sha", "pullPolicy"] + } + }, + "required": ["image"] + }, + "aws": { + "type": "object", + "description": "AWS Marketplace configuration", + "properties": { + "licenseConfigSecretName": { + "type": "string", + "description": "The name of the secret that contains the license configuration." + }, + "region": { + "type": "string", + "description": "The AWS Region", + "pattern": "(af|ap|ca|eu|me|sa|us)-(central|north|(north(?:east|west))|south|south(?:east|west)|east|west)-\\d+" + }, + "enableCheckEntitlement": { + "type": "boolean", + "description": "Enable or not AWS Marketplace license checkout, this is relevant for paid products only." + }, + "productId": { + "type": "string", + "description": "The application's Product SKU (Product ID)" + }, + "issuerKey": { + "type": "string", + "description": "The trusted issuer of the license (AWS Marketplace)", + "enum": ["aws:294406891311:AWS/Marketplace:issuer-fingerprint"] + } + }, + "required": [ + "licenseConfigSecretName", + "region", + "enableCheckEntitlement", + "productId", + "issuerKey" + ] + } + }, + "required": [ + "namespace", + "replicaCount", + "customLabels", + "customAnnotations", + "debug", + "rbac", + "datree", + "image", + "securityContext", + "resources", + "hooks", + "aws" + ] +} diff --git a/charts/datree-admission-webhook-awsmp/values.yaml b/charts/datree-admission-webhook-awsmp/values.yaml new file mode 100644 index 00000000..3231400b --- /dev/null +++ b/charts/datree-admission-webhook-awsmp/values.yaml @@ -0,0 +1,100 @@ +# Default values for datree-admission-webhook. + +# The name of the namespace all resources will be created in, if not specified in the release. +namespace: "" + +# The number of Datree webhook-server replicas to deploy for the webhook. +replicaCount: 2 + +# Additional labels to add to all resources. +customLabels: {} + +# Additional annotations to add to all resources. +customAnnotations: {} + +# Run the webhook-server in debug mode, this will log debug information to the console. +debug: false + +# Create ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server +rbac: + serviceAccount: + # Create the ServiceAccount + create: true + # The ServiceAccount name + name: datree-webhook-server + clusterRole: + # Create the ClusterRole + create: true + # The ClusterRole name + name: datree-webhook-server-read + +# Datree webhook configuration, checkout more details at htttps://hub.datree.com +datree: + # The token used to link the CLI to your dashboard. + token: + # The name of the policy to check, e.g: staging. (string, optional) + policy: "" + # Display 'How to Fix' link for failed rules in output. (boolean ,optional) + verbose: "" + # The format output of the policy check results: yaml, json, xml, simple, JUnit. (string ,optional) + output: "" + # Don’t send policy checks metadata to the backend. (boolean ,optional) + noRecord: "" + +# The Datree webhook-server image to use. +image: + # Image repository + repository: localhost:5000/webhook-server #localhost:5000/datree-admission-webhook #709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/awsmp-datree-admission-webhook + # Image tag + tag: 0.1-rc.1-prerebase #1.0-rc.3 + # Image pull policy + pullPolicy: Always + +initContainer: + repository: localhost:5000/cert-generator # localhost:5000/gen-cert #709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-cert-generator + tag: 0.1-rc.1-prerebase # 0.1.2-rc.2 #0.1.1-rc.1 + +imageWebhook: + # Image repository + repository: localhost:5000/init-webhook #localhost:5000/webhook-init #709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-webhook-init + # Image tag + tag: 0.1-rc.2-prerebase #0.1.1-rc.3 + +# Security context for the containers +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 25000 + +resources: + limits: + memory: 512Mi + requests: + cpu: 1000m + memory: 512Mi + +# During install Datree run two hooks: pre-install and pre-delete. +# `datree-label-namespaces-hook-post-install` - Helm hook that run after the chart is installed and label namespaces with datree.io/skip=true +# `datree-wait-server-ready-hook-post-install` - Helm hook that run after the chart is awaits for the webhook-server to be ready +hooks: + # The timeout time the hook will wait for the webhook-server is ready. + timeoutTime: + # The image for running kubectl commands + image: + repository: bitnami/kubectl #709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/bitnami-kubectl + sha: sha256:1841cb16fddd9660c34f9b5bcec51f8c77bedba76c56381537753976b24649df #sha256:2c963c1aae87f544a53242348d64dce4bc39739211989394833322d29364b73c + pullPolicy: IfNotPresent + +# AWS Marketplace configuration +aws: + # The name of the secret that contains the license configuration. + licenseConfigSecretName: "" + # The AWS Region + region: "us-east-1" + # Enable AWS Marketplace license checkout, this is relevant for paid products only. + enableCheckEntitlement: true + # The application’s Product SKU (Product ID) + productId: "ad0ee0c8-f50f-464a-9bc4-d6270592dd36" + # The trusted issuer of the license (AWS Marketplace) + issuerKey: "aws:294406891311:AWS/Marketplace:issuer-fingerprint" diff --git a/charts/datree-admission-webhook/Chart.lock b/charts/datree-admission-webhook/Chart.lock new file mode 100644 index 00000000..3967fe19 --- /dev/null +++ b/charts/datree-admission-webhook/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: datree-lib + repository: file://../datree-lib + version: 0.1.0 +digest: sha256:9e2042b6eb6267c11aef85f75517279b1df65881070d49eeffab329d80ab679d +generated: "2022-09-19T22:41:23.838934+03:00" diff --git a/charts/datree-admission-webhook/Chart.yaml b/charts/datree-admission-webhook/Chart.yaml index d17a1fb8..94ceb452 100644 --- a/charts/datree-admission-webhook/Chart.yaml +++ b/charts/datree-admission-webhook/Chart.yaml @@ -21,3 +21,9 @@ version: 0.3.14 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "0.1.35" +#appVersion: "0.1.21" + +dependencies: + - name: datree-lib + version: 0.1.0 + repository: file://../datree-lib diff --git a/charts/datree-admission-webhook/charts/datree-lib-0.1.0.tgz b/charts/datree-admission-webhook/charts/datree-lib-0.1.0.tgz new file mode 100644 index 00000000..4ef54c04 Binary files /dev/null and b/charts/datree-admission-webhook/charts/datree-lib-0.1.0.tgz differ diff --git a/charts/datree-admission-webhook/templates/_helpers.tpl b/charts/datree-admission-webhook/templates/_helpers.tpl index 31f52d92..c1070895 100644 --- a/charts/datree-admission-webhook/templates/_helpers.tpl +++ b/charts/datree-admission-webhook/templates/_helpers.tpl @@ -22,4 +22,4 @@ helm.sh/chart: {{ template "datree.chart" . }} {{/* The namespace name. */}} {{- define "datree.namespace" -}} {{- default .Release.Namespace .Values.namespace -}} -{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/datree-admission-webhook/templates/cleanup-pre-delete.yaml b/charts/datree-admission-webhook/templates/cleanup-pre-delete.yaml new file mode 100644 index 00000000..3b89d235 --- /dev/null +++ b/charts/datree-admission-webhook/templates/cleanup-pre-delete.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.cleanup-pre-delete" .}} diff --git a/charts/datree-admission-webhook/templates/clusterrole.yaml b/charts/datree-admission-webhook/templates/clusterrole.yaml index f0dff494..e6a522f6 100644 --- a/charts/datree-admission-webhook/templates/clusterrole.yaml +++ b/charts/datree-admission-webhook/templates/clusterrole.yaml @@ -1,64 +1 @@ -{{- if .Values.rbac.clusterRole.create }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{.Values.rbac.clusterRole.name}} - labels: {{ include "datree.labels" . | nindent 4 }} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -rules: - - apiGroups: - - "" - resources: - - "nodes" - - "namespaces" - verbs: - - "get" - - "list" -{{- end}} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: datree-namespaces-update - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -rules: - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - update - - patch - resourceNames: - - kube-system - - {{template "datree.namespace" .}} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: datree-validationwebhook-delete - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -rules: - - apiGroups: - - "admissionregistration.k8s.io" - resources: - - validatingwebhookconfigurations - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - resourceNames: - - datree-webhook \ No newline at end of file +{{- include "datree-lib.clusterrole" .}} diff --git a/charts/datree-admission-webhook/templates/clusterrolebinding.yaml b/charts/datree-admission-webhook/templates/clusterrolebinding.yaml index aa2b8b6a..266fd9e9 100644 --- a/charts/datree-admission-webhook/templates/clusterrolebinding.yaml +++ b/charts/datree-admission-webhook/templates/clusterrolebinding.yaml @@ -1,55 +1 @@ -{{- if .Values.rbac.clusterRole.create -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{.Values.rbac.clusterRole.name}} - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{.Values.rbac.clusterRole.name}} # datree-webhook-server-read -subjects: - - kind: ServiceAccount - name: {{.Values.rbac.serviceAccount.name}} # datree-webhook-server - namespace: {{template "datree.namespace" .}} ---- -{{- end -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: datree-namespaces-update - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: datree-namespaces-update -subjects: - - kind: ServiceAccount - name: "datree-label-namespaces-hook-post-install" - namespace: "{{template "datree.namespace" .}}" - - kind: ServiceAccount - name: "datree-cleanup-namespaces-hook-pre-delete" - namespace: "{{template "datree.namespace" .}}" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: datree-validationwebhook-delete - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: datree-validationwebhook-delete -subjects: - - kind: ServiceAccount - name: "datree-cleanup-namespaces-hook-pre-delete" - namespace: "{{template "datree.namespace" .}}" +{{- include "datree-lib.clusterrolebinding" .}} diff --git a/charts/datree-admission-webhook/templates/deployment.yaml b/charts/datree-admission-webhook/templates/deployment.yaml index d083bc40..f2d7973f 100644 --- a/charts/datree-admission-webhook/templates/deployment.yaml +++ b/charts/datree-admission-webhook/templates/deployment.yaml @@ -29,19 +29,6 @@ spec: - name: server # caution: don't change the order of the environment variables # changing the order will harm resource patching - env: - - name: DATREE_TOKEN - value: "{{ .Values.datree.token | regexFind $uuidv4RegexPattern | required $emptyTokenMessage }}" - - name: DATREE_POLICY - value: {{.Values.datree.policy | default "Starter"}} - - name: DATREE_VERBOSE - value: "{{.Values.datree.verbose}}" - - name: DATREE_OUTPUT - value: "{{.Values.datree.output}}" - - name: DATREE_NO_RECORD - value: "{{.Values.datree.noRecord}}" - - name: DATREE_ENFORCE - value: "{{.Values.datree.enforce}}" securityContext: {{- toYaml .Values.securityContext | nindent 12 }} livenessProbe: httpGet: @@ -63,6 +50,43 @@ spec: ports: - containerPort: 8443 name: webhook-api + # caution: don't change the order of the environment variables + # changing the order will harm resource patching + env: + - name: DEBUG + value: "{{ .Values.debug }}" + # Datree webhook varaibles + - name: DATREE_TOKEN + value: "{{ .Values.datree.token | regexFind $uuidv4RegexPattern | required $emptyTokenMessage }}" + - name: DATREE_POLICY + value: {{.Values.datree.policy | default "Starter"}} + - name: DATREE_VERBOSE + value: "{{.Values.datree.verbose}}" + - name: DATREE_OUTPUT + value: "{{.Values.datree.output}}" + - name: DATREE_NO_RECORD + value: "{{.Values.datree.noRecord}}" + - name: DATREE_ENFORCE + value: "{{.Values.datree.enforce}}" + # AWS Marketplace varaibles + - name: AWS_MP_PRODUCT_ID + value: {{ .Values.aws.productId }} + - name: AWS_MP_KEY_FINGERPRINT + value: {{ .Values.aws.issuerKey }} + - name: AWS_MP_ENABLE_CHECK_ENTITLEMENT + value: "{{.Values.aws.enableCheckEntitlement}}" + - name: AWS_MP_REGION + value: {{.Values.aws.region}} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/awsmp-product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end}} + # add aws marketplace license config to the licensed container application env volumeMounts: - name: webhook-tls-certs mountPath: /run/secrets/tls @@ -70,6 +94,10 @@ spec: - name: webhook-config mountPath: /config readOnly: true + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/awsmp-product-license" + {{- end}} volumes: - name: webhook-tls-certs secret: @@ -78,3 +106,9 @@ spec: configMap: name: webhook-scanning-filters optional: true + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + secret: + secretName: {{ .Values.aws.licenseConfigSecretName }} + {{- end}} +# {{- include "datree-lib.deployment" .}} diff --git a/charts/datree-admission-webhook/templates/namespace-post-install.yaml b/charts/datree-admission-webhook/templates/namespace-post-install.yaml index ef479899..435f7e9d 100644 --- a/charts/datree-admission-webhook/templates/namespace-post-install.yaml +++ b/charts/datree-admission-webhook/templates/namespace-post-install.yaml @@ -1,36 +1 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: datree-label-namespaces-hook-post-install - namespace: {{template "datree.namespace" .}} - labels: {{include "datree.labels" . | nindent 4}} - annotations: - "helm.sh/hook": post-install, post-upgrade - "helm.sh/hook-weight": "-5" - "helm.sh/hook-delete-policy": hook-succeeded, hook-failed - {{- if .Values.customAnnotations }} - {{- toYaml .Values.customAnnotations }} - {{- end }} -spec: - template: - metadata: - labels: {{include "datree.labels" . | nindent 8}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} - spec: - serviceAccount: datree-label-namespaces-hook-post-install - restartPolicy: OnFailure - nodeSelector: - kubernetes.io/os: linux - containers: - - name: kubectl-label - image: "{{ .Values.hooks.image.repository }}@{{ .Values.hooks.image.sha }}" - imagePullPolicy: {{.Values.hooks.image.pullPolicy}} - args: - - label - - ns - - kube-system - - {{template "datree.namespace" .}} - - admission.datree/validate=skip - - --overwrite +{{- include "datree-lib.namespace-post-install" .}} diff --git a/charts/datree-admission-webhook/templates/role.yaml b/charts/datree-admission-webhook/templates/role.yaml index 1045c648..73cad508 100644 --- a/charts/datree-admission-webhook/templates/role.yaml +++ b/charts/datree-admission-webhook/templates/role.yaml @@ -1,18 +1 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: datree-pods-reader - labels: {{ include "datree.labels" . | nindent 4 }} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -rules: - - apiGroups: - - "" - resources: - - "pods" - - "jobs" - verbs: - - "get" - - "list" - - "watch" \ No newline at end of file +{{- include "datree-lib.role" .}} diff --git a/charts/datree-admission-webhook/templates/rolebinding.yaml b/charts/datree-admission-webhook/templates/rolebinding.yaml index f9945f2c..952ef5ae 100644 --- a/charts/datree-admission-webhook/templates/rolebinding.yaml +++ b/charts/datree-admission-webhook/templates/rolebinding.yaml @@ -1,16 +1 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: datree-pods-reader - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: datree-pods-reader -subjects: - - kind: ServiceAccount - name: datree-wait-server-ready-hook-post-install - namespace: "{{template "datree.namespace" .}}" \ No newline at end of file +{{- include "datree-lib.rolebinding" .}} diff --git a/charts/datree-admission-webhook/templates/service.yaml b/charts/datree-admission-webhook/templates/service.yaml index 4bd26429..63e43841 100644 --- a/charts/datree-admission-webhook/templates/service.yaml +++ b/charts/datree-admission-webhook/templates/service.yaml @@ -1,15 +1,5 @@ -apiVersion: v1 -kind: Service -metadata: - name: datree-webhook-server - namespace: {{template "datree.namespace" .}} - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -spec: - selector: - app: "datree-webhook-server" - ports: - - port: 443 - targetPort: webhook-api +{{- include "datree-lib.service" .}} +--- +{{- include "datree-lib.serviceaccounts" .}} +--- + diff --git a/charts/datree-admission-webhook/templates/serviceaccount.yaml b/charts/datree-admission-webhook/templates/serviceaccount.yaml index 0ef8cbaf..6b118144 100644 --- a/charts/datree-admission-webhook/templates/serviceaccount.yaml +++ b/charts/datree-admission-webhook/templates/serviceaccount.yaml @@ -1,38 +1 @@ -{{- if .Values.rbac.serviceAccount.create }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{.Values.rbac.serviceAccount.name}} - namespace: {{ template "datree.namespace" . }} - labels: {{ include "datree.labels" . | nindent 4 }} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -{{- end}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: datree-label-namespaces-hook-post-install - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: datree-cleanup-namespaces-hook-pre-delete - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: datree-wait-server-ready-hook-post-install - labels: {{include "datree.labels" . | nindent 4}} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} \ No newline at end of file +{{- include "datree-lib.serviceaccount" .}} diff --git a/charts/datree-admission-webhook/templates/wait-server-ready-post-install.yaml b/charts/datree-admission-webhook/templates/wait-server-ready-post-install.yaml index 53f98888..43721649 100644 --- a/charts/datree-admission-webhook/templates/wait-server-ready-post-install.yaml +++ b/charts/datree-admission-webhook/templates/wait-server-ready-post-install.yaml @@ -28,3 +28,4 @@ spec: - "-c" - >- kubectl wait --for=condition=ready pod -l app=datree-webhook-server --timeout="{{ default "180s" .Values.hooks.timeoutTime }}" +# {{- include "datree-lib.wait-server-ready-post-install" .}} diff --git a/charts/datree-admission-webhook/templates/webhook-with-cert-secrets.yaml b/charts/datree-admission-webhook/templates/webhook-with-cert-secrets.yaml index 03c048fc..c4cabdf4 100644 --- a/charts/datree-admission-webhook/templates/webhook-with-cert-secrets.yaml +++ b/charts/datree-admission-webhook/templates/webhook-with-cert-secrets.yaml @@ -1,65 +1 @@ -{{- $ca := genCA "/CN=Admission Controller Webhook Demo CA" 1827 -}} -{{- $altNames := list ( "datree-webhook-server.datree.svc" ) -}} -{{- $cert := genSignedCert "/CN=datree-webhook-server.datree.svc" nil $altNames 1827 $ca -}} -apiVersion: v1 -kind: Secret -metadata: - name: datree-ca-tls - labels: {{ include "datree.labels" . | nindent 4 }} - namespace: {{ template "datree.namespace" . }} - {{- if .Values.customAnnotations }} - annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.key: {{ $ca.Key | b64enc }} - tls.crt: {{ $ca.Cert | b64enc }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: webhook-server-tls - labels: {{ include "datree.labels" . | nindent 4 }} - namespace: {{ template "datree.namespace" . }} - annotations: - self-signed-cert: "true" - {{- if .Values.customAnnotations }} - {{- toYaml .Values.customAnnotations}} - {{- end }} -type: kubernetes.io/tls -data: - tls.key: {{ $cert.Key | b64enc }} - tls.crt: {{ $cert.Cert | b64enc }} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - name: datree-webhook - annotations: - "helm.sh/hook": post-install, post-upgrade - "helm.sh/hook-weight": "-5" - {{- if .Values.customAnnotations }} - {{- toYaml .Values.customAnnotations}} - {{- end }} -webhooks: - - name: webhook-server.datree.svc - sideEffects: None - timeoutSeconds: 30 - admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - name: datree-webhook-server - namespace: {{ template "datree.namespace" . }} - path: "/validate" - caBundle: {{ $ca.Cert | b64enc }} - namespaceSelector: - matchExpressions: - - key: admission.datree/validate - operator: DoesNotExist - rules: - - operations: ["CREATE", "UPDATE"] - apiGroups: ["*"] - apiVersions: ["*"] - resources: ["*"] +{{- include "datree-lib.webhook-with-cert-secrets" .}} diff --git a/charts/datree-admission-webhook/values-awsmp-datree-free.yaml b/charts/datree-admission-webhook/values-awsmp-datree-free.yaml new file mode 100644 index 00000000..e7b38d9d --- /dev/null +++ b/charts/datree-admission-webhook/values-awsmp-datree-free.yaml @@ -0,0 +1,90 @@ +# Default values for datree-admission-webhook. + +# The name of the namespace all resources will be created in, if not specified in the release. +namespace: "" + +# The number of Datree webhook-server replicas to deploy for the webhook. +replicaCount: 2 + +# Additional labels to add to all resources. +customLabels: {} + +# Additional annotations to add to all resources. +customAnnotations: {} + +# Run the webhook-server in debug mode, this will log debug information to the console. +debug: false + +# Create ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server +rbac: + serviceAccount: + # Create the ServiceAccount + create: true + # The ServiceAccount name + name: datree-webhook-server + clusterRole: + # Create the ClusterRole + create: true + # The ClusterRole name + name: datree-webhook-server-read + +# Datree webhook configuration, checkout more details at htttps://hub.datree.com +datree: + # The token used to link the CLI to your dashboard. + token: "" + # The name of the policy to check, e.g: staging. (string, optional) + policy: "" + # Display 'How to Fix' link for failed rules in output. (boolean ,optional) + verbose: "" + # The format output of the policy check results: yaml, json, xml, simple, JUnit. (string ,optional) + output: "" + # Don’t send policy checks metadata to the backend. (boolean ,optional) + noRecord: "" + +# The Datree webhook-server image to use. +image: + # Image repository + repository: public.ecr.aws/m6p7v6h2/datree-admission-webhook + # Image tag + tag: 0.1.24-test + # Image pull policy + pullPolicy: Always + +# Security context for the containers +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 25000 + +resources: + limits: + memory: 512Mi + requests: + cpu: 1000m + memory: 512Mi + +# During install Datree run two hooks: pre-install and pre-delete. +# `datree-label-namespaces-hook-post-install` - Helm hook that run after the chart is installed and label namespaces with datree.io/skip=true +# `datree-wait-server-ready-hook-post-install` - Helm hook that run after the chart is awaits for the webhook-server to be ready +hooks: + # The timeout time the hook will wait for the webhook-server is ready. + timeoutTime: + # The image for running kubectl commands + image: + repository: bitnami/kubectl + sha: sha256:d3c17f1dc6e665dcc78e8c14a83ae630bc3d65b07ea11c5f1a012c2c6786d039 + pullPolicy: IfNotPresent + +# AWS Marketplace configuration +aws: + # The name of the secret that contains the license configuration. + licenseConfigSecretName: "" + # The AWS Region + region: "us-east-1" + # Enable AWS Marketplace license checkout, this is relevant for paid products only. + enableCheckEntitlement: true + # The application’s Product SKU (Product ID) + productId: "e3c1bf72-7c57-435e-be33-deddbe7220f2" + # The trusted issuer of the license (AWS Marketplace) + issuerKey: "aws:294406891311:AWS/Marketplace:issuer-fingerprint" diff --git a/charts/datree-admission-webhook/values.schema.json b/charts/datree-admission-webhook/values.schema.json new file mode 100644 index 00000000..f7f35c24 --- /dev/null +++ b/charts/datree-admission-webhook/values.schema.json @@ -0,0 +1,238 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "The name of the namespace all resources will be created in, if not specified in the release." + }, + "replicaCount": { + "type": "integer", + "description": "The number of Datree webhook-server replicas to deploy for the webhook.", + "minimum": 2 + }, + "customLabels": { + "type": "object", + "description": "Additional labels to add to all resources." + }, + "customAnnotations": { + "type": "object", + "description": "Additional annotations to add to all resources." + }, + "debug": { + "type": "boolean", + "description": "Run the webhook-server in debug mode, this will log debug information to the console." + }, + "rbac": { + "type": "object", + "description": "Required ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server, If not created they should be provided.", + "properties": { + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Create or not the ServiceAccount for the webhook-server. If not created it should be provided." + }, + "name": { + "type": "string", + "description": "The ServiceAccount name" + } + }, + "required": ["create", "name"] + }, + "clusterRole": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Create or not the ClusterRole for the webhook-server. If not created it should be provided." + }, + "name": { + "type": "string", + "description": "The ClusterRole name" + } + }, + "required": ["create", "name"] + } + }, + "required": ["serviceAccount", "clusterRole"] + }, + "datree": { + "type": "object", + "description": "Datree webhook configuration. (Checkout more details at htttps://hub.datree.com)", + "properties": { + "token": { + "type": "string", + "description": "The token used to link the CLI to your dashboard." + }, + "policy": { + "type": "string", + "description": "The name of the policy to check, e.g: staging." + }, + "verbose": { + "type": "string", + "description": "Display 'How to Fix' link for failed rules in output." + }, + "output": { + "type": "string", + "description": "The format output of the policy check results: yaml, json, xml, simple, JUnit.", + "pattern": "^(yaml|json|xml|simple|JUnit|^$)$" + }, + "noRecord": { + "type": "string", + "description": "Don't record the results of the policy check." + } + }, + "required": ["token", "policy", "verbose", "output", "noRecord"] + }, + "image": { + "type": "object", + "description": "The Datree webhook-server image to use.", + "properties": { + "repository": { + "type": "string", + "description": "Image repository" + }, + "tag": { + "type": "string", + "description": "Image tag", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "pullPolicy": { + "type": "string", + "description": "Image pull policy", + "pattern": "^(Always|Never|IfNotPresent)$" + } + }, + "required": ["repository", "tag", "pullPolicy"] + }, + "securityContext": { + "type": "object", + "description": "Security context for the webhook-server containers", + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean" + }, + "readOnlyRootFilesystem": { + "type": "boolean" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "integer" + } + }, + "required": [ + "allowPrivilegeEscalation", + "readOnlyRootFilesystem", + "runAsNonRoot", + "runAsUser" + ] + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "memory": { + "type": "string" + } + }, + "required": ["memory"] + }, + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": ["string", "integer"] + }, + "memory": { + "type": ["string", "integer"] + } + }, + "required": ["cpu", "memory"] + } + }, + "required": ["limits", "requests"] + }, + "hooks": { + "type": "object", + "description": "Helm Hooks: pre-install and pre-delete that the chart run during install", + "properties": { + "timeoutTime": { + "type": ["integer", "null"], + "description": "The timeout time the hook will wait for the webhook-server is ready." + }, + "image": { + "type": "object", + "description": "The image for running kubectl commands", + "properties": { + "repository": { + "type": "string" + }, + "sha": { + "type": "string", + "pattern": "^sha256\\W[a-f0-9]{64}$" + }, + "pullPolicy": { + "type": "string", + "pattern": "^(Always|Never|IfNotPresent)$" + } + }, + "required": ["repository", "sha", "pullPolicy"] + } + }, + "required": ["image"] + }, + "aws": { + "type": "object", + "description": "AWS Marketplace configuration", + "properties": { + "licenseConfigSecretName": { + "type": "string", + "description": "The name of the secret that contains the license configuration." + }, + "region": { + "type": "string", + "description": "The AWS Region" + }, + "enableCheckEntitlement": { + "type": "boolean", + "description": "Enable or not AWS Marketplace license checkout, this is relevant for paid products only." + }, + "productId": { + "type": "string", + "description": "The application's Product SKU (Product ID)" + }, + "issuerKey": { + "type": "string", + "description": "The trusted issuer of the license (AWS Marketplace)" + } + }, + "required": [ + "licenseConfigSecretName", + "region", + "enableCheckEntitlement", + "productId", + "issuerKey" + ] + } + }, + "required": [ + "namespace", + "replicaCount", + "customLabels", + "customAnnotations", + "debug", + "rbac", + "datree", + "image", + "securityContext", + "resources", + "hooks", + "aws" + ] +} diff --git a/charts/datree-admission-webhook/values.yaml b/charts/datree-admission-webhook/values.yaml index 33f7895d..b63a9974 100644 --- a/charts/datree-admission-webhook/values.yaml +++ b/charts/datree-admission-webhook/values.yaml @@ -8,6 +8,10 @@ replicaCount: 2 customLabels: {} # Additional annotations to add to all resources. customAnnotations: {} + +# Run the webhook-server in debug mode, this will log debug information to the console. +# debug: false + # Create ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server rbac: serviceAccount: @@ -20,6 +24,7 @@ rbac: create: true # The ClusterRole name name: datree-webhook-server-read + datree: # The token used to link the CLI to your dashboard. token: @@ -39,6 +44,7 @@ image: repository: datree/admission-webhook # Image tag tag: 0.1.35 + # tag: 0.1.24 # Image pull policy pullPolicy: Always # Security context for the containers @@ -78,3 +84,16 @@ hooks: repository: bitnami/kubectl sha: sha256:d3c17f1dc6e665dcc78e8c14a83ae630bc3d65b07ea11c5f1a012c2c6786d039 pullPolicy: IfNotPresent + +# AWS Marketplace configuration +aws: + # The name of the secret that contains the license configuration. + licenseConfigSecretName: "" + # The AWS Region + region: "" + # Enable AWS Marketplace license checkout, this is relevant for paid products only. + enableCheckEntitlement: false + # The application’s Product SKU (Product ID) + productId: "" + # The trusted issuer of the license (AWS Marketplace) + issuerKey: "" diff --git a/charts/datree-free-admission-webhook-awsmp/Chart.lock b/charts/datree-free-admission-webhook-awsmp/Chart.lock new file mode 100644 index 00000000..592337fb --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: datree-lib + repository: file://../datree-lib + version: 0.1.1 +digest: sha256:a65b157f4e3e466bfc9a80df08a319a3f61abdb84e4a29dfbe18127dd11edac6 +generated: "2022-10-24T19:02:29.445094+03:00" diff --git a/charts/datree-free-admission-webhook-awsmp/Chart.yaml b/charts/datree-free-admission-webhook-awsmp/Chart.yaml new file mode 100644 index 00000000..12e208cc --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/Chart.yaml @@ -0,0 +1,31 @@ +apiVersion: v2 +name: datree-free-admission-webhook-awsmp +description: A Helm chart for Datree admission webhook for Kubernetes clusters +icon: https://github.com/datreeio/admission-webhook-datree/blob/main/internal/images/diagram.png +type: application +keywords: + - datree-admission-webhook + - policy agent + - validating webhook + - admissions controller +home: datree.io +sources: + - https://github.com/datreeio/admission-webhook-datree + +kubeVersion: ">=1.16.0-0" + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 1.0.1-rc.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.1.24-rc.2" + +dependencies: + - name: datree-lib + version: 0.1.1 + repository: file://../datree-lib diff --git a/charts/datree-free-admission-webhook-awsmp/charts/datree-lib-0.1.1.tgz b/charts/datree-free-admission-webhook-awsmp/charts/datree-lib-0.1.1.tgz new file mode 100644 index 00000000..0b81e688 Binary files /dev/null and b/charts/datree-free-admission-webhook-awsmp/charts/datree-lib-0.1.1.tgz differ diff --git a/charts/datree-free-admission-webhook-awsmp/templates/_helpers.tpl b/charts/datree-free-admission-webhook-awsmp/templates/_helpers.tpl new file mode 100644 index 00000000..c1070895 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/_helpers.tpl @@ -0,0 +1,25 @@ +{{/* Create chart name and version as used by the chart label. */}} +{{- define "datree.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{/* Helm and Kubernetes required labels */}} +{{- define "datree.labels" -}} +app.kubernetes.io/name: {{.Chart.Name}} +app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +app.kubernetes.io/instance: {{ .Release.Name | quote }} +app.kubernetes.io/version: {{ .Chart.AppVersion }} +app.kubernetes.io/part-of: "datree" +meta.helm.sh/release-name: "{{ .Chart.Name }}" +meta.helm.sh/release-namespace: "{{ .Release.Namespace}}" +helm.sh/chart: {{ template "datree.chart" . }} + {{- if .Values.customLabels -}} + {{ toYaml .Values.customLabels }} + {{- end -}} +{{- end -}} + +{{/* The namespace name. */}} +{{- define "datree.namespace" -}} +{{- default .Release.Namespace .Values.namespace -}} +{{- end -}} \ No newline at end of file diff --git a/charts/datree-free-admission-webhook-awsmp/templates/clusterrole.yaml b/charts/datree-free-admission-webhook-awsmp/templates/clusterrole.yaml new file mode 100644 index 00000000..e6a522f6 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/clusterrole.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.clusterrole" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/templates/clusterrolebinding.yaml b/charts/datree-free-admission-webhook-awsmp/templates/clusterrolebinding.yaml new file mode 100644 index 00000000..266fd9e9 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/clusterrolebinding.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.clusterrolebinding" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/templates/deployment.yaml b/charts/datree-free-admission-webhook-awsmp/templates/deployment.yaml new file mode 100644 index 00000000..bfdabf48 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/deployment.yaml @@ -0,0 +1,126 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: datree-webhook-server + namespace: {{ template "datree.namespace" . }} + labels: {{ include "datree.labels" . | nindent 4 }} + owner: datree + app: "datree-webhook-server" + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-weight": "5" + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: "datree-webhook-server" + template: + metadata: + labels: {{ include "datree.labels" . | nindent 8 }} + app: "datree-webhook-server" + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} + spec: + serviceAccountName: {{.Values.rbac.serviceAccount.name}} + containers: + - name: webhook-init + image: "{{ .Values.imageWebhook.repository }}:{{ .Values.imageWebhook.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{.Values.image.pullPolicy}} + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + env: + - name: WEBHOOK_NAMESPACE + value: {{ template "datree.namespace" . }} + - name: WEBHOOK_SERVICE + value: "datree-webhook-server" + - name: server + securityContext: {{- toYaml .Values.securityContext | nindent 12 }} + livenessProbe: + httpGet: + path: /health + port: 8443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 10 + resources: {{- toYaml .Values.resources | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{.Values.image.pullPolicy}} + ports: + - containerPort: 8443 + name: webhook-api + # caution: don't change the order of the environment variables + # changing the order will harm resource patching + env: + - name: DEBUG + value: "{{ .Values.debug }}" + # Datree webhook varaibles + - name: DATREE_TOKEN + value: {{.Values.datree.token}} + - name: DATREE_POLICY + value: {{.Values.datree.policy}} + - name: DATREE_VERBOSE + value: {{.Values.datree.verbose}} + - name: DATREE_OUTPUT + value: {{.Values.datree.output}} + - name: DATREE_NO_RECORD + value: {{.Values.datree.noRecord}} + # AWS Marketplace varaibles + - name: AWS_MP_PRODUCT_ID + value: {{ .Values.aws.productId }} + - name: AWS_MP_KEY_FINGERPRINT + value: {{ .Values.aws.issuerKey }} + - name: AWS_MP_ENABLE_CHECK_ENTITLEMENT + value: "{{.Values.aws.enableCheckEntitlement}}" + - name: AWS_MP_REGION + value: {{.Values.aws.region}} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/awsmp-product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end}} + # add aws marketplace license config to the licensed container application env + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + readOnly: true + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/awsmp-product-license" + {{- end}} + volumes: + - name: webhook-certs + emptyDir: {} + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + secret: + secretName: {{ .Values.aws.licenseConfigSecretName }} + {{- end}} + initContainers: + - name: generate-cert + image: "{{ .Values.initContainer.repository }}:{{ .Values.initContainer.tag }}" + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + env: + - name: WEBHOOK_NAMESPACE + value: {{ template "datree.namespace" . }} + - name: WEBHOOK_SERVICE + value: "datree-webhook-server" + \ No newline at end of file diff --git a/charts/datree-free-admission-webhook-awsmp/templates/namespace-post-install.yaml b/charts/datree-free-admission-webhook-awsmp/templates/namespace-post-install.yaml new file mode 100644 index 00000000..435f7e9d --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/namespace-post-install.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.namespace-post-install" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/templates/namespace-pre-delete.yaml b/charts/datree-free-admission-webhook-awsmp/templates/namespace-pre-delete.yaml new file mode 100644 index 00000000..9431e4e5 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/namespace-pre-delete.yaml @@ -0,0 +1,35 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: datree-cleanup-namespaces-hook-pre-delete + labels: {{include "datree.labels" . | nindent 4}} + namespace: {{template "datree.namespace" .}} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-delete-policy": hook-succeeded, hook-failed + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations }} + {{- end }} +spec: + template: + metadata: + labels: {{include "datree.labels" . | nindent 8}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} + spec: + restartPolicy: OnFailure + serviceAccount: datree-cleanup-namespaces-hook-pre-delete + nodeSelector: + kubernetes.io/os: linux + containers: + - name: kubectl-label + image: "{{ .Values.hooks.image.repository }}@{{ .Values.hooks.image.sha }}" + imagePullPolicy: {{.Values.hooks.image.pullPolicy}} + command: + - sh + - "-c" + - >- + kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io datree-webhook -n {{template "datree.namespace" .}}; + kubectl delete deployment datree-webhook-server -n {{template "datree.namespace" .}}; + kubectl label ns kube-system {{template "datree.namespace" .}} datree.io/skip-; \ No newline at end of file diff --git a/charts/datree-free-admission-webhook-awsmp/templates/role.yaml b/charts/datree-free-admission-webhook-awsmp/templates/role.yaml new file mode 100644 index 00000000..73cad508 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/role.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.role" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/templates/rolebinding.yaml b/charts/datree-free-admission-webhook-awsmp/templates/rolebinding.yaml new file mode 100644 index 00000000..952ef5ae --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/rolebinding.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.rolebinding" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/templates/service.yaml b/charts/datree-free-admission-webhook-awsmp/templates/service.yaml new file mode 100644 index 00000000..9de309d0 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/service.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.service" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/templates/serviceaccount.yaml b/charts/datree-free-admission-webhook-awsmp/templates/serviceaccount.yaml new file mode 100644 index 00000000..6b118144 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/templates/serviceaccount.yaml @@ -0,0 +1 @@ +{{- include "datree-lib.serviceaccount" .}} diff --git a/charts/datree-free-admission-webhook-awsmp/values.schema.json b/charts/datree-free-admission-webhook-awsmp/values.schema.json new file mode 100644 index 00000000..92f2d0b1 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/values.schema.json @@ -0,0 +1,239 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "The name of the namespace all resources will be created in, if not specified in the release." + }, + "replicaCount": { + "type": "integer", + "description": "The number of Datree webhook-server replicas to deploy for the webhook.", + "minimum": 2 + }, + "customLabels": { + "type": "object", + "description": "Additional labels to add to all resources." + }, + "customAnnotations": { + "type": "object", + "description": "Additional annotations to add to all resources." + }, + "debug": { + "type": "boolean", + "description": "Run the webhook-server in debug mode, this will log debug information to the console." + }, + "rbac": { + "type": "object", + "description": "Required ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server, If not created they should be provided.", + "properties": { + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Create or not the ServiceAccount for the webhook-server. If not created it should be provided." + }, + "name": { + "type": "string", + "description": "The ServiceAccount name" + } + }, + "required": ["create", "name"] + }, + "clusterRole": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Create or not the ClusterRole for the webhook-server. If not created it should be provided." + }, + "name": { + "type": "string", + "description": "The ClusterRole name" + } + }, + "required": ["create", "name"] + } + }, + "required": ["serviceAccount", "clusterRole"] + }, + "datree": { + "type": "object", + "description": "Datree webhook configuration. (Checkout more details at htttps://hub.datree.com)", + "properties": { + "token": { + "type": "string", + "description": "The token used to link the CLI to your dashboard." + }, + "policy": { + "type": "string", + "description": "The name of the policy to check, e.g: staging." + }, + "verbose": { + "type": "string", + "description": "Display 'How to Fix' link for failed rules in output." + }, + "output": { + "type": "string", + "description": "The format output of the policy check results: yaml, json, xml, simple, JUnit." + }, + "noRecord": { + "type": "string", + "description": "Don't record the results of the policy check." + } + }, + "required": ["token", "policy", "verbose", "output", "noRecord"] + }, + "image": { + "type": "object", + "description": "The Datree webhook-server image to use.", + "properties": { + "repository": { + "type": "string", + "description": "Image repository" + }, + "tag": { + "type": "string", + "description": "Image tag", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "pullPolicy": { + "type": "string", + "description": "Image pull policy", + "pattern": "^(Always|Never|IfNotPresent)$" + } + }, + "required": ["repository", "tag", "pullPolicy"] + }, + "securityContext": { + "type": "object", + "description": "Security context for the webhook-server containers", + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean" + }, + "readOnlyRootFilesystem": { + "type": "boolean" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "integer" + } + }, + "required": [ + "allowPrivilegeEscalation", + "readOnlyRootFilesystem", + "runAsNonRoot", + "runAsUser" + ] + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "memory": { + "type": "string" + } + }, + "required": ["memory"] + }, + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": ["string", "integer"] + }, + "memory": { + "type": ["string", "integer"] + } + }, + "required": ["cpu", "memory"] + } + }, + "required": ["limits", "requests"] + }, + "hooks": { + "type": "object", + "description": "Helm Hooks: pre-install and pre-delete that the chart run during install", + "properties": { + "timeoutTime": { + "type": ["integer", "null"], + "description": "The timeout time the hook will wait for the webhook-server is ready." + }, + "image": { + "type": "object", + "description": "The image for running kubectl commands", + "properties": { + "repository": { + "type": "string" + }, + "sha": { + "type": "string", + "pattern": "^sha256\\W[a-f0-9]{64}$" + }, + "pullPolicy": { + "type": "string", + "pattern": "^(Always|Never|IfNotPresent)$" + } + }, + "required": ["repository", "sha", "pullPolicy"] + } + }, + "required": ["image"] + }, + "aws": { + "type": "object", + "description": "AWS Marketplace configuration", + "properties": { + "licenseConfigSecretName": { + "type": "string", + "description": "The name of the secret that contains the license configuration." + }, + "region": { + "type": "string", + "description": "The AWS Region", + "pattern": "(af|ap|ca|eu|me|sa|us)-(central|north|(north(?:east|west))|south|south(?:east|west)|east|west)-\\d+" + }, + "enableCheckEntitlement": { + "type": "boolean", + "description": "Enable or not AWS Marketplace license checkout, this is relevant for paid products only." + }, + "productId": { + "type": "string", + "description": "The application's Product SKU (Product ID)" + }, + "issuerKey": { + "type": "string", + "description": "The trusted issuer of the license (AWS Marketplace)", + "enum": ["aws:294406891311:AWS/Marketplace:issuer-fingerprint"] + } + }, + "required": [ + "licenseConfigSecretName", + "region", + "enableCheckEntitlement", + "productId", + "issuerKey" + ] + } + }, + "required": [ + "namespace", + "replicaCount", + "customLabels", + "customAnnotations", + "debug", + "rbac", + "datree", + "image", + "securityContext", + "resources", + "hooks", + "aws" + ] +} diff --git a/charts/datree-free-admission-webhook-awsmp/values.yaml b/charts/datree-free-admission-webhook-awsmp/values.yaml new file mode 100644 index 00000000..4164ff41 --- /dev/null +++ b/charts/datree-free-admission-webhook-awsmp/values.yaml @@ -0,0 +1,100 @@ +# Default values for datree-admission-webhook. + +# The name of the namespace all resources will be created in, if not specified in the release. +namespace: "" + +# The number of Datree webhook-server replicas to deploy for the webhook. +replicaCount: 2 + +# Additional labels to add to all resources. +customLabels: {} + +# Additional annotations to add to all resources. +customAnnotations: {} + +# Run the webhook-server in debug mode, this will log debug information to the console. +debug: false + +# Create ClusterRoles, ClusterRoleBindings, and ServiceAccount for datree-webhook-server +rbac: + serviceAccount: + # Create the ServiceAccount + create: true + # The ServiceAccount name + name: datree-webhook-server + clusterRole: + # Create the ClusterRole + create: true + # The ClusterRole name + name: datree-webhook-server-read + +# Datree webhook configuration, checkout more details at htttps://hub.datree.com +datree: + # The token used to link the CLI to your dashboard. + token: "" + # The name of the policy to check, e.g: staging. (string, optional) + policy: "" + # Display 'How to Fix' link for failed rules in output. (boolean ,optional) + verbose: "" + # The format output of the policy check results: yaml, json, xml, simple, JUnit. (string ,optional) + output: "" + # Don’t send policy checks metadata to the backend. (boolean ,optional) + noRecord: "" + +# The Datree webhook-server image to use. +image: + # Image repository + repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-free-admission-webhook-awsmp + # Image tag + tag: 0.1.24-rc.2 + # Image pull policy + pullPolicy: Always + +initContainer: + repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-free-cert-generator + tag: 1.0-rc.1 + +imageWebhook: + # Image repository + repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/datree-free-webhook-init + # Image tag + tag: 1.0-rc.1 + +# Security context for the containers +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 25000 + +resources: + limits: + memory: 512Mi + requests: + cpu: 1000m + memory: 512Mi + +# During install Datree run two hooks: pre-install and pre-delete. +# `datree-label-namespaces-hook-post-install` - Helm hook that run after the chart is installed and label namespaces with datree.io/skip=true +# `datree-wait-server-ready-hook-post-install` - Helm hook that run after the chart is awaits for the webhook-server to be ready +hooks: + # The timeout time the hook will wait for the webhook-server is ready. + timeoutTime: + # The image for running kubectl commands + image: + repository: 709825985650.dkr.ecr.us-east-1.amazonaws.com/datree/bitnami-kubectl-datree-free + sha: sha256:2c963c1aae87f544a53242348d64dce4bc39739211989394833322d29364b73c + pullPolicy: IfNotPresent + +# AWS Marketplace configuration +aws: + # The name of the secret that contains the license configuration. + licenseConfigSecretName: "" + # The AWS Region + region: "us-east-1" + # Enable AWS Marketplace license checkout, this is relevant for paid products only. + enableCheckEntitlement: true + # The application’s Product SKU (Product ID) + productId: "e3c1bf72-7c57-435e-be33-deddbe7220f2" + # The trusted issuer of the license (AWS Marketplace) + issuerKey: "aws:294406891311:AWS/Marketplace:issuer-fingerprint" diff --git a/charts/datree-lib/Chart.yaml b/charts/datree-lib/Chart.yaml new file mode 100644 index 00000000..1dd29011 --- /dev/null +++ b/charts/datree-lib/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: datree-lib +description: A Helm chart library for Datree admission webhook for Kubernetes clusters +type: library +version: 0.1.1 +appVersion: "0.1.21" diff --git a/charts/datree-admission-webhook/templates/namespace-post-delete.yaml b/charts/datree-lib/templates/_cleanup-pre-delete.yaml similarity index 95% rename from charts/datree-admission-webhook/templates/namespace-post-delete.yaml rename to charts/datree-lib/templates/_cleanup-pre-delete.yaml index 672cce07..c5a20c1b 100644 --- a/charts/datree-admission-webhook/templates/namespace-post-delete.yaml +++ b/charts/datree-lib/templates/_cleanup-pre-delete.yaml @@ -1,3 +1,4 @@ +{{- define "datree-lib.cleanup-pre-delete" -}} apiVersion: batch/v1 kind: Job metadata: @@ -32,3 +33,4 @@ spec: - >- kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io datree-webhook -n {{template "datree.namespace" .}}; kubectl label ns kube-system {{template "datree.namespace" .}} datree.io/skip-; +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_clusterrole.yaml b/charts/datree-lib/templates/_clusterrole.yaml new file mode 100644 index 00000000..39346086 --- /dev/null +++ b/charts/datree-lib/templates/_clusterrole.yaml @@ -0,0 +1,98 @@ +{{- define "datree-lib.clusterrole" -}} +{{- if .Values.rbac.clusterRole.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{.Values.rbac.clusterRole.name}} + labels: {{ include "datree.labels" . | nindent 4 }} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + - "admissionregistration.k8s.io" + resources: + - "nodes" + - "namespaces" + - "validatingwebhookconfigurations" + - "pods" + verbs: + - "get" + - "list" + - "create" + - "delete" + - "patch" + - "update" + - "watch" +{{- end}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: datree-namespaces-update + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - update + - patch + resourceNames: + - kube-system + - {{template "datree.namespace" .}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: datree-validationwebhook-delete + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "admissionregistration.k8s.io" + resources: + - validatingwebhookconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + resourceNames: + - datree-webhook +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: datree-deployments-delete + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "apps" + resources: + - "deployments" + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + resourceNames: + - datree-webhook-server +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_clusterrolebinding.yaml b/charts/datree-lib/templates/_clusterrolebinding.yaml new file mode 100644 index 00000000..bd3d1db0 --- /dev/null +++ b/charts/datree-lib/templates/_clusterrolebinding.yaml @@ -0,0 +1,74 @@ +{{- define "datree-lib.clusterrolebinding" -}} +{{- if .Values.rbac.clusterRole.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{.Values.rbac.clusterRole.name}} + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{.Values.rbac.clusterRole.name}} # datree-webhook-server-read +subjects: + - kind: ServiceAccount + name: {{.Values.rbac.serviceAccount.name}} # datree-webhook-server + namespace: {{template "datree.namespace" .}} +--- +{{- end }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: datree-namespaces-update + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: datree-namespaces-update +subjects: + - kind: ServiceAccount + name: "datree-label-namespaces-hook-post-install" + namespace: "{{template "datree.namespace" .}}" + - kind: ServiceAccount + name: "datree-cleanup-namespaces-hook-pre-delete" + namespace: "{{template "datree.namespace" .}}" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: datree-validationwebhook-delete + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: datree-validationwebhook-delete +subjects: + - kind: ServiceAccount + name: "datree-cleanup-namespaces-hook-pre-delete" + namespace: "{{template "datree.namespace" .}}" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: datree-deployments-delete + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: datree-deployments-delete +subjects: + - kind: ServiceAccount + name: "datree-cleanup-namespaces-hook-pre-delete" + namespace: "{{template "datree.namespace" .}}" +{{- end -}} diff --git a/charts/datree-lib/templates/_deployment.yaml b/charts/datree-lib/templates/_deployment.yaml new file mode 100644 index 00000000..ba01d667 --- /dev/null +++ b/charts/datree-lib/templates/_deployment.yaml @@ -0,0 +1,102 @@ +{{- define "datree-lib.deployment" -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: datree-webhook-server + namespace: {{ template "datree.namespace" . }} + labels: {{ include "datree.labels" . | nindent 4 }} + owner: datree + app: "datree-webhook-server" + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: "datree-webhook-server" + template: + metadata: + labels: {{ include "datree.labels" . | nindent 8 }} + app: "datree-webhook-server" + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} + spec: + serviceAccountName: {{.Values.rbac.serviceAccount.name}} + containers: + - name: server + securityContext: {{- toYaml .Values.securityContext | nindent 12 }} + livenessProbe: + httpGet: + path: /health + port: 8443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /ready + port: 8443 + scheme: HTTPS + initialDelaySeconds: 5 + periodSeconds: 10 + resources: {{- toYaml .Values.resources | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{.Values.image.pullPolicy}} + ports: + - containerPort: 8443 + name: webhook-api + # caution: don't change the order of the environment variables + # changing the order will harm resource patching + env: + - name: DEBUG + value: "{{ .Values.debug }}" + # Datree webhook varaibles + - name: DATREE_TOKEN + value: {{.Values.datree.token}} + - name: DATREE_POLICY + value: {{.Values.datree.policy}} + - name: DATREE_VERBOSE + value: {{.Values.datree.verbose}} + - name: DATREE_OUTPUT + value: {{.Values.datree.output}} + - name: DATREE_NO_RECORD + value: {{.Values.datree.noRecord}} + # AWS Marketplace varaibles + - name: AWS_MP_PRODUCT_ID + value: {{ .Values.aws.productId }} + - name: AWS_MP_KEY_FINGERPRINT + value: {{ .Values.aws.issuerKey }} + - name: AWS_MP_ENABLE_CHECK_ENTITLEMENT + value: "{{.Values.aws.enableCheckEntitlement}}" + - name: AWS_MP_REGION + value: {{.Values.aws.region}} + {{- if .Values.aws.licenseConfigSecretName }} + - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE + value: "/var/run/secrets/awsmp-product-license/license_token" + - name: AWS_ROLE_ARN + valueFrom: + secretKeyRef: + name: {{ .Values.aws.licenseConfigSecretName }} + key: iam_role + {{- end}} + # add aws marketplace license config to the licensed container application env + volumeMounts: + - name: webhook-tls-certs + mountPath: /run/secrets/tls + readOnly: true + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + mountPath: "/var/run/secrets/awsmp-product-license" + {{- end}} + volumes: + - name: webhook-tls-certs + secret: + secretName: webhook-server-tls + {{- if .Values.aws.licenseConfigSecretName }} + - name: awsmp-product-license + secret: + secretName: {{ .Values.aws.licenseConfigSecretName }} + {{- end}} +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_namespace-post-install.yaml b/charts/datree-lib/templates/_namespace-post-install.yaml new file mode 100644 index 00000000..4c4fa232 --- /dev/null +++ b/charts/datree-lib/templates/_namespace-post-install.yaml @@ -0,0 +1,38 @@ +{{- define "datree-lib.namespace-post-install" -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: datree-label-namespaces-hook-post-install + namespace: {{template "datree.namespace" .}} + labels: {{include "datree.labels" . | nindent 4}} + annotations: + "helm.sh/hook": post-install, post-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": hook-succeeded, hook-failed + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations }} + {{- end }} +spec: + template: + metadata: + labels: {{include "datree.labels" . | nindent 8}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} + spec: + serviceAccount: datree-label-namespaces-hook-post-install + restartPolicy: OnFailure + nodeSelector: + kubernetes.io/os: linux + containers: + - name: kubectl-label + image: "{{ .Values.hooks.image.repository }}@{{ .Values.hooks.image.sha }}" + imagePullPolicy: {{.Values.hooks.image.pullPolicy}} + args: + - label + - ns + - kube-system + - {{template "datree.namespace" .}} + - datree.io/skip=true + - --overwrite +{{- end -}} diff --git a/charts/datree-lib/templates/_role.yaml b/charts/datree-lib/templates/_role.yaml new file mode 100644 index 00000000..098c7ac9 --- /dev/null +++ b/charts/datree-lib/templates/_role.yaml @@ -0,0 +1,20 @@ +{{- define "datree-lib.role" -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: datree-pods-reader + labels: {{ include "datree.labels" . | nindent 4 }} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - "pods" + - "jobs" + verbs: + - "get" + - "list" + - "watch" +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_rolebinding.yaml b/charts/datree-lib/templates/_rolebinding.yaml new file mode 100644 index 00000000..04a62237 --- /dev/null +++ b/charts/datree-lib/templates/_rolebinding.yaml @@ -0,0 +1,18 @@ +{{- define "datree-lib.rolebinding" -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: datree-pods-reader + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: datree-pods-reader +subjects: + - kind: ServiceAccount + name: datree-wait-server-ready-hook-post-install + namespace: "{{template "datree.namespace" .}}" +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_service.yaml b/charts/datree-lib/templates/_service.yaml new file mode 100644 index 00000000..6d6c4b0d --- /dev/null +++ b/charts/datree-lib/templates/_service.yaml @@ -0,0 +1,17 @@ +{{- define "datree-lib.service" -}} +apiVersion: v1 +kind: Service +metadata: + name: datree-webhook-server + namespace: {{template "datree.namespace" .}} + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +spec: + selector: + app: "datree-webhook-server" + ports: + - port: 443 + targetPort: webhook-api +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_serviceaccount.yaml b/charts/datree-lib/templates/_serviceaccount.yaml new file mode 100644 index 00000000..a0d893f3 --- /dev/null +++ b/charts/datree-lib/templates/_serviceaccount.yaml @@ -0,0 +1,40 @@ +{{- define "datree-lib.serviceaccount" -}} +{{- if .Values.rbac.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{.Values.rbac.serviceAccount.name}} + namespace: {{ template "datree.namespace" . }} + labels: {{ include "datree.labels" . | nindent 4 }} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +{{- end}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: datree-label-namespaces-hook-post-install + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: datree-cleanup-namespaces-hook-pre-delete + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: datree-wait-server-ready-hook-post-install + labels: {{include "datree.labels" . | nindent 4}} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_wait-server-ready-post-install.yaml b/charts/datree-lib/templates/_wait-server-ready-post-install.yaml new file mode 100644 index 00000000..0a33eadc --- /dev/null +++ b/charts/datree-lib/templates/_wait-server-ready-post-install.yaml @@ -0,0 +1,32 @@ +{{- define "datree-lib.wait-server-ready-post-install" -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: datree-wait-server-ready-hook-post-install + namespace: {{template "datree.namespace" .}} + labels: {{ include "datree.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": hook-succeeded, hook-failed + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations }} + {{- end }} +spec: + template: + metadata: + name: datree-wait-server-ready-hook-post-install + labels: {{ include "datree.labels" . | nindent 8 }} + spec: + serviceAccountName: datree-wait-server-ready-hook-post-install + restartPolicy: Never + containers: + - name: kubectl-client + image: "{{ .Values.hooks.image.repository }}@{{ .Values.hooks.image.sha }}" + imagePullPolicy: {{.Values.hooks.image.pullPolicy}} + command: + - sh + - "-c" + - >- + kubectl wait --for=condition=ready pod -l app=datree-webhook-server --timeout="{{ default "180s" .Values.hooks.timeoutTime }}" +{{- end -}} \ No newline at end of file diff --git a/charts/datree-lib/templates/_webhook-with-cert-secrets.yaml b/charts/datree-lib/templates/_webhook-with-cert-secrets.yaml new file mode 100644 index 00000000..c8596a27 --- /dev/null +++ b/charts/datree-lib/templates/_webhook-with-cert-secrets.yaml @@ -0,0 +1,67 @@ +{{ define "datree-lib.webhook-with-cert-secrets" }} +{{- $ca := genCA "/CN=Admission Controller Webhook Demo CA" 1827 -}} +{{- $webhookServerSAN := printf "datree-webhook-server.%s.svc" (include "datree.namespace" .) -}} +{{- $cert := genSignedCert (printf "/CN=%s" $webhookServerSAN) nil (list ($webhookServerSAN )) 1827 $ca -}} +apiVersion: v1 +kind: Secret +metadata: + name: datree-ca-tls + labels: {{ include "datree.labels" . | nindent 4 }} + namespace: {{ template "datree.namespace" . }} + {{- if .Values.customAnnotations }} + annotations: {{- toYaml .Values.customAnnotations | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.key: {{ $ca.Key | b64enc }} + tls.crt: {{ $ca.Cert | b64enc }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: webhook-server-tls + labels: {{ include "datree.labels" . | nindent 4 }} + namespace: {{ template "datree.namespace" . }} + annotations: + self-signed-cert: "true" + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations}} + {{- end }} +type: kubernetes.io/tls +data: + tls.key: {{ $cert.Key | b64enc }} + tls.crt: {{ $cert.Cert | b64enc }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: datree-webhook + annotations: + "helm.sh/hook": post-install, post-upgrade + "helm.sh/hook-weight": "-5" + {{- if .Values.customAnnotations }} + {{- toYaml .Values.customAnnotations}} + {{- end }} +webhooks: + - name: webhook-server.datree.svc + sideEffects: None + timeoutSeconds: 30 + admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: datree-webhook-server + namespace: {{ template "datree.namespace" . }} + path: "/validate" + caBundle: {{ $ca.Cert | b64enc }} + namespaceSelector: + matchExpressions: + - key: admission.datree/validate + operator: DoesNotExist + rules: + - operations: ["CREATE", "UPDATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["*"] +{{- end }} \ No newline at end of file diff --git a/cmd/cert-generator/Dockerfile b/cmd/cert-generator/Dockerfile new file mode 100644 index 00000000..9dfac329 --- /dev/null +++ b/cmd/cert-generator/Dockerfile @@ -0,0 +1,21 @@ +FROM golang:1.18-alpine AS builder + +ARG BUILD_ENVIRONMENT +ARG WEBHOOK_VERSION + +WORKDIR /go/src/app + + + +# download dependencies in a separate step to allow caching +COPY go.* . +RUN go mod download + +COPY . . +# cache the build +RUN --mount=type=cache,target=/root/.cache/go-build go build -o cert-generator -tags $BUILD_ENVIRONMENT -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=$WEBHOOK_VERSION" ./cmd/cert-generator + +FROM alpine:3.14 +COPY --from=builder /go/src/app/cert-generator / +EXPOSE 8443 +ENTRYPOINT ["/cert-generator"] diff --git a/cmd/cert-generator/keypair/keypair.go b/cmd/cert-generator/keypair/keypair.go new file mode 100644 index 00000000..9fedcfba --- /dev/null +++ b/cmd/cert-generator/keypair/keypair.go @@ -0,0 +1,63 @@ +package keypair + +import ( + "crypto/rand" + cryptorand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "fmt" +) + +const ErrInvalidTemplate = "invalid template" +const ErrInvalidArgs = "invalid args, templ, caCert and caKey must not be nil" + +// generateCA creates the self-signed CA cert and private key +// it will be used to sign the webhook server certificate +func GenerateCA(key *rsa.PrivateKey, templ *x509.Certificate) (*rsa.PrivateKey, *x509.Certificate, error) { + if templ == nil { + return nil, nil, fmt.Errorf(ErrInvalidTemplate) + } + + if key == nil { + newKey, err := rsa.GenerateKey(cryptorand.Reader, 2048) + if err != nil { + return nil, nil, err + } + key = newKey + } + + der, err := x509.CreateCertificate(cryptorand.Reader, templ, templ, &key.PublicKey, key) + if err != nil { + return nil, nil, err + } + + cert, err := x509.ParseCertificate(der) + if err != nil { + return nil, nil, err + } + + return key, cert, nil +} + +// generateTLS takes the results of GenerateCACert and uses it to create the +// PEM-encoded public certificate and private key, respectively +func GenerateTLS(caCert *x509.Certificate, caKey *rsa.PrivateKey, templ *x509.Certificate) (*rsa.PrivateKey, *x509.Certificate, error) { + if caCert == nil || caKey == nil || templ == nil { + return nil, nil, fmt.Errorf(ErrInvalidArgs) + } + + key, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return nil, nil, err + } + der, err := x509.CreateCertificate(rand.Reader, templ, caCert, key.Public(), caKey) + if err != nil { + return nil, nil, fmt.Errorf("create certificate failed, err: %v", err) + } + + cert, err := x509.ParseCertificate(der) + if err != nil { + return nil, nil, fmt.Errorf("parse certificate failed, err: %v", err) + } + return key, cert, nil +} diff --git a/cmd/cert-generator/keypair/keypair_test.go b/cmd/cert-generator/keypair/keypair_test.go new file mode 100644 index 00000000..1ef4d1f4 --- /dev/null +++ b/cmd/cert-generator/keypair/keypair_test.go @@ -0,0 +1,234 @@ +package keypair + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +type Condition[T any] func(actual T) bool + +type ExpectedCondition[T any] struct { + condition Condition[T] + message string +} + +type Expected[T any] struct { + value T + message string +} + +func TestGenerateCA(t *testing.T) { + type testArgs struct { + key *rsa.PrivateKey + templ *x509.Certificate + } + + type testCase struct { + args *testArgs + expectedErr Expected[error] + expectedKey ExpectedCondition[*rsa.PrivateKey] + expectedCert ExpectedCondition[*x509.Certificate] + } + + tests := map[string]*testCase{ + "should create CA certificate and private key when key is nil": { + args: &testArgs{ + key: nil, + templ: caCertificateTemplateMock(), + }, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { + return actual.IsCA && actual.SerialNumber.Cmp(big.NewInt(1)) == 0 && actual.Subject.Organization[0] == "/CN=datree.io" && (actual.NotAfter.Sub(actual.NotBefore) == 10*time.Hour) + }, + message: "certificate should be a valid CA certificate and contain given template values", + }, + expectedErr: Expected[error]{ + value: nil, + message: "should not return an error", + }, + expectedKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { + return actual.Validate() == nil + }, + message: "private key should be valid", + }, + }, + "should create CA certificate and private key when key is not nil": { + args: &testArgs{ + key: nil, + templ: caCertificateTemplateMock(), + }, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { + return actual.IsCA + }, + message: "certificate should be a valid CA certificate", + }, + expectedErr: Expected[error]{ + value: nil, + message: "should not return an error", + }, + expectedKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { + return actual.Validate() == nil + }, + message: "private key should be valid", + }, + }, + "should return an error when template is nil": { + args: &testArgs{key: nil, templ: nil}, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { return actual == nil }, + message: "certificate should be nil", + }, + expectedErr: Expected[error]{ + value: fmt.Errorf(ErrInvalidTemplate), + message: "should return an error", + }, + expectedKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { return actual == nil }, + message: "private key should be nil", + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualyPrivKey, actualCert, actualErr := GenerateCA(tc.args.key, tc.args.templ) + assert.Equal(t, tc.expectedErr.value, actualErr, tc.expectedErr.message) + assert.True(t, tc.expectedCert.condition(actualCert), tc.expectedCert.message) + assert.True(t, tc.expectedKey.condition(actualyPrivKey), tc.expectedKey.message) + }) + } +} + +func TestGenerateTLS(t *testing.T) { + type testArgs struct { + caCert *x509.Certificate + caKey *rsa.PrivateKey + templ *x509.Certificate + } + + type testCase struct { + args *testArgs + expectedErr Expected[error] + expectedKey ExpectedCondition[*rsa.PrivateKey] + expectedCert ExpectedCondition[*x509.Certificate] + } + + privateKey, _ := rsa.GenerateKey(rand.Reader, 2048) + serverCert := serverCertificateMock() + + tests := map[string]*testCase{ + "should create CA certificate and private key when key is nil": { + args: &testArgs{ + caKey: privateKey, + caCert: caCertificateTemplateMock(), + templ: serverCert, + }, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { + return !actual.IsCA && actual.SerialNumber.Cmp(serverCert.SerialNumber) == 0 && actual.DNSNames[0] == serverCert.DNSNames[0] && (actual.NotAfter.Sub(actual.NotBefore) == 10*time.Hour) + }, + message: "server certificate should be valid certificate and contain given template values", + }, + expectedErr: Expected[error]{ + value: nil, + message: "should not return an error", + }, + expectedKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { + return actual.Validate() == nil + }, + message: "private key should be valid", + }, + }, + "should return error when private key is nil": { + args: &testArgs{ + caKey: nil, + caCert: caCertificateTemplateMock(), + templ: serverCert, + }, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { + return actual == nil + }, + message: "server certificate should be nil", + }, + expectedErr: Expected[error]{ + value: fmt.Errorf(ErrInvalidArgs), + message: "should return an error", + }, + expectedKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { + return actual == nil + }, + message: "private key should be nil", + }, + }, + "should return an error when template is nil": { + args: &testArgs{caKey: privateKey, caCert: caCertificateTemplateMock(), templ: nil}, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { return actual == nil }, + message: "certificate should be nil", + }, + expectedErr: Expected[error]{ + value: fmt.Errorf(ErrInvalidArgs), + message: "should return an error", + }, + expectedKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { return actual == nil }, + message: "private key should be nil", + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualPrivKey, actualCert, actualErr := GenerateTLS(tc.args.caCert, tc.args.caKey, tc.args.templ) + assert.Equal(t, tc.expectedErr.value, actualErr, tc.expectedErr.message) + assert.True(t, tc.expectedCert.condition(actualCert), tc.expectedCert.message) + assert.True(t, tc.expectedKey.condition(actualPrivKey), tc.expectedKey.message) + }) + } +} + +func caCertificateTemplateMock() *x509.Certificate { + return &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"/CN=datree.io"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(10 * time.Hour), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } +} + +func serverCertificateMock() *x509.Certificate { + return &x509.Certificate{ + DNSNames: []string{ + "localhost", + }, + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: fmt.Sprintf("/CN=%v", "localhost"), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(10 * time.Hour), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } +} diff --git a/cmd/cert-generator/main.go b/cmd/cert-generator/main.go new file mode 100644 index 00000000..a84d87ed --- /dev/null +++ b/cmd/cert-generator/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "bytes" + "os" + + "github.com/datreeio/admission-webhook-datree/cmd/cert-generator/renewer" + "github.com/datreeio/admission-webhook-datree/pkg/logger" +) + +func main() { + tlsDir, isFound := os.LookupEnv("WEBHOOK_CERTS_DIR") + if !isFound { + logger.LogUtil("required directory for certificates is missing, verify env varaible WEBHOOK_CERTS_DIR in deployment") + return + } + + err := os.MkdirAll(tlsDir, 0666) + if err != nil { + logger.LogUtil(err.Error()) + return + } + + renewer := renewer.NewCertRenewer(&fileWriter{}) + + caPrivKey, caCert, err := renewer.RenewCA(tlsDir) + if err != nil { + logger.LogUtil(err.Error()) + return + } + + err = renewer.RenewTLS(tlsDir, caCert, caPrivKey) + if err != nil { + logger.LogUtil(err.Error()) + return + } + + logger.LogUtil("horray! successfully generated self-signed CA and signed webhook server certificate using this CA!") +} + +type fileWriter struct{} + +// WriteFile writes data in the file at the given path +func (fw *fileWriter) WriteFile(filepath string, sCert *bytes.Buffer) error { + f, err := os.Create(filepath) + if err != nil { + return err + } + defer f.Close() + + _, err = f.Write(sCert.Bytes()) + if err != nil { + return err + } + return nil +} diff --git a/cmd/cert-generator/main_test.go b/cmd/cert-generator/main_test.go new file mode 100644 index 00000000..4ba502b9 --- /dev/null +++ b/cmd/cert-generator/main_test.go @@ -0,0 +1,47 @@ +package main + +import ( + "crypto/x509" + "encoding/pem" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMain(m *testing.M) { + os.Setenv("WEBHOOK_CERTS_DIR", "/tmp") + m.Run() +} + +func TestRenewCA(t *testing.T) { + main() + + // Read server cert + certPEM, err := ioutil.ReadFile("/tmp/tls.crt") + assert.True(t, err == nil) + + // Read ca bundle + caPEM, err := ioutil.ReadFile("/tmp/ca-bundle.pem") + assert.True(t, err == nil) + + // Parse ca bundle and append to cert-pool + roots := x509.NewCertPool() + assert.True(t, roots.AppendCertsFromPEM([]byte(caPEM))) + + // Parse server cert + block, _ := pem.Decode([]byte(certPEM)) + assert.True(t, block != nil) + cert, err := x509.ParseCertificate(block.Bytes) + assert.True(t, err == nil) + + // Verify server cert is valid and signed by ca + opts := x509.VerifyOptions{ + Roots: roots, + Intermediates: x509.NewCertPool(), + } + + _, err = cert.Verify(opts) + assert.True(t, err == nil) +} diff --git a/cmd/cert-generator/renewer/renew.go b/cmd/cert-generator/renewer/renew.go new file mode 100644 index 00000000..591b4402 --- /dev/null +++ b/cmd/cert-generator/renewer/renew.go @@ -0,0 +1,148 @@ +package renewer + +import ( + "bytes" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + "math/big" + "os" + "path/filepath" + "time" + + "github.com/datreeio/admission-webhook-datree/cmd/cert-generator/keypair" +) + +type Writer interface { + WriteFile(filepath string, sCert *bytes.Buffer) error +} + +type CertRenewer struct { + w Writer + tlsValidityDuration time.Duration +} + +func NewCertRenewer(w Writer) *CertRenewer { + return &CertRenewer{ + w: w, + tlsValidityDuration: 5 * 365 * 24 * time.Hour, // 5 years + } +} + +func (r *CertRenewer) RenewCA(tlsDir string) (*rsa.PrivateKey, *x509.Certificate, error) { + templ := buildDatreeWebhookCATemplate(r.tlsValidityDuration) + + caPrivKey, caCert, err := keypair.GenerateCA(nil, templ) + if err != nil { + return nil, nil, err + } + + err = r.writeCABundleFile(tlsDir, caCert.Raw) + if err != nil { + return nil, nil, err + } + + return caPrivKey, caCert, nil + +} + +func (r *CertRenewer) RenewTLS(tlsDir string, caCert *x509.Certificate, caPrivKey *rsa.PrivateKey) error { + // sign the server cert with Datree CA + + templ := buildDatreeWebhookServerCertificateTemplate(r.tlsValidityDuration) + serverPrivKey, serverCert, err := keypair.GenerateTLS(caCert, caPrivKey, templ) + if err != nil { + return err + } + + // PEM encode the server cert and key + serverCertPEM, err := certificateToPem(serverCert.Raw) + if err != nil { + return err + } + + err = r.w.WriteFile(filepath.Join(tlsDir, `tls.crt`), serverCertPEM) + if err != nil { + return err + } + + serverPrivKeyPEM, err := privateKeyToPem(serverPrivKey) + if err != nil { + return err + } + + err = r.w.WriteFile(filepath.Join(tlsDir, `tls.key`), serverPrivKeyPEM) + if err != nil { + return err + } + + return nil +} + +func (r *CertRenewer) writeCABundleFile(tlsDir string, caCertificate []byte) error { + // pem encode a bundle of the CA certificates + // this is used to verify that the server is really the correct site you're talking to + datreeCACertPEM, err := certificateToPem(caCertificate) + if err != nil { + return err + } + + err = r.w.WriteFile(filepath.Join(tlsDir, "ca-bundle.pem"), datreeCACertPEM) + if err != nil { + return err + } + + return nil +} + +func buildDatreeWebhookCATemplate(certValidityDuration time.Duration) *x509.Certificate { + now := time.Now() + begin, end := now, now.Add(certValidityDuration) + + return &x509.Certificate{ + SerialNumber: big.NewInt(2022), + Subject: pkix.Name{ + Organization: []string{"/CN=Datree Admission Controller Webhook CA"}, + }, + NotBefore: begin, + NotAfter: end, + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + // keyCertSign bit is for verifying signatures on public key certificates. (e.g. CA certificate) + // KeyUsageDigitalSignature bit is for verifying signatures on digital signatures. Read more: https://ldapwiki.com/wiki/KeyCertSign + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } +} + +func buildDatreeWebhookServerCertificateTemplate(certValidityDuration time.Duration) *x509.Certificate { + webhookDNS := getWebhookServerDNSName() + + now := time.Now() + begin, end := now, now.Add(certValidityDuration) + + return &x509.Certificate{ + DNSNames: []string{ + webhookDNS, + }, + SerialNumber: big.NewInt(1658), + Subject: pkix.Name{ + CommonName: fmt.Sprintf("/CN=%v", webhookDNS), + }, + NotBefore: begin, + NotAfter: end, + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } +} + +func getWebhookServerDNSName() string { + webhookDNS, isFound := os.LookupEnv("WEBHOOK_SERVER_DNS") + if !isFound { + return "datree-webhook-server.datree.svc" + } + + return webhookDNS +} diff --git a/cmd/cert-generator/renewer/renew_test.go b/cmd/cert-generator/renewer/renew_test.go new file mode 100644 index 00000000..c81e2f32 --- /dev/null +++ b/cmd/cert-generator/renewer/renew_test.go @@ -0,0 +1,175 @@ +package renewer + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + "math/big" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type Condition[T any] func(actual T) bool + +type ExpectedCondition[T any] struct { + condition Condition[T] + message string +} + +type Expected[T any] struct { + value T + message string +} + +type FileWriterMock struct { + mock.Mock +} + +func (m *FileWriterMock) WriteFile(filepath string, sCert *bytes.Buffer) error { + args := m.Called(filepath, sCert) + return args.Error(0) +} + +func TestRenewCA(t *testing.T) { + type testArgs struct { + tlsDir string + } + + type testCase struct { + args *testArgs + expectedErr Expected[error] + expectedPrivKey ExpectedCondition[*rsa.PrivateKey] + expectedCert ExpectedCondition[*x509.Certificate] + writerMockExpectedCalls *[]*mock.Call + } + + tlsDir := "testdata" + + tests := map[string]*testCase{ + "should create CA certificate and write file": { + args: &testArgs{tlsDir: tlsDir}, + expectedPrivKey: ExpectedCondition[*rsa.PrivateKey]{ + condition: func(actual *rsa.PrivateKey) bool { + return actual.Validate() == nil + }, + message: "should return a valid private key", + }, + expectedErr: Expected[error]{ + value: nil, + message: "should not return an error", + }, + expectedCert: ExpectedCondition[*x509.Certificate]{ + condition: func(actual *x509.Certificate) bool { + return actual != nil && actual.IsCA && actual.Subject.Organization[0] == "/CN=Datree Admission Controller Webhook CA" && actual.NotBefore.Add(5*24*365*time.Hour).Equal(actual.NotAfter) + }, + message: "should return a certificate", + }, + writerMockExpectedCalls: &[]*mock.Call{ + { + Method: "WriteFile", + Arguments: mock.Arguments{ + filepath.Join(tlsDir, "ca-bundle.pem"), + mock.AnythingOfType("*bytes.Buffer"), + }, + }, + }, + }, + } + + writerMock := &FileWriterMock{} + writerMock.On("WriteFile", mock.Anything, mock.Anything).Return(nil) + renewer := CertRenewer{w: writerMock, tlsValidityDuration: 5 * 365 * 24 * time.Hour} + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualPrivKey, actualCert, actualErr := renewer.RenewCA(tc.args.tlsDir) + assert.Equal(t, tc.expectedErr.value, actualErr, tc.expectedErr.message) + assert.True(t, tc.expectedCert.condition(actualCert), tc.expectedCert.message) + assert.True(t, tc.expectedPrivKey.condition(actualPrivKey), tc.expectedPrivKey.message) + writerMock.ExpectedCalls = *tc.writerMockExpectedCalls + writerMock.AssertExpectations(t) + }) + } + +} + +func TestRenewTLS(t *testing.T) { + type testArgs struct { + tlsDir string + caCert *x509.Certificate + caPrivKey *rsa.PrivateKey + } + + type testCase struct { + args *testArgs + expectedErr Expected[error] + writerMockExpectedCalls *[]*mock.Call + } + + privKey, _ := rsa.GenerateKey(rand.Reader, 2048) + tlsDir := "testdata" + + tests := map[string]*testCase{ + "should create server certificate and write cert and key": { + args: &testArgs{tlsDir: tlsDir, caCert: serverCertificateMock(), caPrivKey: privKey}, + expectedErr: Expected[error]{ + value: nil, + message: "should not return an error", + }, + writerMockExpectedCalls: &[]*mock.Call{ + { + Method: "WriteFile", + Arguments: mock.Arguments{ + filepath.Join(tlsDir, "tls.crt"), + mock.AnythingOfType("*bytes.Buffer"), + }, + }, + { + Method: "WriteFile", + Arguments: mock.Arguments{ + filepath.Join(tlsDir, "tls.key"), + mock.AnythingOfType("*bytes.Buffer"), + }, + }, + }, + }, + } + + writerMock := &FileWriterMock{} + writerMock.On("WriteFile", mock.Anything, mock.Anything).Return(nil) + renewer := CertRenewer{w: writerMock, tlsValidityDuration: 5 * 365 * 24 * time.Hour} + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualErr := renewer.RenewTLS(tc.args.tlsDir, tc.args.caCert, tc.args.caPrivKey) + assert.Equal(t, tc.expectedErr.value, actualErr, tc.expectedErr.message) + writerMock.ExpectedCalls = *tc.writerMockExpectedCalls + writerMock.AssertExpectations(t) + }) + } + +} + +func serverCertificateMock() *x509.Certificate { + return &x509.Certificate{ + DNSNames: []string{ + "localhost", + }, + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: fmt.Sprintf("/CN=%v", "localhost"), + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(10 * time.Hour), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } +} diff --git a/cmd/cert-generator/renewer/tlsutils.go b/cmd/cert-generator/renewer/tlsutils.go new file mode 100644 index 00000000..7d5708fa --- /dev/null +++ b/cmd/cert-generator/renewer/tlsutils.go @@ -0,0 +1,45 @@ +package renewer + +import ( + "bytes" + cryptorand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" +) + +func certificateToPem(cert []byte) (*bytes.Buffer, error) { + certPEM := new(bytes.Buffer) + err := pem.Encode(certPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert, + }) + if err != nil { + return nil, err + } + + return certPEM, nil +} + +func privateKeyToPem(rsaKey *rsa.PrivateKey) (*bytes.Buffer, error) { + privateKeyPEM := new(bytes.Buffer) + err := pem.Encode(privateKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(rsaKey), + }) + if err != nil { + return nil, err + } + + return privateKeyPEM, nil +} + +// generate private and public key for CA with given bitKeySize +func generateRSAPrivKey(bitsSize int) (*rsa.PrivateKey, error) { + privKey, err := rsa.GenerateKey(cryptorand.Reader, bitsSize) + if err != nil { + return nil, err + } + + return privKey, nil +} diff --git a/cmd/init-webhook/Dockerfile b/cmd/init-webhook/Dockerfile new file mode 100644 index 00000000..b9fe9fab --- /dev/null +++ b/cmd/init-webhook/Dockerfile @@ -0,0 +1,21 @@ +FROM golang:1.18-alpine AS builder + +ARG BUILD_ENVIRONMENT +ARG WEBHOOK_VERSION + +WORKDIR /go/src/app + + + +# download dependencies in a separate step to allow caching +COPY go.* . +RUN go mod download + +COPY . . +# cache the build +RUN --mount=type=cache,target=/root/.cache/go-build go build -o init-webhook -tags $BUILD_ENVIRONMENT -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=$WEBHOOK_VERSION" ./cmd/init-webhook + +FROM alpine:3.14 +COPY --from=builder /go/src/app/init-webhook / +EXPOSE 8443 +ENTRYPOINT ["/init-webhook"] diff --git a/cmd/init-webhook/k8s-client/k8sclient.go b/cmd/init-webhook/k8s-client/k8sclient.go new file mode 100644 index 00000000..ea4188dc --- /dev/null +++ b/cmd/init-webhook/k8s-client/k8sclient.go @@ -0,0 +1,195 @@ +package k8sclient + +import ( + "context" + "fmt" + "time" + + "github.com/datreeio/admission-webhook-datree/pkg/logger" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + + admissionregistrationV1 "k8s.io/api/admissionregistration/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +type K8sClient struct { + clientset kubernetes.Interface +} + +func New(c kubernetes.Interface) *K8sClient { + if c != nil { + return &K8sClient{ + clientset: c, + } + } + + c, err := kubernetes.NewForConfig(ctrl.GetConfigOrDie()) + if err != nil { + return nil + } + + return &K8sClient{ + clientset: c, + } + +} + +type ValidatingWebhookOpts struct { + MetaName string + CaBundle []byte + ServiceName string + Selector string + WebhookName string +} + +func (k *K8sClient) CreateValidatingWebhookConfiguration(namespace string, cfg *ValidatingWebhookOpts) (*admissionregistrationV1.ValidatingWebhookConfiguration, error) { + if cfg == nil { + return nil, fmt.Errorf("invalid validating webhook configuration") + } + + path := "/validate" + sideEffects := admissionregistrationV1.SideEffectClassNone + + vw := &admissionregistrationV1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: cfg.MetaName, + }, + Webhooks: []admissionregistrationV1.ValidatingWebhook{{ + Name: cfg.WebhookName, + ClientConfig: admissionregistrationV1.WebhookClientConfig{ + CABundle: cfg.CaBundle, // CA bundle created earlier + Service: &admissionregistrationV1.ServiceReference{ + Name: cfg.ServiceName, // datree-webhook-server + Namespace: namespace, + Path: &path, + }, + }, + Rules: []admissionregistrationV1.RuleWithOperations{{Operations: []admissionregistrationV1.OperationType{ + admissionregistrationV1.Create, + admissionregistrationV1.Update, + }, + Rule: admissionregistrationV1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{"*"}, + }, + }}, + SideEffects: &sideEffects, + AdmissionReviewVersions: []string{"v1", "v1beta1"}, + TimeoutSeconds: &[]int32{30}[0], + NamespaceSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { // only validate pods in namespaces with the label "admission.datree/validate" + Key: cfg.Selector, + Operator: metav1.LabelSelectorOpDoesNotExist, + }, + }, + }, + }}, + } + + vw, err := k.clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.Background(), vw, metav1.CreateOptions{}) + if err != nil { + return nil, err + } + + // should be debug + logger.LogUtil(fmt.Sprintf("created validating webhook configuration: %v", vw)) + return vw, nil +} + +// search for validating webhook and delete if exists +func (k *K8sClient) DeleteExistingValidatingWebhook(name string) error { + vw := k.GetValidatingWebhookConfiguration(name) + if vw != nil { + return k.DeleteValidatingWebhookConfiguration(name) + } + return nil +} + +func (k *K8sClient) DeleteValidatingWebhookConfiguration(name string) error { + return k.clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.Background(), name, metav1.DeleteOptions{}) +} + +func (k *K8sClient) GetValidatingWebhookConfiguration(name string) *admissionregistrationV1.ValidatingWebhookConfiguration { + vw, err := k.clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.Background(), name, metav1.GetOptions{}) + if (vw != nil && vw.Name == name) && err == nil { + return vw + } + + return nil +} + +func (k *K8sClient) CreatePodWatcher(ctx context.Context, namespace string, selector string) (watch.Interface, error) { + opts := metav1.ListOptions{ + TypeMeta: metav1.TypeMeta{}, + LabelSelector: selector, + FieldSelector: "", + } + + return k.clientset.CoreV1().Pods(namespace).Watch(ctx, opts) +} + +func (k *K8sClient) WaitUntilPodsAreRunning(ctx context.Context, namespace string, selector string, replicas int) error { + logger.LogUtil(fmt.Sprintf("creating watcher for POD with label:%s ...", selector)) + watcher, err := k.CreatePodWatcher(ctx, namespace, selector) + if err != nil { + return err + } + + logger.LogUtil("watch out! Succuessfuly created watcher for PODs.") + defer watcher.Stop() + + count := 0 + for { + select { + case event := <-watcher.ResultChan(): + pod := event.Object.(*v1.Pod) + + if pod.Status.Phase == v1.PodRunning { + if k.IsPodReady(pod) { + count++ + logger.LogUtil(fmt.Sprintf("the POD \"%s\" is running", selector)) + if count == replicas { + logger.LogUtil(fmt.Sprintf("all PODs are running")) + return nil + } + } + + } + + case <-time.After(180 * time.Second): + logger.LogUtil(fmt.Sprintf("exit from waitPodRunning for POD \"%s\" because the time is over", selector)) + return nil + + case <-ctx.Done(): + logger.LogUtil(fmt.Sprintf("exit from waitPodRunning for POD \"%s\" because the context is done", selector)) + return nil + } + } +} + +func (k *K8sClient) IsPodReady(pod *v1.Pod) bool { + checkPodReadyCondition := func(condition v1.PodCondition) bool { + return condition.Type == v1.PodReady && condition.Status == "True" + } + + checkPodContainersCondition := func(condition v1.PodCondition) bool { + return condition.Type == v1.ContainersReady && condition.Status == "True" + } + + return findIndex(checkPodReadyCondition, pod.Status.Conditions) > 0 && findIndex(checkPodContainersCondition, pod.Status.Conditions) > 0 +} + +// util function, should be in a separate file but for the sake of simplicity it's here +func findIndex[T interface{}, K []T](findFn func(element T) bool, array K) (idx int) { + for i, v := range array { + if findFn(v) { + return i + } + } + return -1 +} diff --git a/cmd/init-webhook/k8s-client/k8sclient_test.go b/cmd/init-webhook/k8s-client/k8sclient_test.go new file mode 100644 index 00000000..39c31c41 --- /dev/null +++ b/cmd/init-webhook/k8s-client/k8sclient_test.go @@ -0,0 +1,87 @@ +package k8sclient + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + admissionregistrationV1 "k8s.io/api/admissionregistration/v1" + testclient "k8s.io/client-go/kubernetes/fake" +) + +type Condition[T any] func(actual T) bool + +type ExpectedCondition[T any] struct { + condition Condition[T] + message string +} + +type Expected[T any] struct { + value T + message string +} + +func TestCreateValidatingWebhookConfiguration(t *testing.T) { + type testArgs struct { + namespace string + cfg *ValidatingWebhookOpts + } + + type testCase struct { + args *testArgs + expectedErr Expected[error] + expectedWebhook ExpectedCondition[*admissionregistrationV1.ValidatingWebhookConfiguration] + } + + tests := map[string]*testCase{ + "should create a validating webhook configuration": { + args: &testArgs{namespace: "test-namespace", cfg: &ValidatingWebhookOpts{ + MetaName: "datree-webhook", + ServiceName: "webhook-server", + CaBundle: []byte("caBundle"), + Selector: "app=webhook-server", + WebhookName: "webhook-server.datree.svc", + }}, + expectedWebhook: ExpectedCondition[*admissionregistrationV1.ValidatingWebhookConfiguration]{ + condition: func(actual *admissionregistrationV1.ValidatingWebhookConfiguration) bool { + isNameValid := actual.Name == "datree-webhook" + isNamespaceValid := actual.Webhooks[0].ClientConfig.Service.Namespace == "test-namespace" + isWebhookNameValid := actual.Webhooks[0].Name == "webhook-server.datree.svc" + isWebhookServiceNameValid := actual.Webhooks[0].ClientConfig.Service.Name == "webhook-server" + isWebhookCABundleValid := string(actual.Webhooks[0].ClientConfig.CABundle) == string([]byte("caBundle")) + isWebhookSelectorValid := actual.Webhooks[0].NamespaceSelector.MatchExpressions[0].Key == "app=webhook-server" + return isNameValid && isNamespaceValid && isWebhookNameValid && isWebhookServiceNameValid && isWebhookCABundleValid && isWebhookSelectorValid + }, + message: "webhook should use the correct values from the args", + }, + expectedErr: Expected[error]{ + value: nil, + message: "should not return an error", + }, + }, + "should not create validating webhook since opts is nil": { + args: &testArgs{namespace: "test-namespace", cfg: nil}, + expectedWebhook: ExpectedCondition[*admissionregistrationV1.ValidatingWebhookConfiguration]{ + message: "webhook should be nil", + condition: func(actual *admissionregistrationV1.ValidatingWebhookConfiguration) bool { + return actual == nil + }, + }, + expectedErr: Expected[error]{ + value: fmt.Errorf("invalid validating webhook configuration"), + message: "should return an error", + }, + }, + } + + client := testclient.NewSimpleClientset() + k8sClient := New(client) + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + actualVW, actualErr := k8sClient.CreateValidatingWebhookConfiguration(tc.args.namespace, tc.args.cfg) + assert.Equal(t, tc.expectedErr.value, actualErr, tc.expectedErr.message) + assert.True(t, tc.expectedWebhook.condition(actualVW), tc.expectedWebhook.message) + }) + } +} diff --git a/cmd/init-webhook/main.go b/cmd/init-webhook/main.go new file mode 100644 index 00000000..9c8ac132 --- /dev/null +++ b/cmd/init-webhook/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "context" + "fmt" + + k8sclient "github.com/datreeio/admission-webhook-datree/cmd/init-webhook/k8s-client" + webhookinfo "github.com/datreeio/admission-webhook-datree/cmd/init-webhook/webhook-info" + "github.com/datreeio/admission-webhook-datree/pkg/logger" + admissionregistrationV1 "k8s.io/api/admissionregistration/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/watch" +) + +func main() { + k8sClient := k8sclient.New(nil) + if k8sClient == nil { + logger.LogUtil("failed to set k8s go -client, err") + } + + err := InitWebhook(k8sClient) + if err != nil { + logger.LogUtil(fmt.Sprintf("failed to init webhook, err: %v", err)) + } + logger.LogUtil("horray! succesfully created datree validating admission webhook") + + // wait forever to prevent the container from restrating + waitForever() +} + +type k8sClientInterface interface { + CreateValidatingWebhookConfiguration(namespace string, cfg *k8sclient.ValidatingWebhookOpts) (*admissionregistrationV1.ValidatingWebhookConfiguration, error) + DeleteExistingValidatingWebhook(name string) error + WaitUntilPodsAreRunning(ctx context.Context, namespace string, selector string, replicas int) error + GetValidatingWebhookConfiguration(name string) *admissionregistrationV1.ValidatingWebhookConfiguration + CreatePodWatcher(ctx context.Context, namespace string, selector string) (watch.Interface, error) + IsPodReady(pod *v1.Pod) bool +} + +func InitWebhook(k8sClient k8sClientInterface) error { + err := k8sClient.DeleteExistingValidatingWebhook("datree-webhook") + if err != nil { + logger.LogUtil(fmt.Sprintf("failed to delete existed validation webhook config, err: %v", err)) + return err + } + + err = k8sClient.WaitUntilPodsAreRunning(context.Background(), webhookinfo.GetWebhookNamespace(), webhookinfo.GetWebhookPodsSelector(), webhookinfo.GetWebhookServerReplicas()) + if err != nil { + logger.LogUtil(fmt.Sprintf("failed to wait for pods, err: %v", err)) + return err + } + + caBundle, _ := webhookinfo.GetWebhookCABundle() + _, err = k8sClient.CreateValidatingWebhookConfiguration(webhookinfo.GetWebhookNamespace(), &k8sclient.ValidatingWebhookOpts{ + MetaName: "datree-webhook", + ServiceName: webhookinfo.GetWebhookServiceName(), + CaBundle: caBundle, + Selector: webhookinfo.GetWebhookSelector(), + WebhookName: "webhook-server.datree.svc", + }) + if err != nil { + return err + } + + return nil +} + +func waitForever() { + select {} +} diff --git a/cmd/init-webhook/main_test.go b/cmd/init-webhook/main_test.go new file mode 100644 index 00000000..dbf26a1a --- /dev/null +++ b/cmd/init-webhook/main_test.go @@ -0,0 +1,74 @@ +package main + +import ( + "context" + "testing" + + k8sclient "github.com/datreeio/admission-webhook-datree/cmd/init-webhook/k8s-client" + webhookinfo "github.com/datreeio/admission-webhook-datree/cmd/init-webhook/webhook-info" + "github.com/stretchr/testify/mock" + admissionregistrationV1 "k8s.io/api/admissionregistration/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/watch" + testclient "k8s.io/client-go/kubernetes/fake" +) + +type mockK8sClient struct { + mock.Mock + clientset *testclient.Clientset +} + +func (m *mockK8sClient) CreateValidatingWebhookConfiguration(namespace string, cfg *k8sclient.ValidatingWebhookOpts) (*admissionregistrationV1.ValidatingWebhookConfiguration, error) { + args := m.Called(namespace, cfg) + return args.Get(0).(*admissionregistrationV1.ValidatingWebhookConfiguration), args.Error(1) +} + +func (m *mockK8sClient) DeleteExistingValidatingWebhook(name string) error { + args := m.Called(name) + return args.Error(0) +} + +func (m *mockK8sClient) WaitUntilPodsAreRunning(ctx context.Context, namespace string, selector string, replicas int) error { + args := m.Called(ctx, namespace, selector, replicas) + return args.Error(0) +} + +func (m *mockK8sClient) GetValidatingWebhookConfiguration(name string) *admissionregistrationV1.ValidatingWebhookConfiguration { + args := m.Called(name) + return args.Get(0).(*admissionregistrationV1.ValidatingWebhookConfiguration) +} + +func (m *mockK8sClient) CreatePodWatcher(ctx context.Context, namespace string, selector string) (watch.Interface, error) { + args := m.Called(ctx, namespace, selector) + return args.Get(0).(watch.Interface), args.Error(1) +} + +func (m *mockK8sClient) IsPodReady(pod *v1.Pod) bool { + args := m.Called(pod) + return args.Bool(0) +} + +func TestInitWebhook(t *testing.T) { + k8sClient := &mockK8sClient{ + clientset: testclient.NewSimpleClientset(), + } + + k8sClient.On("DeleteExistingValidatingWebhook", mock.Anything).Return(nil) + k8sClient.On("WaitUntilPodsAreRunning", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + k8sClient.On("GetWebhookCABundle", mock.Anything).Return([]byte{}, nil) + k8sClient.On("CreateValidatingWebhookConfiguration", mock.Anything, mock.Anything).Return(&admissionregistrationV1.ValidatingWebhookConfiguration{}, nil) + + t.Run("should delete existing webhook, call wait for pods and then create the validation webhook", func(t *testing.T) { + _ = InitWebhook(k8sClient) + + k8sClient.AssertCalled(t, "DeleteExistingValidatingWebhook", "datree-webhook") + k8sClient.AssertCalled(t, "WaitUntilPodsAreRunning", mock.Anything, webhookinfo.GetWebhookNamespace(), webhookinfo.GetWebhookPodsSelector(), webhookinfo.GetWebhookServerReplicas()) + k8sClient.AssertCalled(t, "CreateValidatingWebhookConfiguration", webhookinfo.GetWebhookNamespace(), &k8sclient.ValidatingWebhookOpts{ + MetaName: "datree-webhook", + ServiceName: webhookinfo.GetWebhookServiceName(), + CaBundle: nil, + Selector: webhookinfo.GetWebhookSelector(), + WebhookName: "webhook-server.datree.svc", + }) + }) +} diff --git a/cmd/init-webhook/webhook-info/webhookInfo.go b/cmd/init-webhook/webhook-info/webhookInfo.go new file mode 100644 index 00000000..93207fa6 --- /dev/null +++ b/cmd/init-webhook/webhook-info/webhookInfo.go @@ -0,0 +1,74 @@ +package webhookinfo + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + + "github.com/datreeio/admission-webhook-datree/pkg/logger" +) + +func GetWebhookServiceName() string { + webhookServiceName, isFound := os.LookupEnv("WEBHOOK_SERVICE") + if !isFound || webhookServiceName == "" { + logger.LogUtil("required environment variable WEBHOOK_SERVICE is missing") + return "datree-webhook-server" + } + + return webhookServiceName +} + +func GetWebhookNamespace() string { + webhookNamespace, isFound := os.LookupEnv("WEBHOOK_NAMESPACE") + if !isFound || webhookNamespace == "" { + logger.LogUtil("required environment variable WEBHOOK_NAMESPACE is missing") + return "datree" + } + + return webhookNamespace +} + +func GetWebhookSelector() string { + webhookSelector, isFound := os.LookupEnv("WEBHOOK_SELECTOR") + if !isFound || webhookSelector == "" { + logger.LogUtil("required environment variable WEBHOOK_SELECTOR is missing") + return "admission.datree/validate" + } + + return webhookSelector +} + +func GetWebhookCABundle() ([]byte, error) { + certPath := filepath.Join(`/etc/webhook/certs`, `ca-bundle.pem`) + caPEM, err := os.ReadFile(certPath) + if err != nil { + return nil, err + } + + return caPEM, nil +} + +func GetWebhookServerReplicas() int { + replicasStr, isFound := os.LookupEnv("WEBHOOK_SERVER_REPLICAS") + if !isFound || replicasStr == "" { + logger.LogUtil("required environment variable WEBHOOK_SERVER_REPLICAS is missing") + return 2 + } + replicas, err := strconv.Atoi(replicasStr) + if err != nil { + logger.LogUtil(fmt.Sprintf("invalid value for env variable WEBHOOK_SERVER_REPLICAS, err: %v", err)) + return 2 + } + + return replicas +} + +func GetWebhookPodsSelector() string { + podsSelector, isFound := os.LookupEnv("WEBHOOK_POD_SELECTOR") + if !isFound || podsSelector == "" { + logger.LogUtil("required environment variable WEBHOOK_POD_SELECTOR is missing") + return "app=datree-webhook-server" + } + return podsSelector +} diff --git a/cmd/init-webhook/webhook-info/webhookInfo_test.go b/cmd/init-webhook/webhook-info/webhookInfo_test.go new file mode 100644 index 00000000..c9771f01 --- /dev/null +++ b/cmd/init-webhook/webhook-info/webhookInfo_test.go @@ -0,0 +1,205 @@ +package webhookinfo + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +type Condition[T any] func(actual T) bool + +type ExpectedCondition[T any] struct { + condition Condition[T] + message string +} + +func TestGetWebhookServerReplicas(t *testing.T) { + type testCase struct { + envVarValue string + expected ExpectedCondition[int] + } + + tests := map[string]*testCase{ + "should return 2 when replicas is not set": { + envVarValue: "", + expected: ExpectedCondition[int]{ + condition: func(actual int) bool { + return actual == 2 + }, + message: "expected replicas to be 2, got %d", + }, + }, + "should return 2 when replicas is set to 2": { + envVarValue: "2", + expected: ExpectedCondition[int]{ + condition: func(actual int) bool { + return actual == 2 + }, + message: "expected replicas to be 2, got %d", + }, + }, + "should return 1 when replicas is set to 1": { + envVarValue: "1", + expected: ExpectedCondition[int]{ + condition: func(actual int) bool { + return actual == 1 + }, + message: "expected replicas to be 1, got %d", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + os.Setenv("WEBHOOK_SERVER_REPLICAS", test.envVarValue) + replicas := GetWebhookServerReplicas() + assert.True(t, test.expected.condition(replicas), test.expected.message, replicas) + }) + } +} + +func TestGetWebhookSelector(t *testing.T) { + type testCase struct { + envVarValue string + expected ExpectedCondition[string] + } + + tests := map[string]*testCase{ + "should 'admission.datree/validate' when replicas is not set": { + envVarValue: "", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "admission.datree/validate" + }, + message: "expected selector to be 'admission.datree/validate', got %d", + }, + }, + "should return 'datree/validate' when selector is set to 'datree/validate'": { + envVarValue: "datree/validate", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "datree/validate" + }, + message: "expected selector to be 'datree/validate', got %d", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + os.Setenv("WEBHOOK_SELECTOR", test.envVarValue) + selector := GetWebhookSelector() + assert.True(t, test.expected.condition(selector), test.expected.message, selector) + }) + } +} + +func TestGetWebhookNamespace(t *testing.T) { + type testCase struct { + envVarValue string + expected ExpectedCondition[string] + } + + tests := map[string]*testCase{ + "should return datree namespace when namespace is not set": { + envVarValue: "", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "datree" + }, + message: "expected namespace to be 'datree', got %d", + }, + }, + "should return default namespace when namespace is set to default": { + envVarValue: "default", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "default" + }, + message: "expected namesapce to be 'default', got %d", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + os.Setenv("WEBHOOK_NAMESPACE", test.envVarValue) + ns := GetWebhookNamespace() + assert.True(t, test.expected.condition(ns), test.expected.message, ns) + }) + } +} + +func TestGetWebhookServiceName(t *testing.T) { + type testCase struct { + envVarValue string + expected ExpectedCondition[string] + } + + tests := map[string]*testCase{ + "should return 'datree-webhook-server' when service name is not set": { + envVarValue: "", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "datree-webhook-server" + }, + message: "expected service name to be 'datree-webhook-server', got %d", + }, + }, + "should return 'datree-service' when selector is set to 'datree-service'": { + envVarValue: "datree-service", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "datree-service" + }, + message: "expected service name to be 'datree-service', got %d", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + os.Setenv("WEBHOOK_SERVICE", test.envVarValue) + serviceName := GetWebhookServiceName() + assert.True(t, test.expected.condition(serviceName), test.expected.message, serviceName) + }) + } + +} + +func TestGetWebhookServerPodSelector(t *testing.T) { + type testCase struct { + envVarValue string + expected ExpectedCondition[string] + } + + tests := map[string]*testCase{ + "should return 'app=app=datree-webhook-server' when pod selector is not set": { + envVarValue: "", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "app=datree-webhook-server" + }, + message: "expected pod selector to be 'app=datree-webhook-server', got %d", + }, + }, + "should return 'app=test' when pod selector is set to 'app=test'": { + envVarValue: "app=test", + expected: ExpectedCondition[string]{ + condition: func(actual string) bool { + return actual == "app=test" + }, + message: "expected pod selector to be 'app=test', got %d", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + os.Setenv("WEBHOOK_POD_SELECTOR", test.envVarValue) + podSelector := GetWebhookPodsSelector() + assert.True(t, test.expected.condition(podSelector), test.expected.message, podSelector) + }) + } +} diff --git a/cmd/webhook-server/Dockerfile b/cmd/webhook-server/Dockerfile new file mode 100644 index 00000000..33213f25 --- /dev/null +++ b/cmd/webhook-server/Dockerfile @@ -0,0 +1,19 @@ +FROM golang:1.18-alpine AS builder + +ARG BUILD_ENVIRONMENT +ARG WEBHOOK_VERSION + +WORKDIR /go/src/app + +# download dependencies in a separate step to allow caching +COPY go.* . +RUN go mod download + +COPY . . +# map /root/.cache/go-build to host go build cache folder +RUN --mount=type=cache,target=/root/.cache/go-build go build -o webhook-server -tags $BUILD_ENVIRONMENT -ldflags="-X github.com/datreeio/admission-webhook-datree/pkg/config.WebhookVersion=$WEBHOOK_VERSION" ./cmd/webhook-server + +FROM alpine:3.14 +COPY --from=builder /go/src/app/webhook-server / +EXPOSE 8443 +ENTRYPOINT ["/webhook-server"] diff --git a/main.go b/cmd/webhook-server/main.go similarity index 93% rename from main.go rename to cmd/webhook-server/main.go index 2c6ed527..554880ce 100644 --- a/main.go +++ b/cmd/webhook-server/main.go @@ -2,10 +2,12 @@ package main import ( "fmt" + "github.com/datreeio/admission-webhook-datree/pkg/config" "github.com/datreeio/admission-webhook-datree/pkg/logger" "github.com/datreeio/admission-webhook-datree/pkg/services" "github.com/robfig/cron/v3" + "net/http" "os" "time" @@ -28,7 +30,6 @@ func main() { if port == "" { port = "8443" } - start(port) } @@ -46,6 +47,7 @@ func start(port string) { } }() + logger.LogUtil("initializing k8s metadata") k8sMetadataUtil.InitK8sMetadataUtil() initMetadataLogsCronjob() server.InitServerVars() @@ -64,7 +66,9 @@ func start(port string) { internalLogger.LogInfo(fmt.Sprintf("server starting in webhook-version: %s", config.WebhookVersion)) // start server - if err := http.ListenAndServeTLS(":"+port, certPath, keyPath, nil); err != nil { + err = http.ListenAndServeTLS(":"+port, certPath, keyPath, nil) + if err != nil { + logger.LogUtil(err.Error()) http.ListenAndServe(":"+port, nil) } } diff --git a/go.mod b/go.mod index e9de7e8f..a381244b 100644 --- a/go.mod +++ b/go.mod @@ -4,36 +4,61 @@ go 1.18 require ( github.com/datreeio/datree v1.6.6 + github.com/gardener/controller-manager-library v0.2.0 github.com/ghodss/yaml v1.0.0 + github.com/go-openapi/runtime v0.24.2 github.com/lithammer/shortuuid v3.0.0+incompatible github.com/stretchr/testify v1.8.0 - go.uber.org/zap v1.10.0 - k8s.io/api v0.24.2 - k8s.io/apimachinery v0.24.2 - k8s.io/client-go v0.24.2 - k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 + go.uber.org/zap v1.21.0 + k8s.io/api v0.25.0 + k8s.io/apimachinery v0.25.0 + k8s.io/client-go v0.25.0 + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed ) require ( - go.uber.org/atomic v1.4.0 // indirect - go.uber.org/multierr v1.1.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/prometheus/client_golang v1.12.2 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/stretchr/objx v0.4.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + k8s.io/apiextensions-apiserver v0.25.0 // indirect + k8s.io/component-base v0.25.0 // indirect +) + +require ( + github.com/Masterminds/semver v1.4.2 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect ) require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/aws/aws-sdk-go v1.44.91 github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect github.com/briandowns/spinner v1.12.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807 // indirect - github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/go-logr/logr v1.2.0 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/swag v0.21.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect @@ -45,17 +70,17 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kyokomi/emoji v2.2.4+incompatible // indirect github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 // indirect - github.com/magiconair/properties v1.8.1 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pelletier/go-toml v1.7.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -78,21 +103,22 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yannh/kubeconform v0.4.14 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.60.1 // indirect - k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect + k8s.io/klog/v2 v2.70.1 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + sigs.k8s.io/controller-runtime v0.13.1 + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 1b81924c..b0cda832 100644 --- a/go.sum +++ b/go.sum @@ -17,9 +17,6 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb 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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -40,39 +37,65 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -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/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -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/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ahmetb/gen-crd-api-reference-docs v0.1.5/go.mod h1:P/XzJ+c2+khJKNKABcm2biRwk2QAuwbLf8DlXuaL7WM= 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/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= +github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.44.91 h1:SRWmuX7PTyhBdLuvSfM7KWrWISJsrRsUPcFDSFduRxY= +github.com/aws/aws-sdk-go v1.44.91/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/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/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmatcuk/doublestar/v2 v2.0.4 h1:6I6oUiT/sU27eE2OFcWqBhL1SwjyvQuOssxT4a1yidI= github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw= github.com/briandowns/spinner v1.12.0 h1:72O0PzqGJb6G3KgrcIOtL/JAGGZ5ptOMCn9cUHmqsmw= github.com/briandowns/spinner v1.12.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -80,76 +103,181 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/datreeio/datree v1.6.6 h1:e2TVV/CpjLepKQrQAWx1ol019nbOhflSKOrHBa6uP/c= github.com/datreeio/datree v1.6.6/go.mod h1:bB2TVZvIG9DeiLzcWFSqBxi87Xmm3OAGNBI8t32E2tk= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807 h1:jdjd5e68T4R/j4PWxfZqcKY8KtT9oo8IPNVuV4bSXDQ= github.com/eiannone/keyboard v0.0.0-20200508000154-caf4b762e807/go.mod h1:Xoiu5VdKMvbRgHuY7+z64lhu/7lvax/22nzASF6GrO8= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/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.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +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/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/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/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/gardener/controller-manager-library v0.2.0 h1:MyxL0k10lwBf8TXkbnuN+oEOkHwCNhp3SKj+ad2w62s= +github.com/gardener/controller-manager-library v0.2.0/go.mod h1:oCK7fW2VpsMhmUh5c6cOhsN8p9Tth1OM3rRtogDF11k= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-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/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.24.2 h1:yX9HMGQbz32M87ECaAhGpJjBmErO3QLcgdZj9BzGx7c= +github.com/go-openapi/runtime v0.24.2/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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= @@ -158,7 +286,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -174,12 +302,11 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -190,12 +317,12 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -212,22 +339,27 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf 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-20201218002935-b9804c9f04c2/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.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/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.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 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.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/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= @@ -251,15 +383,30 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m 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/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= 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.6/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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 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/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.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= @@ -267,15 +414,23 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= 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/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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= @@ -286,12 +441,20 @@ github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHT github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 h1:aczX6NMOtt6L4YT0fQvKkDK6LZEtdOso9sUH89V1+P0= github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281/go.mod h1:lc+czkgO/8F7puNki5jk8QyujbfK1LOT7Wl0ON2hxyk= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -306,46 +469,58 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m 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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/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-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/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 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 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 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= 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 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= @@ -354,28 +529,55 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI= github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +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.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE= @@ -383,11 +585,19 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJQoJA= github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA= +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.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +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 h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -396,20 +606,26 @@ github.com/spf13/afero v1.8.1 h1:izYHOT71f9iZ7iq37Uqjael60/vYC6vMtzedudZ0zEk= github.com/spf13/afero v1.8.1/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -421,12 +637,18 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tklauser/numcpus v0.5.0 h1:ooe7gN0fg6myJ0EKoTAf5hebTZrH52px3New/D9iJ+A= github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -434,9 +656,12 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yannh/kubeconform v0.4.14 h1:EXCt51sbvESYBxCpfHWhTek3GOj5VsGMB1dxU31HO+Y= github.com/yannh/kubeconform v0.4.14/go.mod h1:5md2J7KvQFj+sICN5Je5ckhUcgPoPISuntVJr4Hnr08= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 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= @@ -445,32 +670,50 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= 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.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +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.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +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 v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 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= @@ -503,9 +746,11 @@ 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-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-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= @@ -513,14 +758,16 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-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-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -531,24 +778,21 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -558,14 +802,14 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/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= @@ -573,6 +817,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -580,22 +825,29 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h 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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/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-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/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-20190916202348-b4ddaad3f8a3/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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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= @@ -608,8 +860,9 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -618,28 +871,30 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/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-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210616045830-e2b7044e8c71/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -653,20 +908,29 @@ 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-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= +golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190213192042-740235f6c0d8/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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/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-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -692,7 +956,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs 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= @@ -712,8 +975,12 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 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= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -733,8 +1000,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 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/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= @@ -779,12 +1044,7 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D 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-20210108203827-ffc7fda8c3d7/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-20210226172003-ab064af71705/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/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= @@ -801,8 +1061,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM 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/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= @@ -815,8 +1073,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj 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= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -825,26 +1083,33 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X 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.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.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.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-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -852,30 +1117,66 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= -k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= -k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM= -k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA= -k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/api v0.0.0-20190918155943-95b840bb6a1f/go.mod h1:uWuOHnjmNrtQomJrvEBg0c0HRNyQ+8KTEERVsK0PW48= +k8s.io/api v0.16.4/go.mod h1:AtzMnsR45tccQss5q8RnF+W8L81DH6XwXwo/joEx9u0= +k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= +k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= +k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY= +k8s.io/apiextensions-apiserver v0.16.4/go.mod h1:HYQwjujEkXmQNhap2C9YDdIVOSskGZ3et0Mvjcyjbto= +k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= +k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655/go.mod h1:nL6pwRT8NgfF8TT68DBI8uEePRt89cSvoXUVqbkWHq4= +k8s.io/apimachinery v0.16.4/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= +k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= +k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg= +k8s.io/apiserver v0.16.4/go.mod h1:kbLJOak655g6W7C+muqu1F76u9wnEycfKMqbVaXIdAc= +k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk= +k8s.io/client-go v0.16.4/go.mod h1:ZgxhFDxSnoKY0J0U2/Y1C8obKDdlhGPZwA7oHH863Ok= +k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= +k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= +k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE= +k8s.io/code-generator v0.16.4/go.mod h1:mJUgkl06XV4kstAnLHAIzJPVCOzVR+ZcfPIv4fUsFCY= +k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090/go.mod h1:933PBGtQFJky3TEwYx4aEPZ4IxqhWh3R6DCmzqIn1hA= +k8s.io/component-base v0.16.4/go.mod h1:GYQ+4hlkEwdlpAp59Ztc4gYuFhdoZqiAJD1unYDJ3FM= +k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= +k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/helm v2.11.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= -k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= -k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= -k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/metrics v0.16.4/go.mod h1:dckkfqvaASo+NrzEmp8ST8yCc9hGt7lx9ABAILyDHx8= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= -sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/controller-runtime v0.13.1 h1:tUsRCSJVM1QQOOeViGeX3GMT3dQF1eePPw6sEE3xSlg= +sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= +sigs.k8s.io/structured-merge-diff v1.0.1/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/enums/enums.go b/pkg/enums/enums.go index 436e1f6e..5de8fa05 100644 --- a/pkg/enums/enums.go +++ b/pkg/enums/enums.go @@ -1,11 +1,16 @@ package enums const ( - Token = "DATREE_TOKEN" - ClientId = "DATREE_CLIENT_ID" - Policy = "DATREE_POLICY" - Verbose = "DATREE_VERBOSE" - NoRecord = "DATREE_NO_RECORD" - Output = "DATREE_OUTPUT" - Enforce = "DATREE_ENFORCE" + Token = "DATREE_TOKEN" + ClientId = "DATREE_CLIENT_ID" + Policy = "DATREE_POLICY" + Verbose = "DATREE_VERBOSE" + NoRecord = "DATREE_NO_RECORD" + Output = "DATREE_OUTPUT" + Enforce = "DATREE_ENFORCE" + AWSMarketplaceProductID = "AWS_MP_PRODUCT_ID" + AWSMarketplaceEnableCheckEntitlement = "AWS_MP_ENABLE_CHECK_ENTITLEMENT" + AWSMarketplaceRegion = "AWS_MP_REGION" + AWSMarketplaceKeyFingerprint = "AWS_MP_KEY_FINGERPRINT" + Debug = "DEBUG" ) diff --git a/pkg/k8sMetadataUtil/k8sMetadataUtil.go b/pkg/k8sMetadataUtil/k8sMetadataUtil.go index 1957abba..c46bad5c 100644 --- a/pkg/k8sMetadataUtil/k8sMetadataUtil.go +++ b/pkg/k8sMetadataUtil/k8sMetadataUtil.go @@ -2,11 +2,14 @@ package k8sMetadataUtil import ( "context" + "fmt" "os" "time" - cliClient "github.com/datreeio/admission-webhook-datree/pkg/clients" + cliclient "github.com/datreeio/admission-webhook-datree/pkg/clients" "github.com/datreeio/admission-webhook-datree/pkg/enums" + licensemanagerclient "github.com/datreeio/admission-webhook-datree/pkg/licenseManagerClient" + "github.com/datreeio/admission-webhook-datree/pkg/logger" "github.com/datreeio/datree/pkg/deploymentConfig" "github.com/datreeio/datree/pkg/networkValidator" "github.com/robfig/cron/v3" @@ -17,28 +20,48 @@ import ( ) func InitK8sMetadataUtil() { - validator := networkValidator.NewNetworkValidator() - cliClient := cliClient.NewCliServiceClient(deploymentConfig.URL, validator) - - var clusterUuid k8sTypes.UID + cliClient := cliclient.NewCliServiceClient(deploymentConfig.URL, validator) k8sClient, err := getClientSet() - if err != nil { - sendK8sMetadata(-1, err, clusterUuid, cliClient) + cliClient.ReportK8sMetadata(&cliclient.ReportK8sMetadataRequest{ + ClusterUuid: "", + Token: os.Getenv(enums.Token), + NodesCount: -1, + NodesCountErr: err.Error(), + }) return } - clusterUuid, err = getClusterUuid(k8sClient) + clusterUuid, err := getClusterUuid(k8sClient) if err != nil { - sendK8sMetadata(-1, err, clusterUuid, cliClient) + cliClient.ReportK8sMetadata(&cliclient.ReportK8sMetadataRequest{ + ClusterUuid: clusterUuid, + Token: os.Getenv(enums.Token), + NodesCount: -1, + NodesCountErr: err.Error(), + }) + } + + runHourlyNodesCountCronJob(k8sClient, cliClient, clusterUuid) + + if os.Getenv(enums.AWSMarketplaceEnableCheckEntitlement) == "true" { + runDailyAWSCheckoutLicenseCronJob(k8sClient, cliClient, clusterUuid) } +} + +func runHourlyNodesCountCronJob(k8sClient *kubernetes.Clientset, cliClient *cliclient.CliClient, clusterUuid k8sTypes.UID) { cornJob := cron.New(cron.WithLocation(time.UTC)) cornJob.AddFunc("@hourly", func() { nodesCount, nodesCountErr := getNodesCount(k8sClient) - sendK8sMetadata(nodesCount, nodesCountErr, clusterUuid, cliClient) + cliClient.ReportK8sMetadata(&cliclient.ReportK8sMetadataRequest{ + ClusterUuid: clusterUuid, + Token: os.Getenv(enums.Token), + NodesCount: nodesCount, + NodesCountErr: nodesCountErr.Error(), + }) }) cornJob.Start() } @@ -77,18 +100,39 @@ func getClusterUuid(clientset *kubernetes.Clientset) (k8sTypes.UID, error) { return clusterMetadata.UID, nil } -func sendK8sMetadata(nodesCount int, nodesCountErr error, clusterUuid k8sTypes.UID, client *cliClient.CliClient) { - token := os.Getenv(enums.Token) - - var nodesCountErrString string - if nodesCountErr != nil { - nodesCountErrString = nodesCountErr.Error() - } - - client.ReportK8sMetadata(&cliClient.ReportK8sMetadataRequest{ - ClusterUuid: clusterUuid, - Token: token, - NodesCount: nodesCount, - NodesCountErr: nodesCountErrString, +// run chckout license cron job daily to check if aws marketplace license is valid with the nodes number +func runDailyAWSCheckoutLicenseCronJob(k8sClient *kubernetes.Clientset, cliClient *cliclient.CliClient, clusterUuid k8sTypes.UID) { + licenseManagerClient := licensemanagerclient.NewLicenseManagerClient() + + licenseCheckerCornJob := cron.New(cron.WithLocation(time.UTC)) + // @daily means run once a day, midnight. On debug mode it's very nice to run it once a minute i.e '@every 1min' + licenseCheckerCornJob.AddFunc("@daily", func() { + nodesCount, err := getNodesCount(k8sClient) + if err != nil { + // should be on debug + logger.LogUtil(fmt.Sprint("failed counting nodes for checkout", err)) + cliClient.ReportK8sMetadata(&cliclient.ReportK8sMetadataRequest{ + ClusterUuid: clusterUuid, + Token: os.Getenv(enums.Token), + NodesCount: -1, + NodesCountErr: err.Error(), + }) + return + } + + // should be on debug + logger.LogUtil(fmt.Sprint("checking aws marketplace license with nodes count", nodesCount)) + err = licenseManagerClient.CheckoutLicense(nodesCount) + if err != nil { + // should be on debug + logger.LogUtil(fmt.Sprint("checkout license failed: ", err)) + cliClient.ReportK8sMetadata(&cliclient.ReportK8sMetadataRequest{ + ClusterUuid: clusterUuid, + Token: os.Getenv(enums.Token), + NodesCount: -1, + NodesCountErr: err.Error(), + }) + } }) + licenseCheckerCornJob.Start() } diff --git a/pkg/licenseManagerClient/client.go b/pkg/licenseManagerClient/client.go new file mode 100644 index 00000000..fa822db5 --- /dev/null +++ b/pkg/licenseManagerClient/client.go @@ -0,0 +1,54 @@ +package licensemanagerclient + +import ( + "fmt" + "os" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/licensemanager" + "github.com/datreeio/admission-webhook-datree/pkg/enums" + "github.com/google/uuid" +) + +type LicenseManager struct { + client *licensemanager.LicenseManager + awsMarketplaceProductID string + awsMarketplaceFingerprint string +} + +func NewLicenseManagerClient() *LicenseManager { + clientSession := session.Must(session.NewSession()) + awsClient := licensemanager.New(clientSession, aws.NewConfig().WithRegion(os.Getenv(enums.AWSMarketplaceRegion))) + return &LicenseManager{ + client: awsClient, + awsMarketplaceProductID: os.Getenv(enums.AWSMarketplaceProductID), + awsMarketplaceFingerprint: os.Getenv(enums.AWSMarketplaceKeyFingerprint), + } +} + +// Checkout the account license according to quantity of units the account consumes. +// If everything goes well, the license will be checked out, otherwise an error will returned. +func (l *LicenseManager) CheckoutLicense(consumedUnitsCount int) error { + _, err := l.client.CheckoutLicense(&licensemanager.CheckoutLicenseInput{ + ClientToken: aws.String(uuid.New().String()), + // "PROVISIONAL" checkout type enables to temporarily draw a unit and return it back to the license pool when the application is stopped. + CheckoutType: aws.String("PROVISIONAL"), + Entitlements: []*licensemanager.EntitlementData{ + { + // The entitilement name is the contract API name defined in the product. + // The contract API name is defined in the product "load form" in the AWS Marketplace management protal + Name: aws.String("Datree"), + Value: aws.String(fmt.Sprint(consumedUnitsCount)), + Unit: aws.String("Count"), + }, + }, + ProductSKU: aws.String(l.awsMarketplaceProductID), + KeyFingerprint: aws.String(l.awsMarketplaceFingerprint), + }) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/server/server.go b/pkg/server/server.go index d67a6f9a..5d67b6fc 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -64,7 +64,7 @@ func readConfigScanningFilters() (skipList []string, err error) { } func ValidateCertificate() (certPath string, keyPath string, err error) { - tlsDir := `/run/secrets/tls` + tlsDir := `/etc/webhook/certs` tlsCertFile := `tls.crt` tlsKeyFile := `tls.key`