From 24cc9a0ba2f252baf7e70927b75243e2855dfd90 Mon Sep 17 00:00:00 2001 From: Jose Silva Date: Tue, 26 Oct 2021 17:50:13 +0100 Subject: [PATCH 1/6] Adds reconcilation of Authorino instances --- .dockerignore | 6 + Makefile | 19 +- PROJECT | 12 +- api/v1beta1/authorino_types.go | 132 ++++++++ api/v1beta1/groupversion_info.go | 36 +++ api/v1beta1/zz_generated.deepcopy.go | 189 +++++++++++ ...rator.kuadrant.3scale.net_authorinoes.yaml | 126 ++++++++ config/crd/kustomization.yaml | 21 ++ config/crd/kustomizeconfig.yaml | 19 ++ .../patches/cainjection_in_authorinoes.yaml | 7 + .../crd/patches/webhook_in_authorinoes.yaml | 16 + config/default/manager_auth_proxy_patch.yaml | 4 + config/manager/controller_manager_config.yaml | 4 + config/manager/manager.yaml | 5 + config/rbac/auth_proxy_service.yaml | 4 + config/rbac/authorino_editor_role.yaml | 24 ++ config/rbac/authorino_viewer_role.yaml | 20 ++ config/rbac/role.yaml | 46 +++ .../authorino-operator_v1beta1_authorino.yaml | 19 ++ config/samples/kustomization.yaml | 4 + config/scorecard/patches/basic.config.yaml | 4 + config/scorecard/patches/olm.config.yaml | 10 +- controllers/authorino_controller.go | 299 ++++++++++++++++++ controllers/suite_test.go | 80 +++++ go.mod | 10 + go.sum | 238 ++++++++------ main.go | 14 +- 27 files changed, 1255 insertions(+), 113 deletions(-) create mode 100644 api/v1beta1/authorino_types.go create mode 100644 api/v1beta1/groupversion_info.go create mode 100644 api/v1beta1/zz_generated.deepcopy.go create mode 100644 config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml create mode 100644 config/crd/kustomization.yaml create mode 100644 config/crd/kustomizeconfig.yaml create mode 100644 config/crd/patches/cainjection_in_authorinoes.yaml create mode 100644 config/crd/patches/webhook_in_authorinoes.yaml create mode 100644 config/rbac/authorino_editor_role.yaml create mode 100644 config/rbac/authorino_viewer_role.yaml create mode 100644 config/rbac/role.yaml create mode 100644 config/samples/authorino-operator_v1beta1_authorino.yaml create mode 100644 config/samples/kustomization.yaml create mode 100644 controllers/authorino_controller.go create mode 100644 controllers/suite_test.go diff --git a/.dockerignore b/.dockerignore index 243f81a5..6c6bd0ba 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,11 @@ # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file +<<<<<<< HEAD # Ignore all files which are not go type !**/*.go !**/*.mod !**/*.sum +======= +# Ignore build and test binaries. +bin/ +testbin/ +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) diff --git a/Makefile b/Makefile index 79d51269..5f774b46 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,8 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) # This variable is used to construct full image tags for bundle and catalog images. # # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both -# authorino.3scale.net/authorino-operator-bundle:$VERSION and authorino.3scale.net/authorino-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= authorino.3scale.net/authorino-operator +# kuadrant.3scale.net/authorino-operator-bundle:$VERSION and kuadrant.3scale.net/authorino-operator-catalog:$VERSION. +IMAGE_TAG_BASE ?= kuadrant.3scale.net/authorino-operator # BUNDLE_IMG defines the image:tag used for the bundle. # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) @@ -39,6 +39,8 @@ BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) IMG ?= controller:latest # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.21 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -85,11 +87,8 @@ fmt: ## Run go fmt against code. vet: ## Run go vet against code. go vet ./... -ENVTEST_ASSETS_DIR=$(shell pwd)/testbin -test: manifests generate fmt vet ## Run tests. - mkdir -p ${ENVTEST_ASSETS_DIR} - test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.3/hack/setup-envtest.sh - source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out ##@ Build @@ -123,12 +122,16 @@ undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/confi CONTROLLER_GEN = $(shell pwd)/bin/controller-gen controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1) + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1) KUSTOMIZE = $(shell pwd)/bin/kustomize kustomize: ## Download kustomize locally if necessary. $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) +ENVTEST = $(shell pwd)/bin/setup-envtest +envtest: ## Download envtest-setup locally if necessary. + $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) + # go-get-tool will 'go get' any package $2 and install it to $1. PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) define go-get-tool diff --git a/PROJECT b/PROJECT index e4d728dc..7afc9351 100644 --- a/PROJECT +++ b/PROJECT @@ -1,4 +1,4 @@ -domain: authorino.3scale.net +domain: kuadrant.3scale.net layout: - go.kubebuilder.io/v3 plugins: @@ -6,4 +6,14 @@ plugins: scorecard.sdk.operatorframework.io/v2: {} projectName: authorino-operator repo: github.com/kuadrant/authorino-operator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kuadrant.3scale.net + group: authorino-operator + kind: Authorino + path: github.com/kuadrant/authorino-operator/api/v1beta1 + version: v1beta1 version: "3" diff --git a/api/v1beta1/authorino_types.go b/api/v1beta1/authorino_types.go new file mode 100644 index 00000000..684f9369 --- /dev/null +++ b/api/v1beta1/authorino_types.go @@ -0,0 +1,132 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +type ConditionType string + +const ( + // ConditionReady specifies that the resource is ready + ConditionReady ConditionType = "Ready" + AuthorinoContainerName string = "authorino" + AuthorinoOperatorNamespace string = "authorino-operator" + + // Authorino EnvVars + ExtAuthGRPCPort string = "EXT_AUTH_GRPC_PORT" + TSLCertPath string = "TLS_CERT" + TSLCertKeyPath string = "TLS_CERT_KEY" + OIDCHTTPPort string = "OIDC_HTTP_PORT" + OIDCTSLCertPath string = "OIDC_TLS_CERT" + OIDCTLSCertKeyPath string = "OIDC_TLS_CERT_KEY" +) + +type Condition struct { + // Type of condition + Type ConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status apiv1.ConditionStatus `json:"status"` + // Last time the condition transit from one status to another. + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // (brief) reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty"` + // Human readable message indicating details about last transition. + // +optional + Message string `json:"message,omitempty"` + // Last time the condition was updated + // +optional + LastUpdatedTime *metav1.Time `json:"lastUpdatedTime,omitempty"` +} + +// AuthorinoSpec defines the desired state of Authorino +type AuthorinoSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + Image string `json:"image,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + ImagePullPolicy string `json:"imagepullpolicy,omitempty"` + ClusterWide ClusterWideAuthorino `json:"clusterwide,omitempty"` + Listener Listener `json:"listener,omitempty"` + OIDCServer OIDCServer `json:"oidcserver,omitempty"` +} + +type ClusterWideAuthorino bool + +type Listener struct { + Port *int32 `json:"port,omitempty"` + Tls bool `json:"tsl,omitempty"` + CertPath string `json:"certpath,omitempty"` + CertKeyPath string `json:"certkeypath,omitempty"` +} + +type OIDCServer struct { + Port *int32 `json:"port,omitempty"` + CertPath string `json:"certpath,omitempty"` + CertKeyPath string `json:"certkeypath,omitempty"` +} + +// AuthorinoStatus defines the observed state of Authorino +type AuthorinoStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Number of authorino instances in the cluster + AuthorinoInstances int32 `json:"authorinoinstances"` + + // Conditions is an array of the current Authorino's CR conditions + // Supported condition types: ConditionReady + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Authorino is the Schema for the authorinoes API +type Authorino struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AuthorinoSpec `json:"spec,omitempty"` + Status AuthorinoStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// AuthorinoList contains a list of Authorino +type AuthorinoList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Authorino `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Authorino{}, &AuthorinoList{}) +} diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta1/groupversion_info.go new file mode 100644 index 00000000..eaba7314 --- /dev/null +++ b/api/v1beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the authorino-operator v1beta1 API group +//+kubebuilder:object:generate=true +//+groupName=authorino-operator.kuadrant.3scale.net +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "authorino-operator.kuadrant.3scale.net", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 00000000..d7e9cd18 --- /dev/null +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,189 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Authorino) DeepCopyInto(out *Authorino) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Authorino. +func (in *Authorino) DeepCopy() *Authorino { + if in == nil { + return nil + } + out := new(Authorino) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Authorino) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthorinoList) DeepCopyInto(out *AuthorinoList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Authorino, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorinoList. +func (in *AuthorinoList) DeepCopy() *AuthorinoList { + if in == nil { + return nil + } + out := new(AuthorinoList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AuthorinoList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthorinoSpec) DeepCopyInto(out *AuthorinoSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + in.Listener.DeepCopyInto(&out.Listener) + in.OIDCServer.DeepCopyInto(&out.OIDCServer) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorinoSpec. +func (in *AuthorinoSpec) DeepCopy() *AuthorinoSpec { + if in == nil { + return nil + } + out := new(AuthorinoSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthorinoStatus) DeepCopyInto(out *AuthorinoStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthorinoStatus. +func (in *AuthorinoStatus) DeepCopy() *AuthorinoStatus { + if in == nil { + return nil + } + out := new(AuthorinoStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + if in.LastUpdatedTime != nil { + in, out := &in.LastUpdatedTime, &out.LastUpdatedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Listener) DeepCopyInto(out *Listener) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Listener. +func (in *Listener) DeepCopy() *Listener { + if in == nil { + return nil + } + out := new(Listener) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCServer) DeepCopyInto(out *OIDCServer) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCServer. +func (in *OIDCServer) DeepCopy() *OIDCServer { + if in == nil { + return nil + } + out := new(OIDCServer) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml b/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml new file mode 100644 index 00000000..07cb0179 --- /dev/null +++ b/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml @@ -0,0 +1,126 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: authorinoes.authorino-operator.kuadrant.3scale.net +spec: + group: authorino-operator.kuadrant.3scale.net + names: + kind: Authorino + listKind: AuthorinoList + plural: authorinoes + singular: authorino + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Authorino is the Schema for the authorinoes API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AuthorinoSpec defines the desired state of Authorino + properties: + clusterwide: + type: boolean + image: + type: string + imagepullpolicy: + type: string + listener: + properties: + certkeypath: + type: string + certpath: + type: string + port: + format: int32 + type: integer + tsl: + type: boolean + type: object + oidcserver: + properties: + certkeypath: + type: string + certpath: + type: string + port: + format: int32 + type: integer + type: object + replicas: + format: int32 + type: integer + type: object + status: + description: AuthorinoStatus defines the observed state of Authorino + properties: + authorinoinstances: + description: Number of authorino instances in the cluster + format: int32 + type: integer + conditions: + description: 'Conditions is an array of the current Authorino''s CR + conditions Supported condition types: ConditionReady' + items: + properties: + lastTransitionTime: + description: Last time the condition transit from one status + to another. + format: date-time + type: string + lastUpdatedTime: + description: Last time the condition was updated + format: date-time + type: string + message: + description: Human readable message indicating details about + last transition. + type: string + reason: + description: (brief) reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + required: + - authorinoinstances + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 00000000..4314bd16 --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml +#+kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_authorinoes.yaml +#+kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_authorinoes.yaml +#+kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 00000000..ec5c150a --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/config/crd/patches/cainjection_in_authorinoes.yaml b/config/crd/patches/cainjection_in_authorinoes.yaml new file mode 100644 index 00000000..626b97b0 --- /dev/null +++ b/config/crd/patches/cainjection_in_authorinoes.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: authorinoes.authorino-operator.kuadrant.3scale.net diff --git a/config/crd/patches/webhook_in_authorinoes.yaml b/config/crd/patches/webhook_in_authorinoes.yaml new file mode 100644 index 00000000..4c3f9d8f --- /dev/null +++ b/config/crd/patches/webhook_in_authorinoes.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: authorinoes.authorino-operator.kuadrant.3scale.net +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index a224be19..1ea03693 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -18,6 +18,10 @@ spec: - "--v=10" ports: - containerPort: 8443 +<<<<<<< HEAD +======= + protocol: TCP +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) name: https - name: manager args: diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml index b9f05eb3..88ab9298 100644 --- a/config/manager/controller_manager_config.yaml +++ b/config/manager/controller_manager_config.yaml @@ -8,4 +8,8 @@ webhook: port: 9443 leaderElection: leaderElect: true +<<<<<<< HEAD resourceName: f0fb677d.authorino.3scale.net +======= + resourceName: aac3a15d.kuadrant.3scale.net +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 79adfe72..d812042e 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -47,8 +47,13 @@ spec: periodSeconds: 10 resources: limits: +<<<<<<< HEAD cpu: 100m memory: 30Mi +======= + cpu: 200m + memory: 100Mi +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) requests: cpu: 100m memory: 20Mi diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml index 6cf656be..4d42e129 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/rbac/auth_proxy_service.yaml @@ -9,6 +9,10 @@ spec: ports: - name: https port: 8443 +<<<<<<< HEAD +======= + protocol: TCP +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) targetPort: https selector: control-plane: controller-manager diff --git a/config/rbac/authorino_editor_role.yaml b/config/rbac/authorino_editor_role.yaml new file mode 100644 index 00000000..74be37ae --- /dev/null +++ b/config/rbac/authorino_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit authorinoes. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: authorino-editor-role +rules: +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes/status + verbs: + - get diff --git a/config/rbac/authorino_viewer_role.yaml b/config/rbac/authorino_viewer_role.yaml new file mode 100644 index 00000000..e78a7477 --- /dev/null +++ b/config/rbac/authorino_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view authorinoes. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: authorino-viewer-role +rules: +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes + verbs: + - get + - list + - watch +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml new file mode 100644 index 00000000..cc3c9938 --- /dev/null +++ b/config/rbac/role.yaml @@ -0,0 +1,46 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes/finalizers + verbs: + - update +- apiGroups: + - authorino-operator.kuadrant.3scale.net + resources: + - authorinoes/status + verbs: + - get + - patch + - update diff --git a/config/samples/authorino-operator_v1beta1_authorino.yaml b/config/samples/authorino-operator_v1beta1_authorino.yaml new file mode 100644 index 00000000..8f400e71 --- /dev/null +++ b/config/samples/authorino-operator_v1beta1_authorino.yaml @@ -0,0 +1,19 @@ +apiVersion: authorino-operator.kuadrant.3scale.net/v1beta1 +kind: Authorino +metadata: + name: authorino-sample + namespace: authorino-operator +spec: + image: quay.io/3scale/authorino:latest + replicas: 1 + imagepullpolicy: Always + clusterwide: true + listener: + port: + tsl: true + certpath: "" + certkeypath: "" + oidcserver: + port: + certpath: "" + certkeypath: "" diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml new file mode 100644 index 00000000..39c9923a --- /dev/null +++ b/config/samples/kustomization.yaml @@ -0,0 +1,4 @@ +## Append samples you want in your CSV to this file as resources ## +resources: +- authorino-operator_v1beta1_authorino.yaml +#+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index a32ceaa7..9ab6754b 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,7 +4,11 @@ entrypoint: - scorecard-test - basic-check-spec +<<<<<<< HEAD image: quay.io/operator-framework/scorecard-test:v1.9.0 +======= + image: quay.io/operator-framework/scorecard-test:v1.13.1 +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) labels: suite: basic test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index 92e3e412..79b4a634 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.9.0 + image: quay.io/operator-framework/scorecard-test:v1.13.1 labels: suite: olm test: olm-bundle-validation-test @@ -14,7 +14,7 @@ entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.9.0 + image: quay.io/operator-framework/scorecard-test:v1.13.1 labels: suite: olm test: olm-crds-have-validation-test @@ -24,7 +24,7 @@ entrypoint: - scorecard-test - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.9.0 + image: quay.io/operator-framework/scorecard-test:v1.13.1 labels: suite: olm test: olm-crds-have-resources-test @@ -34,7 +34,7 @@ entrypoint: - scorecard-test - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.9.0 + image: quay.io/operator-framework/scorecard-test:v1.13.1 labels: suite: olm test: olm-spec-descriptors-test @@ -44,7 +44,7 @@ entrypoint: - scorecard-test - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.9.0 + image: quay.io/operator-framework/scorecard-test:v1.13.1 labels: suite: olm test: olm-status-descriptors-test diff --git a/controllers/authorino_controller.go b/controllers/authorino_controller.go new file mode 100644 index 00000000..c39c32a0 --- /dev/null +++ b/controllers/authorino_controller.go @@ -0,0 +1,299 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/go-logr/logr" + authorinooperatorv1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" +) + +// AuthorinoReconciler reconciles a Authorino object +type AuthorinoReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=authorino-operator.kuadrant.3scale.net,resources=authorinoes,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=authorino-operator.kuadrant.3scale.net,resources=authorinoes/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=authorino-operator.kuadrant.3scale.net,resources=authorinoes/finalizers,verbs=update + +// +kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Authorino object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile +func (r *AuthorinoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("authorinoReconciler", req.NamespacedName) + + // get authorino instance + authorinoInstance, err := r.authorinoInstance(req.NamespacedName) + if err != nil { + return ctrl.Result{}, err + } + + if authorinoInstance == nil { + log.Info("Authorino CR not found. returning the reconciler") + return ctrl.Result{}, nil + } + + log.Info("Found an instance of authorino", "authorinoInstanceName", authorinoInstance.Name) + + err = r.authorinoServices(authorinoInstance) + if err != nil { + log.Error(err, "Failed to create authorino services") + return ctrl.Result{}, err + } + + existingDeployment := &appsv1.Deployment{} + err = r.Get(context.TODO(), + types.NamespacedName{ + Name: authorinoInstance.GetName(), + Namespace: authorinoInstance.GetNamespace(), + }, existingDeployment) + if err != nil && errors.IsNotFound(err) { + newDeployment := r.authorinoDeployment(authorinoInstance) + err = r.Create(ctx, newDeployment) + if err != nil { + log.Error(err, "Failed to create Authorino deployment resource", newDeployment.Name, newDeployment.Namespace) + return ctrl.Result{}, err + } + // Deployment created successfully - return and requeue + return ctrl.Result{Requeue: true}, nil + } else if err != nil { + log.Error(err, "Failed to get Deployment for Authorino") + return ctrl.Result{}, err + } + + //TODO: handle deletiton + + //TODO: update status + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *AuthorinoReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&authorinooperatorv1beta1.Authorino{}). + Complete(r) +} + +func (r *AuthorinoReconciler) authorinoInstance(namespacedName types.NamespacedName) (*authorinooperatorv1beta1.Authorino, error) { + authorinoInstance := &authorinooperatorv1beta1.Authorino{} + err := r.Get(context.TODO(), namespacedName, authorinoInstance) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("Authorino CR not found.") + return nil, nil + } + return nil, err + } + return authorinoInstance, nil +} + +func (r *AuthorinoReconciler) authorinoDeployment(authorino *authorinooperatorv1beta1.Authorino) *appsv1.Deployment { + objectMeta := v1.ObjectMeta{ + Name: authorino.GetName(), + Namespace: getNamespace(authorino), + } + + labels := labelsForAuthorino(objectMeta.GetName()) + + return &appsv1.Deployment{ + ObjectMeta: objectMeta, + Spec: appsv1.DeploymentSpec{ + Replicas: authorino.Spec.Replicas, + Selector: &v1.LabelSelector{ + MatchLabels: labels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: labels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: authorino.Spec.Image, + ImagePullPolicy: corev1.PullPolicy(authorino.Spec.ImagePullPolicy), + Name: authorinooperatorv1beta1.AuthorinoContainerName, + Env: r.buildAuthorinoEnv(authorino), + }, + }, + }, + }, + }, + } +} + +func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *authorinooperatorv1beta1.Authorino) []corev1.EnvVar { + envVar := []corev1.EnvVar{} + + if !authorino.Spec.ClusterWide { + envVar = append(envVar, corev1.EnvVar{ + Name: "WATCH_NAMESPACE", + Value: authorino.Namespace, + }) + } + + // external auth service via GRPC + if authorino.Spec.Listener.Port != nil { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.ExtAuthGRPCPort, + Value: fmt.Sprint(authorino.Spec.Listener.Port), + }) + } + if authorino.Spec.Listener.CertPath != "" { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.TSLCertPath, + Value: authorino.Spec.Listener.CertPath, + }) + } + if authorino.Spec.Listener.CertKeyPath != "" { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.TSLCertKeyPath, + Value: authorino.Spec.Listener.CertKeyPath, + }) + } + + // OIDC service + if authorino.Spec.OIDCServer.Port != nil { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.OIDCHTTPPort, + Value: fmt.Sprint(authorino.Spec.OIDCServer.Port), + }) + } + if authorino.Spec.OIDCServer.CertKeyPath != "" { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.OIDCTSLCertPath, + Value: authorino.Spec.OIDCServer.CertPath, + }) + } + if authorino.Spec.OIDCServer.CertKeyPath != "" { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.OIDCTLSCertKeyPath, + Value: authorino.Spec.OIDCServer.CertKeyPath, + }) + } + + return envVar +} + +func (r *AuthorinoReconciler) authorinoDeploymentChanges(existingDeployment, desiredDeployment *appsv1.Deployment) bool { + changed := false + + if len(desiredDeployment.Spec.Template.Spec.Containers) != 1 { + // error + } + + existingContainer := existingDeployment.Spec.Template.Spec.Containers[0] + desiredContainer := desiredDeployment.Spec.Template.Spec.Containers[0] + + if existingContainer.Image != desiredContainer.Image { + changed = true + } + + if existingContainer.ImagePullPolicy != desiredContainer.ImagePullPolicy { + changed = true + } + + if changed { + + } + + return true +} + +func (r *AuthorinoReconciler) authorinoServices(authorino *authorinooperatorv1beta1.Authorino) error { + + services := make(map[string][]corev1.ServicePort) + services["authorino-authorization"] = []corev1.ServicePort{ + { + Name: "grpc", + Port: 50051, + Protocol: corev1.ProtocolTCP, + }, + } + services["authorino-oidc"] = []corev1.ServicePort{ + { + Name: "http", + Port: 8083, + Protocol: corev1.ProtocolTCP, + }, + } + services["authorino-controller-manager-metrics-service"] = []corev1.ServicePort{ + { + Name: "https", + Port: 8443, + TargetPort: intstr.FromString("https"), + }, + } + + for name, service := range services { + obj := &corev1.Service{ + ObjectMeta: v1.ObjectMeta{ + Name: name, + Namespace: getNamespace(authorino), + }, + } + _, err := controllerutil.CreateOrUpdate(context.TODO(), r.Client, obj, func() error { + obj.Spec.Ports = service + obj.Spec.Selector = labelsForAuthorino(authorino.GetName()) + return nil + }) + if err != nil { + return fmt.Errorf("Failed creating %s service, err: %w", name, err) + } + } + + return nil +} + +func getNamespace(authorino *authorinooperatorv1beta1.Authorino) string { + namespace := authorinooperatorv1beta1.AuthorinoOperatorNamespace + if !authorino.Spec.ClusterWide { + namespace = authorino.Namespace + } + return namespace +} + +func labelsForAuthorino(name string) map[string]string { + return map[string]string{ + "control-plane": "controller-manager", + "authorino_cr_name": name, + } +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go new file mode 100644 index 00000000..671727b7 --- /dev/null +++ b/controllers/suite_test.go @@ -0,0 +1,80 @@ +/* +Copyright 2021. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + authorinooperatorv1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + cfg, err := testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = authorinooperatorv1beta1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/go.mod b/go.mod index d111a798..7e8ffd76 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,17 @@ module github.com/kuadrant/authorino-operator go 1.16 require ( +<<<<<<< HEAD k8s.io/apimachinery v0.20.2 k8s.io/client-go v0.20.2 sigs.k8s.io/controller-runtime v0.8.3 +======= + github.com/go-logr/logr v0.4.0 + github.com/onsi/ginkgo v1.16.4 + github.com/onsi/gomega v1.13.0 + k8s.io/api v0.21.2 + k8s.io/apimachinery v0.21.2 + k8s.io/client-go v0.21.2 + sigs.k8s.io/controller-runtime v0.9.2 +>>>>>>> 2620723 (Adds reconcilation of Authorino instances) ) diff --git a/go.sum b/go.sum index 5260fd49..9da8e51b 100644 --- a/go.sum +++ b/go.sum @@ -26,24 +26,22 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest v0.11.12 h1:gI8ytXbxMfI+IVbI9mP2JGCTXIuhHLgRlvQ9X4PsnHE= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1 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/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -51,6 +49,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy 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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -83,12 +82,13 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -97,42 +97,46 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+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/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs= -github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4= -github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= +github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= +github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= 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/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= 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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/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= @@ -157,8 +161,10 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -166,8 +172,9 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -185,8 +192,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -221,31 +228,35 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -266,7 +277,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= 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= @@ -276,23 +288,28 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= -github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= +github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -307,8 +324,9 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -317,14 +335,16 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -335,6 +355,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -359,14 +380,16 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -377,19 +400,16 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -398,8 +418,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U 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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -430,8 +451,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -459,8 +480,11 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/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-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 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= @@ -473,6 +497,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-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/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= @@ -494,6 +520,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -505,27 +532,39 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs= +golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -543,8 +582,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -563,15 +600,18 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k= -gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -587,8 +627,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -608,6 +648,7 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -625,13 +666,16 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -649,55 +693,53 @@ 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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw= -k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= -k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ= -k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= -k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ= -k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= -k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.2 h1:LMmu5I0pLtwjpp5009KLuMGFqSc2S2isGw8t1hpYKLE= -k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0= +k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y= +k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= +k8s.io/apiextensions-apiserver v0.21.2 h1:+exKMRep4pDrphEafRvpEi79wTnCFMqKf8LBtlA3yrE= +k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA= +k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc= +k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= +k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw= +k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0= +k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= +k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U= +k8s.io/component-base v0.21.2 h1:EsnmFFoJ86cEywC0DoIkAUiEV6fjgauNugiw1lmIjs4= +k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 h1:0T5IaWHO3sJTEmCP6mUlBvMukxPKUQWqiI/YuiBNMiQ= -k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210527160623-6fdb442a123b h1:MSqsVQ3pZvPGTqCjptfimO2WjG7A9un2zcpiHkA6M/s= +k8s.io/utils v0.0.0-20210527160623-6fdb442a123b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.8.3 h1:GMHvzjTmaWHQB8HadW+dIvBoJuLvZObYJ5YoZruPRao= -sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/controller-runtime v0.9.2 h1:MnCAsopQno6+hI9SgJHKddzXpmv2wtouZz6931Eax+Q= +sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= \ No newline at end of file diff --git a/main.go b/main.go index 62d772e0..146d7ecb 100644 --- a/main.go +++ b/main.go @@ -30,6 +30,9 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + + authorinooperatorv1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" + "github.com/kuadrant/authorino-operator/controllers" //+kubebuilder:scaffold:imports ) @@ -41,6 +44,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(authorinooperatorv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -67,13 +71,21 @@ func main() { Port: 9443, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, - LeaderElectionID: "f0fb677d.authorino.3scale.net", + LeaderElectionID: "aac3a15d.kuadrant.3scale.net", }) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) } + if err = (&controllers.AuthorinoReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("authorino-operator").WithName("controller").WithName("Authorino"), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Authorino") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From 4113b03db8ee77f2bcc7b6b004b741e7827bceea Mon Sep 17 00:00:00 2001 From: jjaferson Date: Thu, 28 Oct 2021 17:59:54 +0100 Subject: [PATCH 2/6] Adds RBAC permission for Authorino instances --- Makefile | 6 +- .../crd/authorino.3scale.net_authconfigs.yaml | 1040 +++++++++++++++++ config/authorino/crd/kustomization.yaml | 24 + config/authorino/crd/kustomizeconfig.yaml | 17 + .../patches/cainjection_in_authconfigs.yaml | 8 + .../crd/patches/oneof_in_authconfigs.yaml | 57 + .../crd/patches/webhook_in_authconfigs.yaml | 17 + config/authorino/kustomization.yaml | 12 + config/authorino/rbac/kustomization.yaml | 7 + config/authorino/rbac/role.yaml | 45 + .../authorino/rbac/service_editor_role.yaml | 23 + .../authorino/rbac/service_viewer_role.yaml | 20 + config/default/kustomization.yaml | 2 +- config/manager/kustomization.yaml | 10 +- config/rbac/kustomization.yaml | 1 + controllers/authorino_controller.go | 197 +++- 16 files changed, 1463 insertions(+), 23 deletions(-) create mode 100644 config/authorino/crd/authorino.3scale.net_authconfigs.yaml create mode 100644 config/authorino/crd/kustomization.yaml create mode 100644 config/authorino/crd/kustomizeconfig.yaml create mode 100644 config/authorino/crd/patches/cainjection_in_authconfigs.yaml create mode 100644 config/authorino/crd/patches/oneof_in_authconfigs.yaml create mode 100644 config/authorino/crd/patches/webhook_in_authconfigs.yaml create mode 100644 config/authorino/kustomization.yaml create mode 100644 config/authorino/rbac/kustomization.yaml create mode 100644 config/authorino/rbac/role.yaml create mode 100644 config/authorino/rbac/service_editor_role.yaml create mode 100644 config/authorino/rbac/service_viewer_role.yaml diff --git a/Makefile b/Makefile index 5f774b46..f0b16c47 100644 --- a/Makefile +++ b/Makefile @@ -106,8 +106,8 @@ docker-push: ## Push docker image with the manager. ##@ Deployment -install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - +install: manifests kustomize install-authorino ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/crd | kubectl delete -f - @@ -119,6 +119,8 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - +install-authorino: ## install RBAC and CRD for authorino + $(KUSTOMIZE) build config/authorino | kubectl apply -f - CONTROLLER_GEN = $(shell pwd)/bin/controller-gen controller-gen: ## Download controller-gen locally if necessary. diff --git a/config/authorino/crd/authorino.3scale.net_authconfigs.yaml b/config/authorino/crd/authorino.3scale.net_authconfigs.yaml new file mode 100644 index 00000000..a7ed747e --- /dev/null +++ b/config/authorino/crd/authorino.3scale.net_authconfigs.yaml @@ -0,0 +1,1040 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: authconfigs.authorino.3scale.net +spec: + group: authorino.3scale.net + names: + kind: AuthConfig + listKind: AuthConfigList + plural: authconfigs + singular: authconfig + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Ready? + jsonPath: .status.ready + name: Ready + type: boolean + - description: Number of trusted identity sources + jsonPath: .status.numIdentitySources + name: Id sources + priority: 2 + type: integer + - description: Number of external metadata sources + jsonPath: .status.numMetadataSources + name: Metadata sources + priority: 2 + type: integer + - description: Number of authorization policies + jsonPath: .status.numAuthorizationPolicies + name: Authz policies + priority: 2 + type: integer + - description: Number of items added to the client response + jsonPath: .status.numResponseItems + name: Response items + priority: 2 + type: integer + - description: Whether issuing Festival Wristbands + jsonPath: .status.festivalWristbandEnabled + name: Wristband + priority: 2 + type: boolean + name: v1beta1 + schema: + openAPIV3Schema: + description: AuthConfig is the schema for Authorino's AuthConfig API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Specifies the desired state of the AuthConfig resource, i.e. + the authencation/authorization scheme to be applied to protect the matching + service hosts. + properties: + authorization: + description: Authorization is the list of authorization policies. + All policies in this list MUST evaluate to "true" for a request + be successful in the authorization phase. + items: + description: 'Authorization policy to be enforced. Apart from "name", + one of the following parameters is required and only one of the + following parameters is allowed: "opa", "json" or "kubernetes".' + properties: + json: + description: JSON pattern matching authorization policy. + properties: + conditions: + description: Conditions that must match for Authorino to + enforce this policy; otherwise, the policy will be skipped. + items: + properties: + operator: + description: 'The binary operator to be applied to + the content fetched from the authorization JSON, + for comparison with "value". Possible values are: + "eq" (equal to), "neq" (not equal to), "incl" (includes; + for arrays), "excl" (excludes; for arrays), "matches" + (regex)' + enum: + - eq + - neq + - incl + - excl + - matches + type: string + selector: + description: Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. + The value is used to fetch content from the input + authorization JSON built by Authorino along the + identity and metadata phases. + type: string + value: + description: The value of reference for the comparison + with the content fetched from the authorization + policy. If used with the "matches" operator, the + value must compile to a valid Golang regex. + type: string + required: + - operator + - selector + - value + type: object + type: array + rules: + description: The rules that must all evaluate to "true" + for the request to be authorized. + items: + properties: + operator: + description: 'The binary operator to be applied to + the content fetched from the authorization JSON, + for comparison with "value". Possible values are: + "eq" (equal to), "neq" (not equal to), "incl" (includes; + for arrays), "excl" (excludes; for arrays), "matches" + (regex)' + enum: + - eq + - neq + - incl + - excl + - matches + type: string + selector: + description: Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. + The value is used to fetch content from the input + authorization JSON built by Authorino along the + identity and metadata phases. + type: string + value: + description: The value of reference for the comparison + with the content fetched from the authorization + policy. If used with the "matches" operator, the + value must compile to a valid Golang regex. + type: string + required: + - operator + - selector + - value + type: object + type: array + type: object + kubernetes: + description: Kubernetes authorization policy based on `SubjectAccessReview` + Path and Verb are inferred from the request. + properties: + conditions: + description: Conditions that must match for Authorino to + enforce this policy; otherwise, the policy will be skipped. + items: + properties: + operator: + description: 'The binary operator to be applied to + the content fetched from the authorization JSON, + for comparison with "value". Possible values are: + "eq" (equal to), "neq" (not equal to), "incl" (includes; + for arrays), "excl" (excludes; for arrays), "matches" + (regex)' + enum: + - eq + - neq + - incl + - excl + - matches + type: string + selector: + description: Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. + The value is used to fetch content from the input + authorization JSON built by Authorino along the + identity and metadata phases. + type: string + value: + description: The value of reference for the comparison + with the content fetched from the authorization + policy. If used with the "matches" operator, the + value must compile to a valid Golang regex. + type: string + required: + - operator + - selector + - value + type: object + type: array + groups: + description: Groups to test for. + items: + type: string + type: array + resourceAttributes: + description: Use ResourceAttributes for checking permissions + on Kubernetes resources If omitted, it performs a non-resource + `SubjectAccessReview`, with verb and path inferred from + the request. + properties: + group: + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') + or a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + name: + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') + or a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + namespace: + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') + or a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + resource: + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') + or a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + subresource: + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') + or a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + verb: + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') + or a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + type: object + user: + description: User to test for. If without "Groups", then + is it interpreted as "What if User were not a member of + any groups" + properties: + value: + type: string + valueFrom: + properties: + authJSON: + description: 'Selector to fill the value from the + authorization JSON. Any patterns supported by + https://pkg.go.dev/github.com/tidwall/gjson can + be used. The value can be just the pattern with + the path to fetch from the authorization JSON + (e.g. ''context.request.http.host'') or a string + template with variable placeholders that resolve + to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + type: object + required: + - user + type: object + name: + description: Name of the authorization policy. + type: string + opa: + description: Open Policy Agent (OPA) authorization policy. + properties: + externalRegistry: + description: External registry of OPA policies. + properties: + credentials: + description: Defines where client credentials will be + passed in the request to the service. If omitted, + it defaults to client credentials passed in the HTTP + Authorization header and the "Bearer" prefix expected + prepended to the secret value. + properties: + in: + default: authorization_header + description: The location in the request where client + credentials shall be passed on requests authenticating + with this identity source/authentication mode. + enum: + - authorization_header + - custom_header + - query + - cookie + type: string + keySelector: + description: Used in conjunction with the `in` parameter. + When used with `authorization_header`, the value + is the prefix of the client credentials string, + separated by a white-space, in the HTTP Authorization + header (e.g. "Bearer", "Basic"). When used with + `custom_header`, `query` or `cookie`, the value + is the name of the HTTP header, query string parameter + or cookie key, respectively. + type: string + required: + - keySelector + type: object + endpoint: + description: Endpoint of the HTTP external registry. + The endpoint must respond with either plain/text or + application/json content-type. In the latter case, + the JSON returned in the body must include a path + `result.raw`, where the raw Rego policy will be extracted + from. This complies with the specification of the + OPA REST API (https://www.openpolicyagent.org/docs/latest/rest-api/#get-a-policy). + type: string + sharedSecretRef: + description: Reference to a Secret key whose value will + be passed by Authorino in the request. The HTTP service + can use the shared secret to authenticate the origin + of the request. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + type: object + inlineRego: + description: Authorization policy as a Rego language document. + The Rego document must include the "allow" condition, + set by Authorino to "false" by default (i.e. requests + are unauthorized unless changed). The Rego document must + NOT include the "package" declaration in line 1. + type: string + type: object + required: + - name + type: object + type: array + denyWith: + description: Custom denial response codes, statuses and headers to + override default 40x's. + properties: + unauthenticated: + description: Denial status customization when the request is unauthenticated. + properties: + code: + description: HTTP status code to override the default denial + status code. + format: int64 + maximum: 599 + minimum: 300 + type: integer + headers: + description: HTTP response headers to override the default + denial headers. + items: + properties: + name: + description: The name of the claim + type: string + value: + description: Static value of the claim + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: Dynamic value of the claim + properties: + authJSON: + description: 'Selector to fill the value from the + authorization JSON. Any patterns supported by + https://pkg.go.dev/github.com/tidwall/gjson can + be used. The value can be just the pattern with + the path to fetch from the authorization JSON + (e.g. ''context.request.http.host'') or a string + template with variable placeholders that resolve + to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + required: + - name + type: object + type: array + message: + description: HTTP message to override the default denial message. + type: string + type: object + unauthorized: + description: Denial status customization when the request is unauthorized. + properties: + code: + description: HTTP status code to override the default denial + status code. + format: int64 + maximum: 599 + minimum: 300 + type: integer + headers: + description: HTTP response headers to override the default + denial headers. + items: + properties: + name: + description: The name of the claim + type: string + value: + description: Static value of the claim + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: Dynamic value of the claim + properties: + authJSON: + description: 'Selector to fill the value from the + authorization JSON. Any patterns supported by + https://pkg.go.dev/github.com/tidwall/gjson can + be used. The value can be just the pattern with + the path to fetch from the authorization JSON + (e.g. ''context.request.http.host'') or a string + template with variable placeholders that resolve + to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + required: + - name + type: object + type: array + message: + description: HTTP message to override the default denial message. + type: string + type: object + type: object + hosts: + description: The list of public host names of the services protected + by this authentication/authorization scheme. Authorino uses the + requested host to lookup for the corresponding authentication/authorization + configs to enforce. + items: + type: string + type: array + identity: + description: List of identity sources/authentication modes. At least + one config of this list MUST evaluate to a valid identity for a + request to be successful in the identity verification phase. + items: + description: 'The identity source/authentication mode config. Apart + from "name", one of the following parameters is required and only + one of the following parameters is allowed: "oicd", "apiKey" or + "kubernetes".' + properties: + apiKey: + properties: + labelSelectors: + additionalProperties: + type: string + description: The map of label selectors used by Authorino + to match secrets from the cluster storing valid credentials + to authenticate to this service + type: object + required: + - labelSelectors + type: object + credentials: + description: Defines where client credentials are required to + be passed in the request for this identity source/authentication + mode. If omitted, it defaults to client credentials passed + in the HTTP Authorization header and the "Bearer" prefix expected + prepended to the credentials value (token, API key, etc). + properties: + in: + default: authorization_header + description: The location in the request where client credentials + shall be passed on requests authenticating with this identity + source/authentication mode. + enum: + - authorization_header + - custom_header + - query + - cookie + type: string + keySelector: + description: Used in conjunction with the `in` parameter. + When used with `authorization_header`, the value is the + prefix of the client credentials string, separated by + a white-space, in the HTTP Authorization header (e.g. + "Bearer", "Basic"). When used with `custom_header`, `query` + or `cookie`, the value is the name of the HTTP header, + query string parameter or cookie key, respectively. + type: string + required: + - keySelector + type: object + extendedProperties: + description: Extends the resolved identity object with additional + custom properties before appending to the authorization JSON. + It requires the resolved identity object to always be of the + JSON type 'object'. Other JSON types (array, string, etc) + will break. + items: + properties: + name: + description: The name of the claim + type: string + value: + description: Static value of the claim + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: Dynamic value of the claim + properties: + authJSON: + description: 'Selector to fill the value from the + authorization JSON. Any patterns supported by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern with + the path to fetch from the authorization JSON (e.g. + ''context.request.http.host'') or a string template + with variable placeholders that resolve to patterns + (e.g. "Hello, {auth.identity.name}!") The following + string modifiers are available: @extract:{sep:" + ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, + and @base64:encode|decode.' + type: string + type: object + required: + - name + type: object + type: array + kubernetes: + properties: + audiences: + description: The list of audiences (scopes) that must be + claimed in a Kubernetes authentication token supplied + in the request, and reviewed by Authorino. If omitted, + Authorino will review tokens expecting the host name of + the requested protected service amongst the audiences. + items: + type: string + type: array + type: object + name: + description: The name of this identity source/authentication + mode. It usually identifies a source of identities or group + of users/clients of the protected service. It may as well + be used for this identity config to be referred in some metadata + configs. + type: string + oauth2: + properties: + credentialsRef: + description: Reference to a Kubernetes secret in the same + namespace, that stores client credentials to the OAuth2 + server. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + tokenIntrospectionUrl: + description: The full URL of the token introspection endpoint. + type: string + tokenTypeHint: + description: The token type hint for the token introspection. + If omitted, it defaults to "access_token". + type: string + required: + - credentialsRef + - tokenIntrospectionUrl + type: object + oidc: + properties: + endpoint: + description: Endpoint of the OIDC issuer. Authorino will + append to this value the well-known path to the OpenID + Connect discovery endpoint (i.e. "/.well-known/openid-configuration"), + used to automatically discover the OpenID Connect configuration, + whose set of claims is expected to include (among others) + the "jkws_uri" claim. The value must coincide with the + value of the "iss" (issuer) claim of the discovered OpenID + Connect configuration. + type: string + required: + - endpoint + type: object + required: + - name + type: object + type: array + metadata: + description: List of metadata source configs. Authorino fetches JSON + content from sources on this list on every request. + items: + description: 'The metadata config. Apart from "name", one of the + following parameters is required and only one of the following + parameters is allowed: "userInfo" or "uma".' + properties: + http: + description: Generic HTTP interface to obtain authorization + metadata from a HTTP service. + properties: + bodyParameters: + description: Custom parameters to encode in the body of + the HTTP request. Use it with method=POST; for GET requests, + specify parameters using placeholders in the endpoint. + items: + properties: + name: + description: The name of the claim + type: string + value: + description: Static value of the claim + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: Dynamic value of the claim + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') or + a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + required: + - name + type: object + type: array + contentType: + default: application/x-www-form-urlencoded + description: Content-Type of the request body. + enum: + - application/x-www-form-urlencoded + - application/json + type: string + credentials: + description: Defines where client credentials will be passed + in the request to the service. If omitted, it defaults + to client credentials passed in the HTTP Authorization + header and the "Bearer" prefix expected prepended to the + secret value. + properties: + in: + default: authorization_header + description: The location in the request where client + credentials shall be passed on requests authenticating + with this identity source/authentication mode. + enum: + - authorization_header + - custom_header + - query + - cookie + type: string + keySelector: + description: Used in conjunction with the `in` parameter. + When used with `authorization_header`, the value is + the prefix of the client credentials string, separated + by a white-space, in the HTTP Authorization header + (e.g. "Bearer", "Basic"). When used with `custom_header`, + `query` or `cookie`, the value is the name of the + HTTP header, query string parameter or cookie key, + respectively. + type: string + required: + - keySelector + type: object + endpoint: + description: Endpoint of the HTTP service. The endpoint + accepts variable placeholders in the format "{selector}", + where "selector" is any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson + and selects value from the authorization JSON. E.g. https://ext-auth-server.io/metadata?p={context.request.http.path} + type: string + method: + description: 'HTTP verb used in the request to the service. + Accepted values: GET (default), POST. When the request + method is POST, the authorization JSON is passed in the + body of the request.' + enum: + - GET + - POST + type: string + sharedSecretRef: + description: Reference to a Secret key whose value will + be passed by Authorino in the request. The HTTP service + can use the shared secret to authenticate the origin of + the request. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: The name of the secret in the Authorino's + namespace to select from. + type: string + required: + - key + - name + type: object + required: + - endpoint + type: object + name: + description: The name of the metadata source. Policies of te + authorization phase can refer to this metadata by this value. + type: string + uma: + description: User-Managed Access (UMA) source of resource data. + properties: + credentialsRef: + description: Reference to a Kubernetes secret in the same + namespace, that stores client credentials to the resource + registration API of the UMA server. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + endpoint: + description: The endpoint of the UMA server. The value must + coincide with the "issuer" claim of the UMA config discovered + from the well-known uma configuration endpoint. + type: string + required: + - credentialsRef + - endpoint + type: object + userInfo: + description: OpendID Connect UserInfo linked to an OIDC identity + config of this same spec. + properties: + identitySource: + description: The name of an OIDC identity source included + in the "identity" section and whose OpenID Connect configuration + discovered includes the OIDC "userinfo_endpoint" claim. + type: string + required: + - identitySource + type: object + required: + - name + type: object + type: array + response: + description: List of response configs. Authorino gathers data from + the auth pipeline to build custom responses for the client. + items: + description: 'Dynamic response to return to the client. Apart from + "name", one of the following parameters is required and only one + of the following parameters is allowed: "wristband" or "json".' + properties: + json: + properties: + properties: + description: List of JSON property-value pairs to be added + to the dynamic response. + items: + properties: + name: + description: The name of the claim + type: string + value: + description: Static value of the claim + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: Dynamic value of the claim + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') or + a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + required: + - name + type: object + type: array + required: + - properties + type: object + name: + description: Name of the custom response. + type: string + wrapper: + default: httpHeader + description: How Authorino wraps the response. Use "httpHeader" + (default) to wrap the response in an HTTP header; or "envoyDynamicMetadata" + to wrap the response as Envoy Dynamic Metadata + enum: + - httpHeader + - envoyDynamicMetadata + type: string + wrapperKey: + description: The name of key used in the wrapped response (name + of the HTTP header or property of the Envoy Dynamic Metadata + JSON). If omitted, it will be set to the name of the configuration. + type: string + wristband: + properties: + customClaims: + description: Any claims to be added to the wristband token + apart from the standard JWT claims (iss, iat, exp) added + by default. + items: + properties: + name: + description: The name of the claim + type: string + value: + description: Static value of the claim + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: Dynamic value of the claim + properties: + authJSON: + description: 'Selector to fill the value from + the authorization JSON. Any patterns supported + by https://pkg.go.dev/github.com/tidwall/gjson + can be used. The value can be just the pattern + with the path to fetch from the authorization + JSON (e.g. ''context.request.http.host'') or + a string template with variable placeholders + that resolve to patterns (e.g. "Hello, {auth.identity.name}!") + The following string modifiers are available: + @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, + @case:upper|lower, and @base64:encode|decode.' + type: string + type: object + required: + - name + type: object + type: array + issuer: + description: 'The endpoint to the Authorino service that + issues the wristband (format: ://:/, + where = / Date: Mon, 1 Nov 2021 16:00:07 +0000 Subject: [PATCH 3/6] Adds reconcilation of leader election permission --- .gitignore | 3 + Makefile | 2 +- api/v1beta1/authorino_types.go | 38 +++--- ...rator.kuadrant.3scale.net_authorinoes.yaml | 22 ++-- controllers/authorino_controller.go | 116 +++++++++++++++++- 5 files changed, 153 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index c0a7a54c..16ff6da4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ testbin/* *.swp *.swo *~ + +# Vendor dependencies +vendor \ No newline at end of file diff --git a/Makefile b/Makefile index f0b16c47..743f68c1 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,7 @@ docker-push: ## Push docker image with the manager. ##@ Deployment install: manifests kustomize install-authorino ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd + $(KUSTOMIZE) build config/crd | kubectl apply -f - uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/crd | kubectl delete -f - diff --git a/api/v1beta1/authorino_types.go b/api/v1beta1/authorino_types.go index 684f9369..5ba9f17d 100644 --- a/api/v1beta1/authorino_types.go +++ b/api/v1beta1/authorino_types.go @@ -33,12 +33,14 @@ const ( AuthorinoOperatorNamespace string = "authorino-operator" // Authorino EnvVars - ExtAuthGRPCPort string = "EXT_AUTH_GRPC_PORT" - TSLCertPath string = "TLS_CERT" - TSLCertKeyPath string = "TLS_CERT_KEY" - OIDCHTTPPort string = "OIDC_HTTP_PORT" - OIDCTSLCertPath string = "OIDC_TLS_CERT" - OIDCTLSCertKeyPath string = "OIDC_TLS_CERT_KEY" + ExtAuthGRPCPort string = "EXT_AUTH_GRPC_PORT" + TSLCertPath string = "TLS_CERT" + TSLCertKeyPath string = "TLS_CERT_KEY" + OIDCHTTPPort string = "OIDC_HTTP_PORT" + OIDCTSLCertPath string = "OIDC_TLS_CERT" + OIDCTLSCertKeyPath string = "OIDC_TLS_CERT_KEY" + AuthConfigLabelSelector string = "AUTH_CONFIG_LABEL_SELECTOR" + SecretLabelSelector string = "SECRET_LABEL_SELECTOR" ) type Condition struct { @@ -65,12 +67,14 @@ type AuthorinoSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Image string `json:"image,omitempty"` - Replicas *int32 `json:"replicas,omitempty"` - ImagePullPolicy string `json:"imagepullpolicy,omitempty"` - ClusterWide ClusterWideAuthorino `json:"clusterwide,omitempty"` - Listener Listener `json:"listener,omitempty"` - OIDCServer OIDCServer `json:"oidcserver,omitempty"` + Image string `json:"image,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ClusterWide ClusterWideAuthorino `json:"clusterWide,omitempty"` + Listener Listener `json:"listener,omitempty"` + OIDCServer OIDCServer `json:"oidcServer,omitempty"` + AuthConfigLabelSelectors string `json:"authConfigLabelSelectors,omitempty"` + SecretLabelSelectors string `json:"secretLabelSelectors,omitempty"` } type ClusterWideAuthorino bool @@ -78,14 +82,14 @@ type ClusterWideAuthorino bool type Listener struct { Port *int32 `json:"port,omitempty"` Tls bool `json:"tsl,omitempty"` - CertPath string `json:"certpath,omitempty"` - CertKeyPath string `json:"certkeypath,omitempty"` + CertPath string `json:"certPath,omitempty"` + CertKeyPath string `json:"certKeyPath,omitempty"` } type OIDCServer struct { Port *int32 `json:"port,omitempty"` - CertPath string `json:"certpath,omitempty"` - CertKeyPath string `json:"certkeypath,omitempty"` + CertPath string `json:"certPath,omitempty"` + CertKeyPath string `json:"certKeyPath,omitempty"` } // AuthorinoStatus defines the observed state of Authorino @@ -94,7 +98,7 @@ type AuthorinoStatus struct { // Important: Run "make" to regenerate code after modifying this file // Number of authorino instances in the cluster - AuthorinoInstances int32 `json:"authorinoinstances"` + AuthorinoInstances int32 `json:"authorinoInstances"` // Conditions is an array of the current Authorino's CR conditions // Supported condition types: ConditionReady diff --git a/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml b/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml index 07cb0179..89884f76 100644 --- a/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml +++ b/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml @@ -36,17 +36,19 @@ spec: spec: description: AuthorinoSpec defines the desired state of Authorino properties: - clusterwide: + authConfigLabelSelectors: + type: string + clusterWide: type: boolean image: type: string - imagepullpolicy: + imagePullPolicy: type: string listener: properties: - certkeypath: + certKeyPath: type: string - certpath: + certPath: type: string port: format: int32 @@ -54,11 +56,11 @@ spec: tsl: type: boolean type: object - oidcserver: + oidcServer: properties: - certkeypath: + certKeyPath: type: string - certpath: + certPath: type: string port: format: int32 @@ -67,11 +69,13 @@ spec: replicas: format: int32 type: integer + secretLabelSelectors: + type: string type: object status: description: AuthorinoStatus defines the observed state of Authorino properties: - authorinoinstances: + authorinoInstances: description: Number of authorino instances in the cluster format: int32 type: integer @@ -111,7 +115,7 @@ spec: - type x-kubernetes-list-type: map required: - - authorinoinstances + - authorinoInstances type: object type: object served: true diff --git a/controllers/authorino_controller.go b/controllers/authorino_controller.go index 33deeebf..a3a49733 100644 --- a/controllers/authorino_controller.go +++ b/controllers/authorino_controller.go @@ -90,7 +90,6 @@ func (r *AuthorinoReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - //TODO: check permission for leader election, proxy and so on err = r.authorinoPermission(authorinoInstance, req.NamespacedName.Namespace) if err != nil { log.Error(err, "Failed to create authorino permission") @@ -206,6 +205,20 @@ func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *authorinooperatorv1be }) } + if authorino.Spec.AuthConfigLabelSelectors != "" { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.AuthConfigLabelSelector, + Value: fmt.Sprint(authorino.Spec.AuthConfigLabelSelectors), + }) + } + + if authorino.Spec.SecretLabelSelectors != "" { + envVar = append(envVar, corev1.EnvVar{ + Name: authorinooperatorv1beta1.SecretLabelSelector, + Value: fmt.Sprint(authorino.Spec.SecretLabelSelectors), + }) + } + // external auth service via GRPC if authorino.Spec.Listener.Port != nil { envVar = append(envVar, corev1.EnvVar{ @@ -317,6 +330,12 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 ) } + // creates leader election role + err = r.leaderElectionPermission(authorino, serviceAccount.GetName()) + if err != nil { + return err + } + if authorino.Spec.ClusterWide { clusterRoleBinding := &rbacv1.ClusterRoleBinding{ ObjectMeta: v1.ObjectMeta{ @@ -404,6 +423,101 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 return nil } +func (r *AuthorinoReconciler) leaderElectionPermission(authorino *authorinooperatorv1beta1.Authorino, saName string) error { + leaderElectionRole := &rbacv1.Role{ + ObjectMeta: v1.ObjectMeta{ + Name: "authorino-leader-election-role", + Namespace: authorino.GetNamespace(), + Labels: labelsForAuthorino(authorino.GetName()), + }, + } + + err := r.Get(context.TODO(), client.ObjectKeyFromObject(leaderElectionRole), leaderElectionRole) + if err != nil && !errors.IsNotFound(err) { + return fmt.Errorf( + "Failed to get leader election role err: %d", + err, + ) + } + + if errors.IsNotFound(err) { + ctrl.SetControllerReference(authorino, leaderElectionRole, r.Scheme) + leaderElectionRole.Rules = getLeaderElectionRules() + err = r.Create(context.TODO(), leaderElectionRole) + if err != nil { + return fmt.Errorf( + "Failed to create leader election role, err: %d", + err, + ) + } + } + + prefix := authorino.GetName() + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: v1.ObjectMeta{ + Name: prefix + "-authorino-leader-election", + Namespace: authorino.GetNamespace(), + }, + } + err = r.Get(context.TODO(), client.ObjectKeyFromObject(roleBinding), roleBinding) + if err != nil && !errors.IsNotFound(err) { + return fmt.Errorf( + "Failed to get roleBinding %s for authorino instance %s, err: %d", + roleBinding.GetName(), + authorino.GetName(), + err, + ) + } + + if errors.IsNotFound(err) { + roleBinding.RoleRef = rbacv1.RoleRef{ + Name: roleBinding.GetName(), + Kind: "Role", + } + roleBinding.Subjects = []rbacv1.Subject{ + { + Kind: rbacv1.ServiceAccountKind, + Name: saName, + Namespace: authorino.GetNamespace(), + }, + } + ctrl.SetControllerReference(authorino, roleBinding, r.Scheme) + err = r.Create(context.TODO(), roleBinding) + if err != nil { + return fmt.Errorf( + "Failed to create leader election role binding, err: %d", + err, + ) + } + } + return nil +} + +func getLeaderElectionRules() []rbacv1.PolicyRule { + return []rbacv1.PolicyRule{ + { + APIGroups: []string{"*"}, + Resources: []string{"configmaps"}, + Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"}, + }, + { + APIGroups: []string{"*"}, + Resources: []string{"configmaps/status"}, + Verbs: []string{"get", "update", "patch"}, + }, + { + APIGroups: []string{"*"}, + Resources: []string{"events"}, + Verbs: []string{"create", "patch"}, + }, + { + APIGroups: []string{"coordination.k8s.io"}, + Resources: []string{"leases"}, + Verbs: []string{"get", "list", "create", "update"}, + }, + } +} + func (r *AuthorinoReconciler) authorinoServices(authorino *authorinooperatorv1beta1.Authorino) error { services := make(map[string][]corev1.ServicePort) From 7fc7322cd907fa64414e49ae1da204f8abbe76f0 Mon Sep 17 00:00:00 2001 From: jjaferson Date: Mon, 1 Nov 2021 16:27:10 +0000 Subject: [PATCH 4/6] Adds status updates to authorino CRD --- .dockerignore | 3 - Makefile | 16 +- PROJECT | 5 +- api/v1beta1/authorino_types.go | 40 +-- api/v1beta1/groupversion_info.go | 4 +- ..._role.yaml => authconfig_editor_role.yaml} | 2 +- ..._role.yaml => authconfig_viewer_role.yaml} | 2 +- config/authorino/rbac/kustomization.yaml | 4 +- config/authorino/rbac/role.yaml | 12 + ...tor.authorino.kuadrant.io_authorinos.yaml} | 21 +- config/crd/kustomization.yaml | 6 +- ...es.yaml => cainjection_in_authorinos.yaml} | 2 +- ...rinoes.yaml => webhook_in_authorinos.yaml} | 2 +- config/default/manager_auth_proxy_patch.yaml | 3 - config/manager/controller_manager_config.yaml | 6 +- config/manager/kustomization.yaml | 2 +- config/manager/manager.yaml | 5 - config/rbac/auth_proxy_service.yaml | 3 - config/rbac/authorino_editor_role.yaml | 10 +- config/rbac/authorino_viewer_role.yaml | 10 +- config/rbac/role.yaml | 137 ++++++++- .../authorino-operator_v1beta1_authorino.yaml | 18 +- config/scorecard/patches/basic.config.yaml | 4 - controllers/authorino_controller.go | 285 +++++++++++------- go.mod | 6 - main.go | 2 +- 26 files changed, 395 insertions(+), 215 deletions(-) rename config/authorino/rbac/{service_editor_role.yaml => authconfig_editor_role.yaml} (91%) rename config/authorino/rbac/{service_viewer_role.yaml => authconfig_viewer_role.yaml} (91%) rename config/crd/bases/{authorino-operator.kuadrant.3scale.net_authorinoes.yaml => operator.authorino.kuadrant.io_authorinos.yaml} (89%) rename config/crd/patches/{cainjection_in_authorinoes.yaml => cainjection_in_authorinos.yaml} (81%) rename config/crd/patches/{webhook_in_authorinoes.yaml => webhook_in_authorinos.yaml} (85%) diff --git a/.dockerignore b/.dockerignore index 6c6bd0ba..c16e8a89 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,11 +1,8 @@ # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file -<<<<<<< HEAD # Ignore all files which are not go type !**/*.go !**/*.mod !**/*.sum -======= # Ignore build and test binaries. bin/ testbin/ ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) diff --git a/Makefile b/Makefile index 743f68c1..0cf65380 100644 --- a/Makefile +++ b/Makefile @@ -28,15 +28,15 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) # This variable is used to construct full image tags for bundle and catalog images. # # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both -# kuadrant.3scale.net/authorino-operator-bundle:$VERSION and kuadrant.3scale.net/authorino-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= kuadrant.3scale.net/authorino-operator +# authorino.kuadrant.io/authorino-operator-bundle:$VERSION and authorino.kuadrant.io/authorino-operator-catalog:$VERSION. +IMAGE_TAG_BASE ?= authorino.kuadrant.io/authorino-operator # BUNDLE_IMG defines the image:tag used for the bundle. # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) # Image URL to use all building/pushing image targets -IMG ?= controller:latest +IMG ?= authorino-operator:latest # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. @@ -98,7 +98,7 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -docker-build: test ## Build docker image with the manager. +docker-build: ## Build docker image with the manager. docker build -t ${IMG} . docker-push: ## Push docker image with the manager. @@ -119,8 +119,12 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - -install-authorino: ## install RBAC and CRD for authorino - $(KUSTOMIZE) build config/authorino | kubectl apply -f - +AUTHORINO_VERSION=v0.4.0 +install-authorino: kustomize ## install RBAC and CRD for authorino + $(eval TMP := $(shell mktemp -d)) + cd $(TMP); git clone --depth 1 --branch $(AUTHORINO_VERSION) https://github.com/kuadrant/authorino.git + cd $(TMP)/authorino; $(KUSTOMIZE) build install | kubectl apply -f - + -rm -rf $(TMP) CONTROLLER_GEN = $(shell pwd)/bin/controller-gen controller-gen: ## Download controller-gen locally if necessary. diff --git a/PROJECT b/PROJECT index 7afc9351..3acc84cf 100644 --- a/PROJECT +++ b/PROJECT @@ -1,4 +1,4 @@ -domain: kuadrant.3scale.net +domain: authorino.kuadrant.io layout: - go.kubebuilder.io/v3 plugins: @@ -11,9 +11,10 @@ resources: crdVersion: v1 namespaced: true controller: true - domain: kuadrant.3scale.net + domain: authorino.kuadrant.io group: authorino-operator kind: Authorino path: github.com/kuadrant/authorino-operator/api/v1beta1 version: v1beta1 + plural: authorinos version: "3" diff --git a/api/v1beta1/authorino_types.go b/api/v1beta1/authorino_types.go index 5ba9f17d..b8b1be19 100644 --- a/api/v1beta1/authorino_types.go +++ b/api/v1beta1/authorino_types.go @@ -28,16 +28,16 @@ type ConditionType string const ( // ConditionReady specifies that the resource is ready - ConditionReady ConditionType = "Ready" - AuthorinoContainerName string = "authorino" - AuthorinoOperatorNamespace string = "authorino-operator" + ConditionReady ConditionType = "Ready" + AuthorinoContainerName string = "authorino" // Authorino EnvVars + WatchNamespace string = "WATCH_NAMESPACE" ExtAuthGRPCPort string = "EXT_AUTH_GRPC_PORT" - TSLCertPath string = "TLS_CERT" - TSLCertKeyPath string = "TLS_CERT_KEY" + TLSCertPath string = "TLS_CERT" + TLSCertKeyPath string = "TLS_CERT_KEY" OIDCHTTPPort string = "OIDC_HTTP_PORT" - OIDCTSLCertPath string = "OIDC_TLS_CERT" + OIDCTLSCertPath string = "OIDC_TLS_CERT" OIDCTLSCertKeyPath string = "OIDC_TLS_CERT_KEY" AuthConfigLabelSelector string = "AUTH_CONFIG_LABEL_SELECTOR" SecretLabelSelector string = "SECRET_LABEL_SELECTOR" @@ -67,18 +67,16 @@ type AuthorinoSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Image string `json:"image,omitempty"` - Replicas *int32 `json:"replicas,omitempty"` - ImagePullPolicy string `json:"imagePullPolicy,omitempty"` - ClusterWide ClusterWideAuthorino `json:"clusterWide,omitempty"` - Listener Listener `json:"listener,omitempty"` - OIDCServer OIDCServer `json:"oidcServer,omitempty"` - AuthConfigLabelSelectors string `json:"authConfigLabelSelectors,omitempty"` - SecretLabelSelectors string `json:"secretLabelSelectors,omitempty"` + Image string `json:"image,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` + ClusterWide bool `json:"clusterWide,omitempty"` + Listener Listener `json:"listener,omitempty"` + OIDCServer OIDCServer `json:"oidcServer,omitempty"` + AuthConfigLabelSelectors string `json:"authConfigLabelSelectors,omitempty"` + SecretLabelSelectors string `json:"secretLabelSelectors,omitempty"` } -type ClusterWideAuthorino bool - type Listener struct { Port *int32 `json:"port,omitempty"` Tls bool `json:"tsl,omitempty"` @@ -97,8 +95,11 @@ type AuthorinoStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - // Number of authorino instances in the cluster - AuthorinoInstances int32 `json:"authorinoInstances"` + // Defines if the authorino intance is ready + Ready bool `json:"ready"` + + // Reports an error during the deployment of an instance + LastError string `json:"lastError"` // Conditions is an array of the current Authorino's CR conditions // Supported condition types: ConditionReady @@ -112,8 +113,9 @@ type AuthorinoStatus struct { //+kubebuilder:object:root=true //+kubebuilder:subresource:status +//+kubebuilder:resource:path="authorinos" -// Authorino is the Schema for the authorinoes API +// Authorino is the Schema for the authorinos API type Authorino struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1beta1/groupversion_info.go b/api/v1beta1/groupversion_info.go index eaba7314..47fba8f3 100644 --- a/api/v1beta1/groupversion_info.go +++ b/api/v1beta1/groupversion_info.go @@ -16,7 +16,7 @@ limitations under the License. // Package v1beta1 contains API Schema definitions for the authorino-operator v1beta1 API group //+kubebuilder:object:generate=true -//+groupName=authorino-operator.kuadrant.3scale.net +//+groupName=operator.authorino.kuadrant.io package v1beta1 import ( @@ -26,7 +26,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "authorino-operator.kuadrant.3scale.net", Version: "v1beta1"} + GroupVersion = schema.GroupVersion{Group: "operator.authorino.kuadrant.io", Version: "v1beta1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/config/authorino/rbac/service_editor_role.yaml b/config/authorino/rbac/authconfig_editor_role.yaml similarity index 91% rename from config/authorino/rbac/service_editor_role.yaml rename to config/authorino/rbac/authconfig_editor_role.yaml index 131f350c..783cb354 100644 --- a/config/authorino/rbac/service_editor_role.yaml +++ b/config/authorino/rbac/authconfig_editor_role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: service-editor-role + name: authconfig-editor-role rules: - apiGroups: - authorino.3scale.net diff --git a/config/authorino/rbac/service_viewer_role.yaml b/config/authorino/rbac/authconfig_viewer_role.yaml similarity index 91% rename from config/authorino/rbac/service_viewer_role.yaml rename to config/authorino/rbac/authconfig_viewer_role.yaml index 0398aea8..cd4c9b14 100644 --- a/config/authorino/rbac/service_viewer_role.yaml +++ b/config/authorino/rbac/authconfig_viewer_role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: service-viewer-role + name: authconfig-viewer-role rules: - apiGroups: - authorino.3scale.net diff --git a/config/authorino/rbac/kustomization.yaml b/config/authorino/rbac/kustomization.yaml index 609e0817..7b779dd8 100644 --- a/config/authorino/rbac/kustomization.yaml +++ b/config/authorino/rbac/kustomization.yaml @@ -3,5 +3,5 @@ kind: Kustomization resources: - role.yaml -- service_editor_role.yaml -- service_viewer_role.yaml +- authconfig_editor_role.yaml +- authconfig_viewer_role.yaml diff --git a/config/authorino/rbac/role.yaml b/config/authorino/rbac/role.yaml index e994c8de..a4c7f6d9 100644 --- a/config/authorino/rbac/role.yaml +++ b/config/authorino/rbac/role.yaml @@ -43,3 +43,15 @@ rules: - get - list - watch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create \ No newline at end of file diff --git a/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml b/config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml similarity index 89% rename from config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml rename to config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml index 89884f76..3e3863e8 100644 --- a/config/crd/bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml +++ b/config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml @@ -6,20 +6,20 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null - name: authorinoes.authorino-operator.kuadrant.3scale.net + name: authorinos.operator.authorino.kuadrant.io spec: - group: authorino-operator.kuadrant.3scale.net + group: operator.authorino.kuadrant.io names: kind: Authorino listKind: AuthorinoList - plural: authorinoes + plural: authorinos singular: authorino scope: Namespaced versions: - name: v1beta1 schema: openAPIV3Schema: - description: Authorino is the Schema for the authorinoes API + description: Authorino is the Schema for the authorinos API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -75,10 +75,6 @@ spec: status: description: AuthorinoStatus defines the observed state of Authorino properties: - authorinoInstances: - description: Number of authorino instances in the cluster - format: int32 - type: integer conditions: description: 'Conditions is an array of the current Authorino''s CR conditions Supported condition types: ConditionReady' @@ -114,8 +110,15 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map + lastError: + description: Reports an error during the deployment of an instance + type: string + ready: + description: ' Defines if the authorino intance is ready' + type: boolean required: - - authorinoInstances + - lastError + - ready type: object type: object served: true diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 4314bd16..ab0ea7e2 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -2,18 +2,18 @@ # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default resources: -- bases/authorino-operator.kuadrant.3scale.net_authorinoes.yaml +- bases/operator.authorino.kuadrant.io_authorinos.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_authorinoes.yaml +#- patches/webhook_in_authorinos.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_authorinoes.yaml +#- patches/cainjection_in_authorinos.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_authorinoes.yaml b/config/crd/patches/cainjection_in_authorinos.yaml similarity index 81% rename from config/crd/patches/cainjection_in_authorinoes.yaml rename to config/crd/patches/cainjection_in_authorinos.yaml index 626b97b0..5058e59e 100644 --- a/config/crd/patches/cainjection_in_authorinoes.yaml +++ b/config/crd/patches/cainjection_in_authorinos.yaml @@ -4,4 +4,4 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: authorinoes.authorino-operator.kuadrant.3scale.net + name: authorinos.operator.authorino.kuadrant.io diff --git a/config/crd/patches/webhook_in_authorinoes.yaml b/config/crd/patches/webhook_in_authorinos.yaml similarity index 85% rename from config/crd/patches/webhook_in_authorinoes.yaml rename to config/crd/patches/webhook_in_authorinos.yaml index 4c3f9d8f..0f94c189 100644 --- a/config/crd/patches/webhook_in_authorinoes.yaml +++ b/config/crd/patches/webhook_in_authorinos.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: authorinoes.authorino-operator.kuadrant.3scale.net + name: authorinos.operator.authorino.kuadrant.io spec: conversion: strategy: Webhook diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index 1ea03693..4e2232fa 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -18,10 +18,7 @@ spec: - "--v=10" ports: - containerPort: 8443 -<<<<<<< HEAD -======= protocol: TCP ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) name: https - name: manager args: diff --git a/config/manager/controller_manager_config.yaml b/config/manager/controller_manager_config.yaml index 88ab9298..0b230632 100644 --- a/config/manager/controller_manager_config.yaml +++ b/config/manager/controller_manager_config.yaml @@ -8,8 +8,4 @@ webhook: port: 9443 leaderElection: leaderElect: true -<<<<<<< HEAD - resourceName: f0fb677d.authorino.3scale.net -======= - resourceName: aac3a15d.kuadrant.3scale.net ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) + resourceName: aac3a15d.authorino.kuadrant.io diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5e793dd1..421a8f2a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -12,5 +12,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: controller + newName: authorino-operator newTag: latest diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index d812042e..20262442 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -47,13 +47,8 @@ spec: periodSeconds: 10 resources: limits: -<<<<<<< HEAD - cpu: 100m - memory: 30Mi -======= cpu: 200m memory: 100Mi ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) requests: cpu: 100m memory: 20Mi diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml index 4d42e129..71f17972 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/rbac/auth_proxy_service.yaml @@ -9,10 +9,7 @@ spec: ports: - name: https port: 8443 -<<<<<<< HEAD -======= protocol: TCP ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) targetPort: https selector: control-plane: controller-manager diff --git a/config/rbac/authorino_editor_role.yaml b/config/rbac/authorino_editor_role.yaml index 74be37ae..720bee52 100644 --- a/config/rbac/authorino_editor_role.yaml +++ b/config/rbac/authorino_editor_role.yaml @@ -1,13 +1,13 @@ -# permissions for end users to edit authorinoes. +# permissions for end users to edit authorinos. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: authorino-editor-role rules: - apiGroups: - - authorino-operator.kuadrant.3scale.net + - operator.authorino.kuadrant.io resources: - - authorinoes + - authorinos verbs: - create - delete @@ -17,8 +17,8 @@ rules: - update - watch - apiGroups: - - authorino-operator.kuadrant.3scale.net + - operator.authorino.kuadrant.io resources: - - authorinoes/status + - authorinos/status verbs: - get diff --git a/config/rbac/authorino_viewer_role.yaml b/config/rbac/authorino_viewer_role.yaml index e78a7477..caaaf2bb 100644 --- a/config/rbac/authorino_viewer_role.yaml +++ b/config/rbac/authorino_viewer_role.yaml @@ -1,20 +1,20 @@ -# permissions for end users to view authorinoes. +# permissions for end users to view authorinos. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: authorino-viewer-role rules: - apiGroups: - - authorino-operator.kuadrant.3scale.net + - operator.authorino.kuadrant.io resources: - - authorinoes + - authorinos verbs: - get - list - watch - apiGroups: - - authorino-operator.kuadrant.3scale.net + - operator.authorino.kuadrant.io resources: - - authorinoes/status + - authorinos/status verbs: - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index cc3c9938..ea08fcb2 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,6 +6,102 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - '*' + resources: + - clusterrolebindings + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - '*' + resources: + - clusterroles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - '*' + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - '*' + resources: + - configmaps/status + verbs: + - delete + - get + - patch + - update +- apiGroups: + - '*' + resources: + - events + verbs: + - create + - patch +- apiGroups: + - '*' + resources: + - rolebindings + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - '*' + resources: + - roles + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - '*' + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - '*' + resources: + - serviceaccounts + verbs: + - create + - get + - list + - update + - watch +- apiGroups: + - '*' + resources: + - services + verbs: + - create + - get + - list + - update + - watch - apiGroups: - apps resources: @@ -19,9 +115,38 @@ rules: - update - watch - apiGroups: - - authorino-operator.kuadrant.3scale.net + - authorino.3scale.net + resources: + - authconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - authorino.3scale.net + resources: + - authconfigs/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update +- apiGroups: + - operator.authorino.kuadrant.io resources: - - authorinoes + - authorinos verbs: - create - delete @@ -31,15 +156,15 @@ rules: - update - watch - apiGroups: - - authorino-operator.kuadrant.3scale.net + - operator.authorino.kuadrant.io resources: - - authorinoes/finalizers + - authorinos/finalizers verbs: - update - apiGroups: - - authorino-operator.kuadrant.3scale.net + - operator.authorino.kuadrant.io resources: - - authorinoes/status + - authorinos/status verbs: - get - patch diff --git a/config/samples/authorino-operator_v1beta1_authorino.yaml b/config/samples/authorino-operator_v1beta1_authorino.yaml index 8f400e71..967c2553 100644 --- a/config/samples/authorino-operator_v1beta1_authorino.yaml +++ b/config/samples/authorino-operator_v1beta1_authorino.yaml @@ -1,19 +1,19 @@ -apiVersion: authorino-operator.kuadrant.3scale.net/v1beta1 +apiVersion: operator.authorino.kuadrant.io/v1beta1 kind: Authorino metadata: - name: authorino-sample + name: authorino-sample22 namespace: authorino-operator spec: image: quay.io/3scale/authorino:latest replicas: 1 - imagepullpolicy: Always - clusterwide: true + imagePullPolicy: Always + clusterWide: true listener: port: tsl: true - certpath: "" - certkeypath: "" - oidcserver: + certPath: "" + certKeyPath: "" + oidcServer: port: - certpath: "" - certkeypath: "" + certPath: "" + certKeyPath: "" diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index 9ab6754b..ebd7145c 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,11 +4,7 @@ entrypoint: - scorecard-test - basic-check-spec -<<<<<<< HEAD - image: quay.io/operator-framework/scorecard-test:v1.9.0 -======= image: quay.io/operator-framework/scorecard-test:v1.13.1 ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) labels: suite: basic test: basic-check-spec-test diff --git a/controllers/authorino_controller.go b/controllers/authorino_controller.go index a3a49733..9b18fd6b 100644 --- a/controllers/authorino_controller.go +++ b/controllers/authorino_controller.go @@ -19,12 +19,11 @@ package controllers import ( "context" "fmt" - "os" "time" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" + k8sapps "k8s.io/api/apps/v1" + k8score "k8s.io/api/core/v1" + k8srbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -35,22 +34,35 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "github.com/go-logr/logr" - authorinooperatorv1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" + api "github.com/kuadrant/authorino-operator/api/v1beta1" ) // AuthorinoReconciler reconciles a Authorino object type AuthorinoReconciler struct { client.Client - OperatorNamespace string - Log logr.Logger - Scheme *runtime.Scheme + Log logr.Logger + Scheme *runtime.Scheme } -//+kubebuilder:rbac:groups=authorino-operator.kuadrant.3scale.net,resources=authorinoes,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=authorino-operator.kuadrant.3scale.net,resources=authorinoes/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=authorino-operator.kuadrant.3scale.net,resources=authorinoes/finalizers,verbs=update +//+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos/finalizers,verbs=update // +kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="*",resources=services,verbs=get;list;watch;create;update; +// +kubebuilder:rbac:groups="*",resources=clusterroles,verbs=get;list;watch;create;update; +// +kubebuilder:rbac:groups="*",resources=rolebindings,verbs=get;list;watch;create;update; +// +kubebuilder:rbac:groups="*",resources=clusterrolebindings,verbs=get;list;watch;create;update; +// +kubebuilder:rbac:groups="*",resources=serviceaccounts,verbs=get;list;watch;create;update; +// +kubebuilder:rbac:groups="*",resources=roles,verbs=get;list;watch;create;update; +// +kubebuilder:rbac:groups="*",resources=configmaps,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="*",resources=configmaps/status,verbs=get;update;delete;patch +// +kubebuilder:rbac:groups="*",resources=events,verbs=create;patch; +// +kubebuilder:rbac:groups="*",resources=secrets,verbs=get;list;watch +// +kubebuilder:rbac:groups="authorino.3scale.net",resources=authconfigs,verbs=create;delete;get;list;patch;update;watch +// +kubebuilder:rbac:groups="authorino.3scale.net",resources=authconfigs,verbs=create;delete;get;list;patch;update;watch +// +kubebuilder:rbac:groups="authorino.3scale.net",resources=authconfigs/status,verbs=get;patch;update +// +kubebuilder:rbac:groups="coordination.k8s.io",resources=leases,verbs=get;list;create;update; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -62,17 +74,10 @@ type AuthorinoReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile func (r *AuthorinoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithValues("authorinoReconciler", req.NamespacedName) - - //get operator namespace - r.OperatorNamespace = authorinooperatorv1beta1.AuthorinoOperatorNamespace - ns, found := os.LookupEnv("WATCH_NAMESPACE") - if found { - r.OperatorNamespace = ns - } + log := r.Log.WithValues("authorino", req.NamespacedName) // get authorino instance - authorinoInstance, err := r.authorinoInstance(req.NamespacedName) + authorinoInstance, err := r.getAuthorinoInstance(req.NamespacedName) if err != nil { return ctrl.Result{}, err } @@ -82,54 +87,75 @@ func (r *AuthorinoReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, nil } - log.Info("Found an instance of authorino", "authorinoInstanceName", authorinoInstance.Name) + log.V(1).Info("Found an instance of authorino", "authorinoInstanceName", authorinoInstance.Name) - err = r.authorinoServices(authorinoInstance) + err = r.createOrUpdateAuthorinoServices(authorinoInstance) if err != nil { + authorinoInstance.Status.Ready = false + authorinoInstance.Status.LastError = err.Error() + if statusErr := r.handleStatusUpdate(authorinoInstance); statusErr != nil { + log.Error(statusErr, statusErr.Error()) + } log.Error(err, "Failed to create authorino services") return ctrl.Result{}, err } - err = r.authorinoPermission(authorinoInstance, req.NamespacedName.Namespace) + err = r.createAuthorinoPermission(authorinoInstance, req.NamespacedName.Namespace) if err != nil { + authorinoInstance.Status.Ready = false + authorinoInstance.Status.LastError = err.Error() + if statusErr := r.handleStatusUpdate(authorinoInstance); statusErr != nil { + log.Error(statusErr, statusErr.Error()) + } log.Error(err, "Failed to create authorino permission") return ctrl.Result{}, err } - existingDeployment := &appsv1.Deployment{} - err = r.Get(context.TODO(), - types.NamespacedName{ - Name: authorinoInstance.GetName(), - Namespace: authorinoInstance.GetNamespace(), - }, existingDeployment) - if err != nil && errors.IsNotFound(err) { - newDeployment := r.authorinoDeployment(authorinoInstance) + existingDeployment, err := r.getAuthorinoDeployment(authorinoInstance) + if err != nil { + authorinoInstance.Status.Ready = false + authorinoInstance.Status.LastError = err.Error() + if statusErr := r.handleStatusUpdate(authorinoInstance); statusErr != nil { + log.Error(statusErr, statusErr.Error()) + } + log.Error(err, "Failed to get Deployment for Authorino") + return ctrl.Result{}, err + } else if existingDeployment == nil { + newDeployment := r.buildAuthorinoDeployment(authorinoInstance) err = r.Create(ctx, newDeployment) if err != nil { + authorinoInstance.Status.Ready = false + authorinoInstance.Status.LastError = err.Error() + if statusErr := r.handleStatusUpdate(authorinoInstance); statusErr != nil { + log.Error(statusErr, statusErr.Error()) + } log.Error(err, "Failed to create Authorino deployment resource", newDeployment.Name, newDeployment.Namespace) return ctrl.Result{}, err } // Deployment created successfully - return and requeue return ctrl.Result{Requeue: true}, nil - } else if err != nil { - log.Error(err, "Failed to get Deployment for Authorino") - return ctrl.Result{}, err - } - - // checks for upgrades - desiredDeployment := r.authorinoDeployment(authorinoInstance) - if r.authorinoDeploymentChanges(existingDeployment, desiredDeployment) { - err = r.Update(ctx, desiredDeployment) - if err != nil { - log.Error(err, "Failed to update Authorino deployment resource", desiredDeployment.Name, desiredDeployment.Namespace) - return ctrl.Result{}, err + } else { + desiredDeployment := r.buildAuthorinoDeployment(authorinoInstance) + if r.authorinoDeploymentChanges(existingDeployment, desiredDeployment) { + err = r.Update(ctx, desiredDeployment) + if err != nil { + authorinoInstance.Status.Ready = false + authorinoInstance.Status.LastError = err.Error() + if statusErr := r.handleStatusUpdate(authorinoInstance); statusErr != nil { + log.Error(statusErr, statusErr.Error()) + } + log.Error(err, "Failed to update Authorino deployment resource", desiredDeployment.Name, desiredDeployment.Namespace) + return ctrl.Result{}, err + } + return ctrl.Result{RequeueAfter: time.Minute}, nil } - return ctrl.Result{RequeueAfter: time.Minute}, nil } - //TODO: handle deletiton - - //TODO: update status + authorinoInstance.Status.Ready = true + authorinoInstance.Status.LastError = "" + if statusErr := r.handleStatusUpdate(authorinoInstance); statusErr != nil { + log.Error(statusErr, statusErr.Error()) + } return ctrl.Result{RequeueAfter: time.Minute}, nil } @@ -137,12 +163,12 @@ func (r *AuthorinoReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // SetupWithManager sets up the controller with the Manager. func (r *AuthorinoReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&authorinooperatorv1beta1.Authorino{}). + For(&api.Authorino{}). Complete(r) } -func (r *AuthorinoReconciler) authorinoInstance(namespacedName types.NamespacedName) (*authorinooperatorv1beta1.Authorino, error) { - authorinoInstance := &authorinooperatorv1beta1.Authorino{} +func (r *AuthorinoReconciler) getAuthorinoInstance(namespacedName types.NamespacedName) (*api.Authorino, error) { + authorinoInstance := &api.Authorino{} err := r.Get(context.TODO(), namespacedName, authorinoInstance) if err != nil { if errors.IsNotFound(err) { @@ -154,7 +180,24 @@ func (r *AuthorinoReconciler) authorinoInstance(namespacedName types.NamespacedN return authorinoInstance, nil } -func (r *AuthorinoReconciler) authorinoDeployment(authorino *authorinooperatorv1beta1.Authorino) *appsv1.Deployment { +func (r *AuthorinoReconciler) getAuthorinoDeployment(authorino *api.Authorino) (*k8sapps.Deployment, error) { + authorinoDeployment := &k8sapps.Deployment{} + err := r.Get(context.TODO(), + types.NamespacedName{ + Name: authorino.GetName(), + Namespace: authorino.GetNamespace(), + }, authorinoDeployment) + if err != nil { + if errors.IsNotFound(err) { + r.Log.Info("Authorino deployment not found.") + return nil, nil + } + return nil, err + } + return authorinoDeployment, nil +} + +func (r *AuthorinoReconciler) buildAuthorinoDeployment(authorino *api.Authorino) *k8sapps.Deployment { prefix := authorino.GetName() objectMeta := v1.ObjectMeta{ @@ -164,24 +207,24 @@ func (r *AuthorinoReconciler) authorinoDeployment(authorino *authorinooperatorv1 labels := labelsForAuthorino(objectMeta.GetName()) - dep := &appsv1.Deployment{ + dep := &k8sapps.Deployment{ ObjectMeta: objectMeta, - Spec: appsv1.DeploymentSpec{ + Spec: k8sapps.DeploymentSpec{ Replicas: authorino.Spec.Replicas, Selector: &v1.LabelSelector{ MatchLabels: labels, }, - Template: corev1.PodTemplateSpec{ + Template: k8score.PodTemplateSpec{ ObjectMeta: v1.ObjectMeta{ Labels: labels, }, - Spec: corev1.PodSpec{ + Spec: k8score.PodSpec{ ServiceAccountName: prefix + "-authorino", - Containers: []corev1.Container{ + Containers: []k8score.Container{ { Image: authorino.Spec.Image, - ImagePullPolicy: corev1.PullPolicy(authorino.Spec.ImagePullPolicy), - Name: authorinooperatorv1beta1.AuthorinoContainerName, + ImagePullPolicy: k8score.PullPolicy(authorino.Spec.ImagePullPolicy), + Name: api.AuthorinoContainerName, Env: r.buildAuthorinoEnv(authorino), }, }, @@ -195,66 +238,66 @@ func (r *AuthorinoReconciler) authorinoDeployment(authorino *authorinooperatorv1 return dep } -func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *authorinooperatorv1beta1.Authorino) []corev1.EnvVar { - envVar := []corev1.EnvVar{} +func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *api.Authorino) []k8score.EnvVar { + envVar := []k8score.EnvVar{} if !authorino.Spec.ClusterWide { - envVar = append(envVar, corev1.EnvVar{ - Name: "WATCH_NAMESPACE", + envVar = append(envVar, k8score.EnvVar{ + Name: api.WatchNamespace, Value: authorino.GetNamespace(), }) } if authorino.Spec.AuthConfigLabelSelectors != "" { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.AuthConfigLabelSelector, + envVar = append(envVar, k8score.EnvVar{ + Name: api.AuthConfigLabelSelector, Value: fmt.Sprint(authorino.Spec.AuthConfigLabelSelectors), }) } if authorino.Spec.SecretLabelSelectors != "" { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.SecretLabelSelector, + envVar = append(envVar, k8score.EnvVar{ + Name: api.SecretLabelSelector, Value: fmt.Sprint(authorino.Spec.SecretLabelSelectors), }) } // external auth service via GRPC if authorino.Spec.Listener.Port != nil { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.ExtAuthGRPCPort, + envVar = append(envVar, k8score.EnvVar{ + Name: api.ExtAuthGRPCPort, Value: fmt.Sprint(authorino.Spec.Listener.Port), }) } if authorino.Spec.Listener.CertPath != "" { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.TSLCertPath, + envVar = append(envVar, k8score.EnvVar{ + Name: api.TLSCertPath, Value: authorino.Spec.Listener.CertPath, }) } if authorino.Spec.Listener.CertKeyPath != "" { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.TSLCertKeyPath, + envVar = append(envVar, k8score.EnvVar{ + Name: api.TLSCertKeyPath, Value: authorino.Spec.Listener.CertKeyPath, }) } // OIDC service if authorino.Spec.OIDCServer.Port != nil { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.OIDCHTTPPort, + envVar = append(envVar, k8score.EnvVar{ + Name: api.OIDCHTTPPort, Value: fmt.Sprint(authorino.Spec.OIDCServer.Port), }) } if authorino.Spec.OIDCServer.CertKeyPath != "" { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.OIDCTSLCertPath, + envVar = append(envVar, k8score.EnvVar{ + Name: api.OIDCTLSCertPath, Value: authorino.Spec.OIDCServer.CertPath, }) } if authorino.Spec.OIDCServer.CertKeyPath != "" { - envVar = append(envVar, corev1.EnvVar{ - Name: authorinooperatorv1beta1.OIDCTLSCertKeyPath, + envVar = append(envVar, k8score.EnvVar{ + Name: api.OIDCTLSCertKeyPath, Value: authorino.Spec.OIDCServer.CertKeyPath, }) } @@ -262,9 +305,13 @@ func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *authorinooperatorv1be return envVar } -func (r *AuthorinoReconciler) authorinoDeploymentChanges(existingDeployment, desiredDeployment *appsv1.Deployment) bool { +func (r *AuthorinoReconciler) authorinoDeploymentChanges(existingDeployment, desiredDeployment *k8sapps.Deployment) bool { changed := false + if existingDeployment.Spec.Replicas != desiredDeployment.Spec.Replicas { + changed = true + } + if len(desiredDeployment.Spec.Template.Spec.Containers) != 1 { // error } @@ -292,15 +339,14 @@ func (r *AuthorinoReconciler) authorinoDeploymentChanges(existingDeployment, des return changed } -func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1beta1.Authorino, operatorNamespace string) error { +func (r *AuthorinoReconciler) createAuthorinoPermission(authorino *api.Authorino, operatorNamespace string) error { prefix := authorino.GetName() authorinoInstanceNamespace := authorino.GetNamespace() - authorinoClusterRoleName := "authorino-operator-authorino-manager-role" + authorinoClusterRoleName := "authorino-manager-role" - authorinoClusterRole := &rbacv1.ClusterRole{ + authorinoClusterRole := &k8srbac.ClusterRole{ ObjectMeta: v1.ObjectMeta{ - Name: authorinoClusterRoleName, - Namespace: r.OperatorNamespace, + Name: authorinoClusterRoleName, }, } err := r.Get(context.TODO(), client.ObjectKeyFromObject(authorinoClusterRole), authorinoClusterRole) @@ -312,7 +358,7 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 } roleName := prefix + "-authorino" - serviceAccount := &corev1.ServiceAccount{ + serviceAccount := &k8score.ServiceAccount{ ObjectMeta: v1.ObjectMeta{ Name: roleName, Namespace: authorinoInstanceNamespace, @@ -337,7 +383,7 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 } if authorino.Spec.ClusterWide { - clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + clusterRoleBinding := &k8srbac.ClusterRoleBinding{ ObjectMeta: v1.ObjectMeta{ Name: roleName, Namespace: authorinoInstanceNamespace, @@ -354,13 +400,13 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 ) } if errors.IsNotFound(err) { - clusterRoleBinding.RoleRef = rbacv1.RoleRef{ + clusterRoleBinding.RoleRef = k8srbac.RoleRef{ Name: authorinoClusterRole.GetName(), Kind: "ClusterRole", } - clusterRoleBinding.Subjects = []rbacv1.Subject{ + clusterRoleBinding.Subjects = []k8srbac.Subject{ { - Kind: rbacv1.ServiceAccountKind, + Kind: k8srbac.ServiceAccountKind, Name: serviceAccount.GetName(), Namespace: authorinoInstanceNamespace, }, @@ -377,7 +423,7 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 } } } else { - roleBinding := &rbacv1.RoleBinding{ + roleBinding := &k8srbac.RoleBinding{ ObjectMeta: v1.ObjectMeta{ Name: roleName, Namespace: authorinoInstanceNamespace, @@ -396,13 +442,13 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 } if errors.IsNotFound(err) { - roleBinding.RoleRef = rbacv1.RoleRef{ + roleBinding.RoleRef = k8srbac.RoleRef{ Name: authorinoClusterRole.GetName(), Kind: "ClusterRole", } - roleBinding.Subjects = []rbacv1.Subject{ + roleBinding.Subjects = []k8srbac.Subject{ { - Kind: rbacv1.ServiceAccountKind, + Kind: k8srbac.ServiceAccountKind, Name: serviceAccount.GetName(), Namespace: authorinoInstanceNamespace, }, @@ -423,8 +469,8 @@ func (r *AuthorinoReconciler) authorinoPermission(authorino *authorinooperatorv1 return nil } -func (r *AuthorinoReconciler) leaderElectionPermission(authorino *authorinooperatorv1beta1.Authorino, saName string) error { - leaderElectionRole := &rbacv1.Role{ +func (r *AuthorinoReconciler) leaderElectionPermission(authorino *api.Authorino, saName string) error { + leaderElectionRole := &k8srbac.Role{ ObjectMeta: v1.ObjectMeta{ Name: "authorino-leader-election-role", Namespace: authorino.GetNamespace(), @@ -453,7 +499,7 @@ func (r *AuthorinoReconciler) leaderElectionPermission(authorino *authorinoopera } prefix := authorino.GetName() - roleBinding := &rbacv1.RoleBinding{ + roleBinding := &k8srbac.RoleBinding{ ObjectMeta: v1.ObjectMeta{ Name: prefix + "-authorino-leader-election", Namespace: authorino.GetNamespace(), @@ -470,13 +516,13 @@ func (r *AuthorinoReconciler) leaderElectionPermission(authorino *authorinoopera } if errors.IsNotFound(err) { - roleBinding.RoleRef = rbacv1.RoleRef{ - Name: roleBinding.GetName(), + roleBinding.RoleRef = k8srbac.RoleRef{ + Name: leaderElectionRole.GetName(), Kind: "Role", } - roleBinding.Subjects = []rbacv1.Subject{ + roleBinding.Subjects = []k8srbac.Subject{ { - Kind: rbacv1.ServiceAccountKind, + Kind: k8srbac.ServiceAccountKind, Name: saName, Namespace: authorino.GetNamespace(), }, @@ -493,8 +539,8 @@ func (r *AuthorinoReconciler) leaderElectionPermission(authorino *authorinoopera return nil } -func getLeaderElectionRules() []rbacv1.PolicyRule { - return []rbacv1.PolicyRule{ +func getLeaderElectionRules() []k8srbac.PolicyRule { + return []k8srbac.PolicyRule{ { APIGroups: []string{"*"}, Resources: []string{"configmaps"}, @@ -518,24 +564,24 @@ func getLeaderElectionRules() []rbacv1.PolicyRule { } } -func (r *AuthorinoReconciler) authorinoServices(authorino *authorinooperatorv1beta1.Authorino) error { +func (r *AuthorinoReconciler) createOrUpdateAuthorinoServices(authorino *api.Authorino) error { - services := make(map[string][]corev1.ServicePort) - services["authorino-authorization"] = []corev1.ServicePort{ + services := make(map[string][]k8score.ServicePort) + services["authorino-authorization"] = []k8score.ServicePort{ { Name: "grpc", Port: 50051, - Protocol: corev1.ProtocolTCP, + Protocol: k8score.ProtocolTCP, }, } - services["authorino-oidc"] = []corev1.ServicePort{ + services["authorino-oidc"] = []k8score.ServicePort{ { Name: "http", Port: 8083, - Protocol: corev1.ProtocolTCP, + Protocol: k8score.ProtocolTCP, }, } - services["authorino-controller-manager-metrics-service"] = []corev1.ServicePort{ + services["controller-metrics"] = []k8score.ServicePort{ { Name: "https", Port: 8443, @@ -544,7 +590,7 @@ func (r *AuthorinoReconciler) authorinoServices(authorino *authorinooperatorv1be } for name, service := range services { - obj := &corev1.Service{ + obj := &k8score.Service{ ObjectMeta: v1.ObjectMeta{ Name: authorino.GetName() + "-" + name, Namespace: authorino.GetNamespace(), @@ -566,9 +612,24 @@ func (r *AuthorinoReconciler) authorinoServices(authorino *authorinooperatorv1be return nil } +func (r *AuthorinoReconciler) handleStatusUpdate(authorino *api.Authorino) error { + // get authorino instance + existingAuthorinoInstance, err := r.getAuthorinoInstance(types.NamespacedName{Name: authorino.GetName(), Namespace: authorino.GetNamespace()}) + if err != nil { + return err + } + existingAuthorinoInstance.Status.Ready = authorino.Status.Ready + + err = r.Status().Update(context.TODO(), existingAuthorinoInstance) + if err != nil { + return fmt.Errorf("Failed to update authorino's %s status , err: %w", authorino.GetName(), err) + } + return nil +} + func labelsForAuthorino(name string) map[string]string { return map[string]string{ - "control-plane": "controller-manager", - "authorino_cr_name": name, + "control-plane": "controller-manager", + "authorino-resource": name, } } diff --git a/go.mod b/go.mod index 7e8ffd76..0614ad2e 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,6 @@ module github.com/kuadrant/authorino-operator go 1.16 require ( -<<<<<<< HEAD - k8s.io/apimachinery v0.20.2 - k8s.io/client-go v0.20.2 - sigs.k8s.io/controller-runtime v0.8.3 -======= github.com/go-logr/logr v0.4.0 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.13.0 @@ -15,5 +10,4 @@ require ( k8s.io/apimachinery v0.21.2 k8s.io/client-go v0.21.2 sigs.k8s.io/controller-runtime v0.9.2 ->>>>>>> 2620723 (Adds reconcilation of Authorino instances) ) diff --git a/main.go b/main.go index 146d7ecb..cf04273b 100644 --- a/main.go +++ b/main.go @@ -71,7 +71,7 @@ func main() { Port: 9443, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, - LeaderElectionID: "aac3a15d.kuadrant.3scale.net", + LeaderElectionID: "aac3a15d.authorino.kuadrant.io", }) if err != nil { setupLog.Error(err, "unable to start manager") From 10c839a084d599bef119385bf9da9cf118a967cb Mon Sep 17 00:00:00 2001 From: jjaferson Date: Fri, 12 Nov 2021 17:06:51 +0000 Subject: [PATCH 5/6] Adds support for tls certs in authorino --- Makefile | 2 +- api/v1beta1/authorino_types.go | 44 ++++--- api/v1beta1/zz_generated.deepcopy.go | 22 ++++ ...ator.authorino.kuadrant.io_authorinos.yaml | 28 ++-- config/manager/manager.yaml | 1 + .../authorino-operator_v1beta1_authorino.yaml | 13 +- controllers/authorino_controller.go | 124 ++++++++++++++---- 7 files changed, 171 insertions(+), 63 deletions(-) diff --git a/Makefile b/Makefile index 0cf65380..2749e966 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,7 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - -AUTHORINO_VERSION=v0.4.0 +AUTHORINO_VERSION=v0.5.0 install-authorino: kustomize ## install RBAC and CRD for authorino $(eval TMP := $(shell mktemp -d)) cd $(TMP); git clone --depth 1 --branch $(AUTHORINO_VERSION) https://github.com/kuadrant/authorino.git diff --git a/api/v1beta1/authorino_types.go b/api/v1beta1/authorino_types.go index b8b1be19..29dbde3c 100644 --- a/api/v1beta1/authorino_types.go +++ b/api/v1beta1/authorino_types.go @@ -17,7 +17,7 @@ limitations under the License. package v1beta1 import ( - apiv1 "k8s.io/api/core/v1" + k8score "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -32,22 +32,28 @@ const ( AuthorinoContainerName string = "authorino" // Authorino EnvVars - WatchNamespace string = "WATCH_NAMESPACE" - ExtAuthGRPCPort string = "EXT_AUTH_GRPC_PORT" - TLSCertPath string = "TLS_CERT" - TLSCertKeyPath string = "TLS_CERT_KEY" - OIDCHTTPPort string = "OIDC_HTTP_PORT" - OIDCTLSCertPath string = "OIDC_TLS_CERT" - OIDCTLSCertKeyPath string = "OIDC_TLS_CERT_KEY" - AuthConfigLabelSelector string = "AUTH_CONFIG_LABEL_SELECTOR" - SecretLabelSelector string = "SECRET_LABEL_SELECTOR" + WatchNamespace string = "WATCH_NAMESPACE" + ExtAuthGRPCPort string = "EXT_AUTH_GRPC_PORT" + EnvVarTlsCert string = "TLS_CERT" + EnvVarTlsCertKey string = "TLS_CERT_KEY" + OIDCHTTPPort string = "OIDC_HTTP_PORT" + EnvVarOidcTlsCertPath string = "OIDC_TLS_CERT" + EnvVarOidcTlsCertKeyPath string = "OIDC_TLS_CERT_KEY" + AuthConfigLabelSelector string = "AUTH_CONFIG_LABEL_SELECTOR" + SecretLabelSelector string = "SECRET_LABEL_SELECTOR" + + // Authorino TLS file paths + DefaultTlsCertPath string = "/etc/ssl/certs/tls.crt" + DefaultTlsCertKeyPath string = "/etc/ssl/private/tls.key" + DefaultOidcTlsCertPath string = "/etc/ssl/certs/oidc.crt" + DefaultOidcTlsCertKeyPath string = "/etc/ssl/private/oidc.key" ) type Condition struct { // Type of condition Type ConditionType `json:"type"` // Status of the condition, one of True, False, Unknown. - Status apiv1.ConditionStatus `json:"status"` + Status k8score.ConditionStatus `json:"status"` // Last time the condition transit from one status to another. // +optional LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` @@ -78,16 +84,18 @@ type AuthorinoSpec struct { } type Listener struct { - Port *int32 `json:"port,omitempty"` - Tls bool `json:"tsl,omitempty"` - CertPath string `json:"certPath,omitempty"` - CertKeyPath string `json:"certKeyPath,omitempty"` + Port *int32 `json:"port,omitempty"` + Tls Tls `json:"tls,omitempty"` } type OIDCServer struct { - Port *int32 `json:"port,omitempty"` - CertPath string `json:"certPath,omitempty"` - CertKeyPath string `json:"certKeyPath,omitempty"` + Port *int32 `json:"port,omitempty"` + Tls Tls `json:"tls,omitempty"` +} + +type Tls struct { + Enabled *bool `json:"enabled,omitempty"` + CertSecretName string `json:"certSecretName"` } // AuthorinoStatus defines the observed state of Authorino diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index d7e9cd18..b766cc54 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -156,6 +156,7 @@ func (in *Listener) DeepCopyInto(out *Listener) { *out = new(int32) **out = **in } + in.Tls.DeepCopyInto(&out.Tls) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Listener. @@ -176,6 +177,7 @@ func (in *OIDCServer) DeepCopyInto(out *OIDCServer) { *out = new(int32) **out = **in } + in.Tls.DeepCopyInto(&out.Tls) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCServer. @@ -187,3 +189,23 @@ func (in *OIDCServer) DeepCopy() *OIDCServer { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Tls) DeepCopyInto(out *Tls) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tls. +func (in *Tls) DeepCopy() *Tls { + if in == nil { + return nil + } + out := new(Tls) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml b/config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml index 3e3863e8..f6fe94f8 100644 --- a/config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml +++ b/config/crd/bases/operator.authorino.kuadrant.io_authorinos.yaml @@ -46,25 +46,33 @@ spec: type: string listener: properties: - certKeyPath: - type: string - certPath: - type: string port: format: int32 type: integer - tsl: - type: boolean + tls: + properties: + certSecretName: + type: string + enabled: + type: boolean + required: + - certSecretName + type: object type: object oidcServer: properties: - certKeyPath: - type: string - certPath: - type: string port: format: int32 type: integer + tls: + properties: + certSecretName: + type: string + enabled: + type: boolean + required: + - certSecretName + type: object type: object replicas: format: int32 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 20262442..db096284 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -30,6 +30,7 @@ spec: args: - --leader-elect image: controller:latest + imagePullPolicy: IfNotPresent name: manager securityContext: allowPrivilegeEscalation: false diff --git a/config/samples/authorino-operator_v1beta1_authorino.yaml b/config/samples/authorino-operator_v1beta1_authorino.yaml index 967c2553..7193955d 100644 --- a/config/samples/authorino-operator_v1beta1_authorino.yaml +++ b/config/samples/authorino-operator_v1beta1_authorino.yaml @@ -10,10 +10,11 @@ spec: clusterWide: true listener: port: - tsl: true - certPath: "" - certKeyPath: "" + # tls: + # enabled: true # default + # certSecretName: authorino-cert # secret must contain `tls.crt` and `tls.key` entries oidcServer: - port: - certPath: "" - certKeyPath: "" + port: + # tls: + # enabled: true # default + # certSecretName: authorino-cert # secret must contain `tls.crt` and `tls.key` entries diff --git a/controllers/authorino_controller.go b/controllers/authorino_controller.go index 9b18fd6b..be34341e 100644 --- a/controllers/authorino_controller.go +++ b/controllers/authorino_controller.go @@ -44,6 +44,11 @@ type AuthorinoReconciler struct { Scheme *runtime.Scheme } +const ( + tlsCertName string = "tls-cert" + oidcTlsCertName string = "oidc-cert" +) + //+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos/status,verbs=get;update;patch //+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos/finalizers,verbs=update @@ -220,24 +225,72 @@ func (r *AuthorinoReconciler) buildAuthorinoDeployment(authorino *api.Authorino) }, Spec: k8score.PodSpec{ ServiceAccountName: prefix + "-authorino", - Containers: []k8score.Container{ - { - Image: authorino.Spec.Image, - ImagePullPolicy: k8score.PullPolicy(authorino.Spec.ImagePullPolicy), - Name: api.AuthorinoContainerName, - Env: r.buildAuthorinoEnv(authorino), - }, - }, }, }, }, } + authorinoContainer := k8score.Container{ + Image: authorino.Spec.Image, + ImagePullPolicy: k8score.PullPolicy(authorino.Spec.ImagePullPolicy), + Name: api.AuthorinoContainerName, + Env: r.buildAuthorinoEnv(authorino), + } + + if enabled := authorino.Spec.Listener.Tls.Enabled; enabled == nil || *enabled { + secretName := authorino.Spec.Listener.Tls.CertSecretName + authorinoContainer.VolumeMounts = append(authorinoContainer.VolumeMounts, + buildTlsVolumeMount(tlsCertName, api.DefaultTlsCertPath, api.DefaultTlsCertKeyPath)..., + ) + dep.Spec.Template.Spec.Volumes = append(dep.Spec.Template.Spec.Volumes, + buildTlsVolume(tlsCertName, secretName), + ) + } + + if enabled := authorino.Spec.OIDCServer.Tls.Enabled; enabled == nil || *enabled { + secretName := authorino.Spec.OIDCServer.Tls.CertSecretName + authorinoContainer.VolumeMounts = append(authorinoContainer.VolumeMounts, + buildTlsVolumeMount(oidcTlsCertName, api.DefaultOidcTlsCertPath, api.DefaultOidcTlsCertKeyPath)..., + ) + dep.Spec.Template.Spec.Volumes = append(dep.Spec.Template.Spec.Volumes, + buildTlsVolume(oidcTlsCertName, secretName), + ) + } + dep.Spec.Template.Spec.Containers = append(dep.Spec.Template.Spec.Containers, authorinoContainer) + ctrl.SetControllerReference(authorino, dep, r.Scheme) return dep } +func buildTlsVolume(certName, secretName string) k8score.Volume { + return k8score.Volume{ + Name: certName, + VolumeSource: k8score.VolumeSource{ + Secret: &k8score.SecretVolumeSource{ + SecretName: secretName, + }, + }, + } +} + +func buildTlsVolumeMount(certName, certPath, certKeyPath string) []k8score.VolumeMount { + return []k8score.VolumeMount{ + { + Name: certName, + MountPath: certPath, + SubPath: "tls.crt", + ReadOnly: true, + }, + { + Name: certName, + MountPath: certKeyPath, + SubPath: "tls.key", + ReadOnly: true, + }, + } +} + func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *api.Authorino) []k8score.EnvVar { envVar := []k8score.EnvVar{} @@ -266,19 +319,19 @@ func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *api.Authorino) []k8sc if authorino.Spec.Listener.Port != nil { envVar = append(envVar, k8score.EnvVar{ Name: api.ExtAuthGRPCPort, - Value: fmt.Sprint(authorino.Spec.Listener.Port), + Value: fmt.Sprintf("%v", *authorino.Spec.Listener.Port), }) } - if authorino.Spec.Listener.CertPath != "" { + + if enabled := authorino.Spec.Listener.Tls.Enabled; enabled == nil || *enabled { envVar = append(envVar, k8score.EnvVar{ - Name: api.TLSCertPath, - Value: authorino.Spec.Listener.CertPath, + Name: api.EnvVarTlsCert, + Value: api.DefaultTlsCertPath, }) - } - if authorino.Spec.Listener.CertKeyPath != "" { + envVar = append(envVar, k8score.EnvVar{ - Name: api.TLSCertKeyPath, - Value: authorino.Spec.Listener.CertKeyPath, + Name: api.EnvVarTlsCertKey, + Value: api.DefaultTlsCertKeyPath, }) } @@ -286,19 +339,17 @@ func (r *AuthorinoReconciler) buildAuthorinoEnv(authorino *api.Authorino) []k8sc if authorino.Spec.OIDCServer.Port != nil { envVar = append(envVar, k8score.EnvVar{ Name: api.OIDCHTTPPort, - Value: fmt.Sprint(authorino.Spec.OIDCServer.Port), + Value: fmt.Sprintf("%v", *authorino.Spec.OIDCServer.Port), }) } - if authorino.Spec.OIDCServer.CertKeyPath != "" { + if enabled := authorino.Spec.OIDCServer.Tls.Enabled; enabled == nil || *enabled { envVar = append(envVar, k8score.EnvVar{ - Name: api.OIDCTLSCertPath, - Value: authorino.Spec.OIDCServer.CertPath, + Name: api.EnvVarOidcTlsCertPath, + Value: api.DefaultOidcTlsCertPath, }) - } - if authorino.Spec.OIDCServer.CertKeyPath != "" { envVar = append(envVar, k8score.EnvVar{ - Name: api.OIDCTLSCertKeyPath, - Value: authorino.Spec.OIDCServer.CertKeyPath, + Name: api.EnvVarOidcTlsCertKeyPath, + Value: api.DefaultOidcTlsCertKeyPath, }) } @@ -330,12 +381,29 @@ func (r *AuthorinoReconciler) authorinoDeploymentChanges(existingDeployment, des // checking envvars existingEnvvars := existingContainer.Env desiredEnvvars := desiredContainer.Env - for envIndex, existingEnvvar := range existingEnvvars { - desiredEnvvar := desiredEnvvars[envIndex] - if existingEnvvar.Name == desiredEnvvar.Name && existingEnvvar.Value != desiredEnvvar.Value { - changed = true + for _, desiredEnvvar := range desiredEnvvars { + for _, existingEnvvar := range existingEnvvars { + if existingEnvvar.Name == desiredEnvvar.Name && existingEnvvar.Value != desiredEnvvar.Value { + changed = true + break + } + } + } + + // checking volume + existingVolumes := existingDeployment.Spec.Template.Spec.Volumes + desiredVolumes := desiredDeployment.Spec.Template.Spec.Volumes + for _, desiredVolume := range desiredVolumes { + if desiredVolume.Name == tlsCertName || desiredVolume.Name == oidcTlsCertName { + for _, existingVolume := range existingVolumes { + if existingVolume.Name == tlsCertName || desiredVolume.Name == oidcTlsCertName && existingVolume.VolumeSource.Secret.SecretName != desiredVolume.VolumeSource.Secret.SecretName { + changed = true + break + } + } } } + return changed } From 958fca86533245300e46956e9c15050825fa77c7 Mon Sep 17 00:00:00 2001 From: jjaferson Date: Tue, 16 Nov 2021 16:11:33 +0000 Subject: [PATCH 6/6] Creates manifest file for installation of the operator * Installs authorino CRDs and Clusterroles via manifest --- .github/workflows/build-latest.yaml | 36 + Makefile | 31 +- api/v1beta1/authorino_types.go | 6 +- .../crd/authorino.3scale.net_authconfigs.yaml | 1040 ----------------- config/authorino/crd/kustomization.yaml | 24 - config/authorino/crd/kustomizeconfig.yaml | 17 - .../patches/cainjection_in_authconfigs.yaml | 8 - .../crd/patches/oneof_in_authconfigs.yaml | 57 - .../crd/patches/webhook_in_authconfigs.yaml | 17 - config/authorino/kustomization.yaml | 12 - .../rbac/authconfig_editor_role.yaml | 23 - .../rbac/authconfig_viewer_role.yaml | 20 - config/authorino/rbac/kustomization.yaml | 7 - config/authorino/rbac/role.yaml | 57 - config/install/kustomization.yaml | 10 + config/install/manifests.yaml | 430 +++++++ config/manager/kustomization.yaml | 2 +- config/manager/manager.yaml | 1 - .../authorino-operator_v1beta1_authorino.yaml | 16 +- controllers/authorino_controller.go | 8 +- 20 files changed, 513 insertions(+), 1309 deletions(-) create mode 100644 .github/workflows/build-latest.yaml delete mode 100644 config/authorino/crd/authorino.3scale.net_authconfigs.yaml delete mode 100644 config/authorino/crd/kustomization.yaml delete mode 100644 config/authorino/crd/kustomizeconfig.yaml delete mode 100644 config/authorino/crd/patches/cainjection_in_authconfigs.yaml delete mode 100644 config/authorino/crd/patches/oneof_in_authconfigs.yaml delete mode 100644 config/authorino/crd/patches/webhook_in_authconfigs.yaml delete mode 100644 config/authorino/kustomization.yaml delete mode 100644 config/authorino/rbac/authconfig_editor_role.yaml delete mode 100644 config/authorino/rbac/authconfig_viewer_role.yaml delete mode 100644 config/authorino/rbac/kustomization.yaml delete mode 100644 config/authorino/rbac/role.yaml create mode 100644 config/install/kustomization.yaml create mode 100644 config/install/manifests.yaml diff --git a/.github/workflows/build-latest.yaml b/.github/workflows/build-latest.yaml new file mode 100644 index 00000000..013097ed --- /dev/null +++ b/.github/workflows/build-latest.yaml @@ -0,0 +1,36 @@ +name: Build and push image +on: + push: + branches: + - 'main' + - 'master' + +jobs: + build: + name: Build and push image + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Build Image + id: build-image + uses: redhat-actions/buildah-build@v2 + with: + image: authorino-operator + tags: latest ${{ github.sha }} + dockerfiles: | + ./Dockerfile + + - name: Push To quay.io + id: push-to-quay + uses: redhat-actions/push-to-registry@v2 + with: + image: ${{ steps.build-image.outputs.image }} + tags: ${{ steps.build-image.outputs.tags }} + registry: quay.io/3scale + username: 3scale+authorino_github + password: ${{ secrets.REGISTRY_AUTH_TOKEN }} + + - name: Print Image URL + run: echo "Image pushed to ${{ steps.push-to-quay.outputs.registry-paths }}" \ No newline at end of file diff --git a/Makefile b/Makefile index 2749e966..d49561de 100644 --- a/Makefile +++ b/Makefile @@ -36,12 +36,16 @@ IMAGE_TAG_BASE ?= authorino.kuadrant.io/authorino-operator BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) # Image URL to use all building/pushing image targets -IMG ?= authorino-operator:latest +DEFAULT_OPERATOR_IMAGE = quay.io/3scale/authorino-operator:latest +OPERATOR_IMAGE ?= $(DEFAULT_OPERATOR_IMAGE) + # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.21 +OPERATOR_MANIFESTS ?= $(PROJECT_DIR)/config/install/manifests.yaml + # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) GOBIN=$(shell go env GOPATH)/bin @@ -75,8 +79,8 @@ help: ## Display this help. ##@ Development -manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases +manifests: controller-gen kustomize ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases && $(KUSTOMIZE) build config/install > $(OPERATOR_MANIFESTS) generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." @@ -99,32 +103,31 @@ run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go docker-build: ## Build docker image with the manager. - docker build -t ${IMG} . + docker build -t ${OPERATOR_IMAGE} . docker-push: ## Push docker image with the manager. - docker push ${IMG} + docker push ${OPERATOR_IMAGE} ##@ Deployment install: manifests kustomize install-authorino ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - + kubectl apply -f $(OPERATOR_MANIFESTS) uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl delete -f - + kubectl delete -f $(OPERATOR_MANIFESTS) deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + cd config/manager && $(KUSTOMIZE) edit set image controller=${OPERATOR_IMAGE} $(KUSTOMIZE) build config/default | kubectl apply -f - + # rollback kustomize edit + cd config/manager && $(KUSTOMIZE) edit set image controller=${DEFAULT_OPERATOR_IMAGE} + undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/default | kubectl delete -f - -AUTHORINO_VERSION=v0.5.0 install-authorino: kustomize ## install RBAC and CRD for authorino - $(eval TMP := $(shell mktemp -d)) - cd $(TMP); git clone --depth 1 --branch $(AUTHORINO_VERSION) https://github.com/kuadrant/authorino.git - cd $(TMP)/authorino; $(KUSTOMIZE) build install | kubectl apply -f - - -rm -rf $(TMP) + kubectl apply -f https://raw.githubusercontent.com/Kuadrant/authorino/main/install/manifests.yaml CONTROLLER_GEN = $(shell pwd)/bin/controller-gen controller-gen: ## Download controller-gen locally if necessary. @@ -155,7 +158,7 @@ endef .PHONY: bundle bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. operator-sdk generate kustomize manifests -q - cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) + cd config/manager && $(KUSTOMIZE) edit set image controller=$(OPERATOR_IMAGE) $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) operator-sdk bundle validate ./bundle diff --git a/api/v1beta1/authorino_types.go b/api/v1beta1/authorino_types.go index 29dbde3c..da9c66ba 100644 --- a/api/v1beta1/authorino_types.go +++ b/api/v1beta1/authorino_types.go @@ -47,6 +47,8 @@ const ( DefaultTlsCertKeyPath string = "/etc/ssl/private/tls.key" DefaultOidcTlsCertPath string = "/etc/ssl/certs/oidc.crt" DefaultOidcTlsCertKeyPath string = "/etc/ssl/private/oidc.key" + + AuthorinoVersion string = "latest" ) type Condition struct { @@ -94,8 +96,8 @@ type OIDCServer struct { } type Tls struct { - Enabled *bool `json:"enabled,omitempty"` - CertSecretName string `json:"certSecretName"` + Enabled *bool `json:"enabled,omitempty"` + CertSecret *k8score.LocalObjectReference `json:"certSecretRef,omitempty"` } // AuthorinoStatus defines the observed state of Authorino diff --git a/config/authorino/crd/authorino.3scale.net_authconfigs.yaml b/config/authorino/crd/authorino.3scale.net_authconfigs.yaml deleted file mode 100644 index a7ed747e..00000000 --- a/config/authorino/crd/authorino.3scale.net_authconfigs.yaml +++ /dev/null @@ -1,1040 +0,0 @@ - ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.6.1 - creationTimestamp: null - name: authconfigs.authorino.3scale.net -spec: - group: authorino.3scale.net - names: - kind: AuthConfig - listKind: AuthConfigList - plural: authconfigs - singular: authconfig - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Ready? - jsonPath: .status.ready - name: Ready - type: boolean - - description: Number of trusted identity sources - jsonPath: .status.numIdentitySources - name: Id sources - priority: 2 - type: integer - - description: Number of external metadata sources - jsonPath: .status.numMetadataSources - name: Metadata sources - priority: 2 - type: integer - - description: Number of authorization policies - jsonPath: .status.numAuthorizationPolicies - name: Authz policies - priority: 2 - type: integer - - description: Number of items added to the client response - jsonPath: .status.numResponseItems - name: Response items - priority: 2 - type: integer - - description: Whether issuing Festival Wristbands - jsonPath: .status.festivalWristbandEnabled - name: Wristband - priority: 2 - type: boolean - name: v1beta1 - schema: - openAPIV3Schema: - description: AuthConfig is the schema for Authorino's AuthConfig API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Specifies the desired state of the AuthConfig resource, i.e. - the authencation/authorization scheme to be applied to protect the matching - service hosts. - properties: - authorization: - description: Authorization is the list of authorization policies. - All policies in this list MUST evaluate to "true" for a request - be successful in the authorization phase. - items: - description: 'Authorization policy to be enforced. Apart from "name", - one of the following parameters is required and only one of the - following parameters is allowed: "opa", "json" or "kubernetes".' - properties: - json: - description: JSON pattern matching authorization policy. - properties: - conditions: - description: Conditions that must match for Authorino to - enforce this policy; otherwise, the policy will be skipped. - items: - properties: - operator: - description: 'The binary operator to be applied to - the content fetched from the authorization JSON, - for comparison with "value". Possible values are: - "eq" (equal to), "neq" (not equal to), "incl" (includes; - for arrays), "excl" (excludes; for arrays), "matches" - (regex)' - enum: - - eq - - neq - - incl - - excl - - matches - type: string - selector: - description: Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. - The value is used to fetch content from the input - authorization JSON built by Authorino along the - identity and metadata phases. - type: string - value: - description: The value of reference for the comparison - with the content fetched from the authorization - policy. If used with the "matches" operator, the - value must compile to a valid Golang regex. - type: string - required: - - operator - - selector - - value - type: object - type: array - rules: - description: The rules that must all evaluate to "true" - for the request to be authorized. - items: - properties: - operator: - description: 'The binary operator to be applied to - the content fetched from the authorization JSON, - for comparison with "value". Possible values are: - "eq" (equal to), "neq" (not equal to), "incl" (includes; - for arrays), "excl" (excludes; for arrays), "matches" - (regex)' - enum: - - eq - - neq - - incl - - excl - - matches - type: string - selector: - description: Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. - The value is used to fetch content from the input - authorization JSON built by Authorino along the - identity and metadata phases. - type: string - value: - description: The value of reference for the comparison - with the content fetched from the authorization - policy. If used with the "matches" operator, the - value must compile to a valid Golang regex. - type: string - required: - - operator - - selector - - value - type: object - type: array - type: object - kubernetes: - description: Kubernetes authorization policy based on `SubjectAccessReview` - Path and Verb are inferred from the request. - properties: - conditions: - description: Conditions that must match for Authorino to - enforce this policy; otherwise, the policy will be skipped. - items: - properties: - operator: - description: 'The binary operator to be applied to - the content fetched from the authorization JSON, - for comparison with "value". Possible values are: - "eq" (equal to), "neq" (not equal to), "incl" (includes; - for arrays), "excl" (excludes; for arrays), "matches" - (regex)' - enum: - - eq - - neq - - incl - - excl - - matches - type: string - selector: - description: Any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson. - The value is used to fetch content from the input - authorization JSON built by Authorino along the - identity and metadata phases. - type: string - value: - description: The value of reference for the comparison - with the content fetched from the authorization - policy. If used with the "matches" operator, the - value must compile to a valid Golang regex. - type: string - required: - - operator - - selector - - value - type: object - type: array - groups: - description: Groups to test for. - items: - type: string - type: array - resourceAttributes: - description: Use ResourceAttributes for checking permissions - on Kubernetes resources If omitted, it performs a non-resource - `SubjectAccessReview`, with verb and path inferred from - the request. - properties: - group: - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') - or a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - name: - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') - or a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - namespace: - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') - or a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - resource: - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') - or a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - subresource: - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') - or a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - verb: - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') - or a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - type: object - user: - description: User to test for. If without "Groups", then - is it interpreted as "What if User were not a member of - any groups" - properties: - value: - type: string - valueFrom: - properties: - authJSON: - description: 'Selector to fill the value from the - authorization JSON. Any patterns supported by - https://pkg.go.dev/github.com/tidwall/gjson can - be used. The value can be just the pattern with - the path to fetch from the authorization JSON - (e.g. ''context.request.http.host'') or a string - template with variable placeholders that resolve - to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - type: object - required: - - user - type: object - name: - description: Name of the authorization policy. - type: string - opa: - description: Open Policy Agent (OPA) authorization policy. - properties: - externalRegistry: - description: External registry of OPA policies. - properties: - credentials: - description: Defines where client credentials will be - passed in the request to the service. If omitted, - it defaults to client credentials passed in the HTTP - Authorization header and the "Bearer" prefix expected - prepended to the secret value. - properties: - in: - default: authorization_header - description: The location in the request where client - credentials shall be passed on requests authenticating - with this identity source/authentication mode. - enum: - - authorization_header - - custom_header - - query - - cookie - type: string - keySelector: - description: Used in conjunction with the `in` parameter. - When used with `authorization_header`, the value - is the prefix of the client credentials string, - separated by a white-space, in the HTTP Authorization - header (e.g. "Bearer", "Basic"). When used with - `custom_header`, `query` or `cookie`, the value - is the name of the HTTP header, query string parameter - or cookie key, respectively. - type: string - required: - - keySelector - type: object - endpoint: - description: Endpoint of the HTTP external registry. - The endpoint must respond with either plain/text or - application/json content-type. In the latter case, - the JSON returned in the body must include a path - `result.raw`, where the raw Rego policy will be extracted - from. This complies with the specification of the - OPA REST API (https://www.openpolicyagent.org/docs/latest/rest-api/#get-a-policy). - type: string - sharedSecretRef: - description: Reference to a Secret key whose value will - be passed by Authorino in the request. The HTTP service - can use the shared secret to authenticate the origin - of the request. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: The name of the secret in the Authorino's - namespace to select from. - type: string - required: - - key - - name - type: object - type: object - inlineRego: - description: Authorization policy as a Rego language document. - The Rego document must include the "allow" condition, - set by Authorino to "false" by default (i.e. requests - are unauthorized unless changed). The Rego document must - NOT include the "package" declaration in line 1. - type: string - type: object - required: - - name - type: object - type: array - denyWith: - description: Custom denial response codes, statuses and headers to - override default 40x's. - properties: - unauthenticated: - description: Denial status customization when the request is unauthenticated. - properties: - code: - description: HTTP status code to override the default denial - status code. - format: int64 - maximum: 599 - minimum: 300 - type: integer - headers: - description: HTTP response headers to override the default - denial headers. - items: - properties: - name: - description: The name of the claim - type: string - value: - description: Static value of the claim - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: Dynamic value of the claim - properties: - authJSON: - description: 'Selector to fill the value from the - authorization JSON. Any patterns supported by - https://pkg.go.dev/github.com/tidwall/gjson can - be used. The value can be just the pattern with - the path to fetch from the authorization JSON - (e.g. ''context.request.http.host'') or a string - template with variable placeholders that resolve - to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - required: - - name - type: object - type: array - message: - description: HTTP message to override the default denial message. - type: string - type: object - unauthorized: - description: Denial status customization when the request is unauthorized. - properties: - code: - description: HTTP status code to override the default denial - status code. - format: int64 - maximum: 599 - minimum: 300 - type: integer - headers: - description: HTTP response headers to override the default - denial headers. - items: - properties: - name: - description: The name of the claim - type: string - value: - description: Static value of the claim - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: Dynamic value of the claim - properties: - authJSON: - description: 'Selector to fill the value from the - authorization JSON. Any patterns supported by - https://pkg.go.dev/github.com/tidwall/gjson can - be used. The value can be just the pattern with - the path to fetch from the authorization JSON - (e.g. ''context.request.http.host'') or a string - template with variable placeholders that resolve - to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - required: - - name - type: object - type: array - message: - description: HTTP message to override the default denial message. - type: string - type: object - type: object - hosts: - description: The list of public host names of the services protected - by this authentication/authorization scheme. Authorino uses the - requested host to lookup for the corresponding authentication/authorization - configs to enforce. - items: - type: string - type: array - identity: - description: List of identity sources/authentication modes. At least - one config of this list MUST evaluate to a valid identity for a - request to be successful in the identity verification phase. - items: - description: 'The identity source/authentication mode config. Apart - from "name", one of the following parameters is required and only - one of the following parameters is allowed: "oicd", "apiKey" or - "kubernetes".' - properties: - apiKey: - properties: - labelSelectors: - additionalProperties: - type: string - description: The map of label selectors used by Authorino - to match secrets from the cluster storing valid credentials - to authenticate to this service - type: object - required: - - labelSelectors - type: object - credentials: - description: Defines where client credentials are required to - be passed in the request for this identity source/authentication - mode. If omitted, it defaults to client credentials passed - in the HTTP Authorization header and the "Bearer" prefix expected - prepended to the credentials value (token, API key, etc). - properties: - in: - default: authorization_header - description: The location in the request where client credentials - shall be passed on requests authenticating with this identity - source/authentication mode. - enum: - - authorization_header - - custom_header - - query - - cookie - type: string - keySelector: - description: Used in conjunction with the `in` parameter. - When used with `authorization_header`, the value is the - prefix of the client credentials string, separated by - a white-space, in the HTTP Authorization header (e.g. - "Bearer", "Basic"). When used with `custom_header`, `query` - or `cookie`, the value is the name of the HTTP header, - query string parameter or cookie key, respectively. - type: string - required: - - keySelector - type: object - extendedProperties: - description: Extends the resolved identity object with additional - custom properties before appending to the authorization JSON. - It requires the resolved identity object to always be of the - JSON type 'object'. Other JSON types (array, string, etc) - will break. - items: - properties: - name: - description: The name of the claim - type: string - value: - description: Static value of the claim - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: Dynamic value of the claim - properties: - authJSON: - description: 'Selector to fill the value from the - authorization JSON. Any patterns supported by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern with - the path to fetch from the authorization JSON (e.g. - ''context.request.http.host'') or a string template - with variable placeholders that resolve to patterns - (e.g. "Hello, {auth.identity.name}!") The following - string modifiers are available: @extract:{sep:" - ",pos:0}, @replace{old:"",new:""}, @case:upper|lower, - and @base64:encode|decode.' - type: string - type: object - required: - - name - type: object - type: array - kubernetes: - properties: - audiences: - description: The list of audiences (scopes) that must be - claimed in a Kubernetes authentication token supplied - in the request, and reviewed by Authorino. If omitted, - Authorino will review tokens expecting the host name of - the requested protected service amongst the audiences. - items: - type: string - type: array - type: object - name: - description: The name of this identity source/authentication - mode. It usually identifies a source of identities or group - of users/clients of the protected service. It may as well - be used for this identity config to be referred in some metadata - configs. - type: string - oauth2: - properties: - credentialsRef: - description: Reference to a Kubernetes secret in the same - namespace, that stores client credentials to the OAuth2 - server. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - tokenIntrospectionUrl: - description: The full URL of the token introspection endpoint. - type: string - tokenTypeHint: - description: The token type hint for the token introspection. - If omitted, it defaults to "access_token". - type: string - required: - - credentialsRef - - tokenIntrospectionUrl - type: object - oidc: - properties: - endpoint: - description: Endpoint of the OIDC issuer. Authorino will - append to this value the well-known path to the OpenID - Connect discovery endpoint (i.e. "/.well-known/openid-configuration"), - used to automatically discover the OpenID Connect configuration, - whose set of claims is expected to include (among others) - the "jkws_uri" claim. The value must coincide with the - value of the "iss" (issuer) claim of the discovered OpenID - Connect configuration. - type: string - required: - - endpoint - type: object - required: - - name - type: object - type: array - metadata: - description: List of metadata source configs. Authorino fetches JSON - content from sources on this list on every request. - items: - description: 'The metadata config. Apart from "name", one of the - following parameters is required and only one of the following - parameters is allowed: "userInfo" or "uma".' - properties: - http: - description: Generic HTTP interface to obtain authorization - metadata from a HTTP service. - properties: - bodyParameters: - description: Custom parameters to encode in the body of - the HTTP request. Use it with method=POST; for GET requests, - specify parameters using placeholders in the endpoint. - items: - properties: - name: - description: The name of the claim - type: string - value: - description: Static value of the claim - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: Dynamic value of the claim - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') or - a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - required: - - name - type: object - type: array - contentType: - default: application/x-www-form-urlencoded - description: Content-Type of the request body. - enum: - - application/x-www-form-urlencoded - - application/json - type: string - credentials: - description: Defines where client credentials will be passed - in the request to the service. If omitted, it defaults - to client credentials passed in the HTTP Authorization - header and the "Bearer" prefix expected prepended to the - secret value. - properties: - in: - default: authorization_header - description: The location in the request where client - credentials shall be passed on requests authenticating - with this identity source/authentication mode. - enum: - - authorization_header - - custom_header - - query - - cookie - type: string - keySelector: - description: Used in conjunction with the `in` parameter. - When used with `authorization_header`, the value is - the prefix of the client credentials string, separated - by a white-space, in the HTTP Authorization header - (e.g. "Bearer", "Basic"). When used with `custom_header`, - `query` or `cookie`, the value is the name of the - HTTP header, query string parameter or cookie key, - respectively. - type: string - required: - - keySelector - type: object - endpoint: - description: Endpoint of the HTTP service. The endpoint - accepts variable placeholders in the format "{selector}", - where "selector" is any pattern supported by https://pkg.go.dev/github.com/tidwall/gjson - and selects value from the authorization JSON. E.g. https://ext-auth-server.io/metadata?p={context.request.http.path} - type: string - method: - description: 'HTTP verb used in the request to the service. - Accepted values: GET (default), POST. When the request - method is POST, the authorization JSON is passed in the - body of the request.' - enum: - - GET - - POST - type: string - sharedSecretRef: - description: Reference to a Secret key whose value will - be passed by Authorino in the request. The HTTP service - can use the shared secret to authenticate the origin of - the request. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: The name of the secret in the Authorino's - namespace to select from. - type: string - required: - - key - - name - type: object - required: - - endpoint - type: object - name: - description: The name of the metadata source. Policies of te - authorization phase can refer to this metadata by this value. - type: string - uma: - description: User-Managed Access (UMA) source of resource data. - properties: - credentialsRef: - description: Reference to a Kubernetes secret in the same - namespace, that stores client credentials to the resource - registration API of the UMA server. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - endpoint: - description: The endpoint of the UMA server. The value must - coincide with the "issuer" claim of the UMA config discovered - from the well-known uma configuration endpoint. - type: string - required: - - credentialsRef - - endpoint - type: object - userInfo: - description: OpendID Connect UserInfo linked to an OIDC identity - config of this same spec. - properties: - identitySource: - description: The name of an OIDC identity source included - in the "identity" section and whose OpenID Connect configuration - discovered includes the OIDC "userinfo_endpoint" claim. - type: string - required: - - identitySource - type: object - required: - - name - type: object - type: array - response: - description: List of response configs. Authorino gathers data from - the auth pipeline to build custom responses for the client. - items: - description: 'Dynamic response to return to the client. Apart from - "name", one of the following parameters is required and only one - of the following parameters is allowed: "wristband" or "json".' - properties: - json: - properties: - properties: - description: List of JSON property-value pairs to be added - to the dynamic response. - items: - properties: - name: - description: The name of the claim - type: string - value: - description: Static value of the claim - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: Dynamic value of the claim - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') or - a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - required: - - name - type: object - type: array - required: - - properties - type: object - name: - description: Name of the custom response. - type: string - wrapper: - default: httpHeader - description: How Authorino wraps the response. Use "httpHeader" - (default) to wrap the response in an HTTP header; or "envoyDynamicMetadata" - to wrap the response as Envoy Dynamic Metadata - enum: - - httpHeader - - envoyDynamicMetadata - type: string - wrapperKey: - description: The name of key used in the wrapped response (name - of the HTTP header or property of the Envoy Dynamic Metadata - JSON). If omitted, it will be set to the name of the configuration. - type: string - wristband: - properties: - customClaims: - description: Any claims to be added to the wristband token - apart from the standard JWT claims (iss, iat, exp) added - by default. - items: - properties: - name: - description: The name of the claim - type: string - value: - description: Static value of the claim - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: Dynamic value of the claim - properties: - authJSON: - description: 'Selector to fill the value from - the authorization JSON. Any patterns supported - by https://pkg.go.dev/github.com/tidwall/gjson - can be used. The value can be just the pattern - with the path to fetch from the authorization - JSON (e.g. ''context.request.http.host'') or - a string template with variable placeholders - that resolve to patterns (e.g. "Hello, {auth.identity.name}!") - The following string modifiers are available: - @extract:{sep:" ",pos:0}, @replace{old:"",new:""}, - @case:upper|lower, and @base64:encode|decode.' - type: string - type: object - required: - - name - type: object - type: array - issuer: - description: 'The endpoint to the Authorino service that - issues the wristband (format: ://:/, - where = /