diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7233674ba..62333aa9d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -129,6 +129,21 @@ jobs: run: | make verify-fmt + verify-kuadrant-controller-manifests: + name: Verify kuadrant controller manifests + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.16.x + uses: actions/setup-go@v2 + with: + go-version: 1.16.x + id: go + - name: Check out code + uses: actions/checkout@v2 + - name: Run make verify-kuadrant-controller-manifests + run: | + make verify-kuadrant-controller-manifests + test-scripts: name: Test Scripts strategy: diff --git a/Dockerfile b/Dockerfile index 8357a9d34..1b4e3a79c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ COPY pkg/ pkg/ +COPY kuadrantcontrollermanifests/ kuadrantcontrollermanifests/ # Build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go diff --git a/Makefile b/Makefile index b644343cc..10cb09753 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +PROJECT_PATH := $(patsubst %/,%,$(dir $(MKFILE_PATH))) + # VERSION defines the project version for the bundle. # Update this value when you upgrade the version of your project. # To re-generate a bundle for another specific version without changing the standard setup, you can: @@ -125,13 +128,9 @@ manifests: controller-gen dependencies-manifests ## Generate WebhookConfiguratio $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: dependencies-manifests -dependencies-manifests: export KUADRANT_CONTROLLER_GITREF := $(KUADRANT_CONTROLLER_GITREF) dependencies-manifests: export AUTHORINO_OPERATOR_GITREF := $(AUTHORINO_OPERATOR_GITREF) dependencies-manifests: export LIMITADOR_OPERATOR_GITREF := $(LIMITADOR_OPERATOR_GITREF) dependencies-manifests: ## Update kuadrant dependencies manifests. - envsubst \ - < config/dependencies/controller/kustomization.template.yaml \ - > config/dependencies/controller/kustomization.yaml envsubst \ < config/dependencies/authorino/kustomization.template.yaml \ > config/dependencies/authorino/kustomization.yaml @@ -163,7 +162,7 @@ build: generate fmt vet ## Build manager binary. run: export LOG_LEVEL = debug run: export LOG_MODE = development -run: manifests generate fmt vet ## Run a controller from your host. +run: generate fmt vet ## Run a controller from your host. go run ./main.go docker-build: ## Build docker image with the manager. @@ -183,7 +182,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/deploy | kubectl apply -f - - ${MAKE} post-deploy-hacks + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMAGE_TAG_BASE}:latest undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/deploy | kubectl delete -f - @@ -198,20 +197,10 @@ uninstall-olm: deploy-olm: ## Deploy controller to the K8s cluster specified in ~/.kube/config using OLM catalog image. $(KUSTOMIZE) build config/deploy/olm | kubectl apply -f - - ${MAKE} post-deploy-hacks undeploy-olm: ## Undeploy controller from the K8s cluster specified in ~/.kube/config using OLM catalog image. $(KUSTOMIZE) build config/deploy/olm | kubectl delete -f - -#This target is temporary to aid dev/test of the operator. Eventually it will be the responsibility of the -# operator itself to create/configure these things as part of the reconciliation of a kuadrant CR. -post-deploy-hacks: - # Wait for deployment to complete - timeout 60s bash -c 'until kubectl -n kuadrant-system get deployments/kuadrant-operator-controller-manager; do sleep 10; done;' - kubectl -n kuadrant-system wait --timeout=300s --for=condition=Available deployments --all - kubectl apply -f config/dependencies/istio/default-gateway.yaml -n kuadrant-system - kubectl apply -f config/dependencies/authorino/authorino.yaml -n kuadrant-system - 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.7.0) diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 336b39556..e461ad89a 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* diff --git a/bundle/manifests/apim.kuadrant.io_authpolicies.yaml b/bundle/manifests/apim.kuadrant.io_authpolicies.yaml deleted file mode 100644 index ed6e1ee29..000000000 --- a/bundle/manifests/apim.kuadrant.io_authpolicies.yaml +++ /dev/null @@ -1,317 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app: kuadrant - name: authpolicies.apim.kuadrant.io -spec: - group: apim.kuadrant.io - names: - kind: AuthPolicy - listKind: AuthPolicyList - plural: authpolicies - singular: authpolicy - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - 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: - properties: - policy: - description: Policy per action type but also per provider if using - custom type. - items: - properties: - action: - description: The action to take if the request is matches with - the rules. - enum: - - ALLOW - - CUSTOM - - DENY - - AUDIT - type: string - provider: - default: "" - description: Specifies detailed configuration of the CUSTOM - action. Must be used only with CUSTOM action. - type: string - rules: - description: A list of rules to match the request. A match occurs - when at least one rule matches the request. - items: - description: "Rule matches requests from a list of sources - that perform a list of operations subject to a list of conditions. - A match occurs when at least one source, operation and condition - matches the request. An empty rule is always matched. \n - Any string field in the rule supports Exact, Prefix, Suffix - and Presence match: \n - Exact match: \"abc\" will match - on value \"abc\". - Prefix match: \"abc*\" will match on - value \"abc\" and \"abcd\". - Suffix match: \"*abc\" will - match on value \"abc\" and \"xabc\". - Presence match: \"*\" - will match when value is not empty." - properties: - from: - description: "Optional. from specifies the source of a - request. \n If not set, any source is allowed." - items: - description: From includes a list or sources. - properties: - source: - description: Source specifies the source of a request. - properties: - ip_blocks: - description: "Optional. A list of IP blocks, - which matches to the \"source.ip\" attribute. - Populated from the source address of the IP - packet. Single IP (e.g. \"1.2.3.4\") and CIDR - (e.g. \"1.2.3.0/24\") are supported. \n If - not set, any IP is allowed." - items: - type: string - type: array - namespaces: - description: "Optional. A list of namespaces, - which matches to the \"source.namespace\" - attribute. This field requires mTLS enabled. - \n If not set, any namespace is allowed." - items: - type: string - type: array - not_ip_blocks: - description: Optional. A list of negative match - of IP blocks. - items: - type: string - type: array - not_namespaces: - description: Optional. A list of negative match - of namespaces. - items: - type: string - type: array - not_principals: - description: Optional. A list of negative match - of source peer identities. - items: - type: string - type: array - not_remote_ip_blocks: - description: Optional. A list of negative match - of remote IP blocks. - items: - type: string - type: array - not_request_principals: - description: Optional. A list of negative match - of request identities. - items: - type: string - type: array - principals: - description: "Optional. A list of source peer - identities (i.e. service account), which matches - to the \"source.principal\" attribute. This - field requires mTLS enabled. \n If not set, - any principal is allowed." - items: - type: string - type: array - remote_ip_blocks: - description: "Optional. A list of IP blocks, - which matches to the \"remote.ip\" attribute. - Populated from X-Forwarded-For header or proxy - protocol. To make use of this field, you must - configure the numTrustedProxies field of the - gatewayTopology under the meshConfig when - you install Istio or using an annotation on - the ingress gateway. See the documentation - here: [Configuring Gateway Network Topology](https://istio.io/latest/docs/ops/configuration/traffic-management/network-topologies/). - Single IP (e.g. \"1.2.3.4\") and CIDR (e.g. - \"1.2.3.0/24\") are supported. \n If not set, - any IP is allowed." - items: - type: string - type: array - request_principals: - description: "Optional. A list of request identities - (i.e. \"iss/sub\" claims), which matches to - the \"request.auth.principal\" attribute. - \n If not set, any request principal is allowed." - items: - type: string - type: array - type: object - type: object - type: array - to: - description: "Optional. to specifies the operation of - a request. \n If not set, any operation is allowed." - items: - description: To includes a list or operations. - properties: - operation: - description: Operation specifies the operation of - a request. - properties: - hosts: - description: "Optional. A list of hosts, which - matches to the \"request.host\" attribute. - \n If not set, any host is allowed. Must be - used only with HTTP." - items: - type: string - type: array - methods: - description: "Optional. A list of methods, which - matches to the \"request.method\" attribute. - For gRPC service, this will always be \"POST\". - \n If not set, any method is allowed. Must - be used only with HTTP." - items: - type: string - type: array - not_hosts: - description: Optional. A list of negative match - of hosts. - items: - type: string - type: array - not_methods: - description: Optional. A list of negative match - of methods. - items: - type: string - type: array - not_paths: - description: Optional. A list of negative match - of paths. - items: - type: string - type: array - not_ports: - description: Optional. A list of negative match - of ports. - items: - type: string - type: array - paths: - description: "Optional. A list of paths, which - matches to the \"request.url_path\" attribute. - For gRPC service, this will be the fully-qualified - name in the form of \"/package.service/method\". - \n If not set, any path is allowed. Must be - used only with HTTP." - items: - type: string - type: array - ports: - description: "Optional. A list of ports, which - matches to the \"destination.port\" attribute. - \n If not set, any port is allowed." - items: - type: string - type: array - type: object - type: object - type: array - when: - description: "Optional. when specifies a list of additional - conditions of a request. \n If not set, any condition - is allowed." - items: - description: Condition specifies additional required - attributes. - properties: - key: - description: The name of an Istio attribute. See - the [full list of supported attributes](https://istio.io/docs/reference/config/security/conditions/). - type: string - not_values: - description: 'Optional. A list of negative match - of values for the attribute. Note: at least one - of values or not_values must be set.' - items: - type: string - type: array - values: - description: 'Optional. A list of allowed values - for the attribute. Note: at least one of values - or not_values must be set.' - items: - type: string - type: array - type: object - type: array - type: object - type: array - required: - - action - type: object - type: array - x-kubernetes-list-map-keys: - - action - - provider - x-kubernetes-list-type: map - targetRef: - description: TargetRef identifies an API object to apply policy to. - properties: - group: - description: Group is the group of the target resource. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the target resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the target resource. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace is the namespace of the referent. When - unspecified, the local namespace is inferred. Even when policy - targets a resource in a different namespace, it MUST only apply - to traffic originating from the same namespace as the policy. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - required: - - targetRef - type: object - type: object - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/bundle/manifests/apim.kuadrant.io_ratelimitpolicies.yaml b/bundle/manifests/apim.kuadrant.io_ratelimitpolicies.yaml deleted file mode 100644 index c93b2f4d5..000000000 --- a/bundle/manifests/apim.kuadrant.io_ratelimitpolicies.yaml +++ /dev/null @@ -1,407 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app: kuadrant - name: ratelimitpolicies.apim.kuadrant.io -spec: - group: apim.kuadrant.io - names: - kind: RateLimitPolicy - listKind: RateLimitPolicyList - plural: ratelimitpolicies - singular: ratelimitpolicy - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: RateLimitPolicy is the Schema for the ratelimitpolicies 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: RateLimitPolicySpec defines the desired state of RateLimitPolicy - properties: - domain: - type: string - limits: - items: - description: RateLimitSpec defines the desired state of RateLimit - properties: - conditions: - items: - type: string - type: array - max_value: - type: integer - namespace: - type: string - seconds: - type: integer - variables: - items: - type: string - type: array - required: - - conditions - - max_value - - namespace - - seconds - - variables - type: object - type: array - rateLimits: - items: - properties: - actions: - items: - description: Action_Specifier defines one envoy rate limit - action - oneOf: - - required: - - generic_key - - required: - - metadata - - required: - - remote_address - - required: - - request_headers - properties: - generic_key: - properties: - descriptor_key: - type: string - descriptor_value: - type: string - required: - - descriptor_value - type: object - metadata: - properties: - default_value: - type: string - descriptor_key: - type: string - metadata_key: - properties: - key: - type: string - path: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - required: - - key - - path - type: object - source: - description: MetadataSource https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-enum-config-route-v3-ratelimit-action-metadata-source - enum: - - DYNAMIC - - ROUTE_ENTRY - type: string - required: - - descriptor_key - - metadata_key - type: object - remote_address: - description: RemoteAddressSpec no need to specify descriptor - entry is populated using the trusted address from [x-forwarded-for](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#config-http-conn-man-headers-x-forwarded-for) - type: object - request_headers: - description: RequestHeadersSpec Rate limit on request - headers. - properties: - descriptor_key: - type: string - header_name: - type: string - skip_if_absent: - type: boolean - required: - - descriptor_key - - header_name - type: object - type: object - type: array - stage: - description: 'Definfing phase at which rate limits will be applied. - Valid values are: PREAUTH, POSTAUTH, BOTH' - enum: - - PREAUTH - - POSTAUTH - - BOTH - type: string - required: - - stage - type: object - type: array - rules: - items: - properties: - name: - description: Name supports regex for fetching operations from - routing resources For VirtualService, if route name matches, - all the match requests are converted to operations internally. - But specific match request names are also supported. - type: string - operations: - description: Operation specifies the operations of a request - items: - description: Each operation type has OR semantics and overall - AND semantics for a match. - properties: - methods: - items: - type: string - type: array - paths: - items: - type: string - type: array - type: object - type: array - rateLimits: - items: - properties: - actions: - items: - description: Action_Specifier defines one envoy rate - limit action - oneOf: - - required: - - generic_key - - required: - - metadata - - required: - - remote_address - - required: - - request_headers - properties: - generic_key: - properties: - descriptor_key: - type: string - descriptor_value: - type: string - required: - - descriptor_value - type: object - metadata: - properties: - default_value: - type: string - descriptor_key: - type: string - metadata_key: - properties: - key: - type: string - path: - items: - properties: - key: - type: string - required: - - key - type: object - type: array - required: - - key - - path - type: object - source: - description: MetadataSource https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-enum-config-route-v3-ratelimit-action-metadata-source - enum: - - DYNAMIC - - ROUTE_ENTRY - type: string - required: - - descriptor_key - - metadata_key - type: object - remote_address: - description: RemoteAddressSpec no need to specify - descriptor entry is populated using the trusted - address from [x-forwarded-for](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#config-http-conn-man-headers-x-forwarded-for) - type: object - request_headers: - description: RequestHeadersSpec Rate limit on request - headers. - properties: - descriptor_key: - type: string - header_name: - type: string - skip_if_absent: - type: boolean - required: - - descriptor_key - - header_name - type: object - type: object - type: array - stage: - description: 'Definfing phase at which rate limits will - be applied. Valid values are: PREAUTH, POSTAUTH, BOTH' - enum: - - PREAUTH - - POSTAUTH - - BOTH - type: string - required: - - stage - type: object - type: array - type: object - type: array - targetRef: - description: TargetRef identifies an API object to apply policy to. - properties: - group: - description: Group is the group of the target resource. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the target resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the target resource. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace is the namespace of the referent. When - unspecified, the local namespace is inferred. Even when policy - targets a resource in a different namespace, it MUST only apply - to traffic originating from the same namespace as the policy. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - required: - - domain - - targetRef - type: object - status: - description: RateLimitPolicyStatus defines the observed state of RateLimitPolicy - properties: - conditions: - description: 'Represents the observations of a foo''s current state. - Known .status.conditions.type are: "Available"' - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - observedGeneration: - description: ObservedGeneration reflects the generation of the most - recently observed spec. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/bundle/manifests/kuadrant-controller-manager-metrics-service_v1_service.yaml b/bundle/manifests/kuadrant-controller-manager-metrics-service_v1_service.yaml deleted file mode 100644 index 42862e17d..000000000 --- a/bundle/manifests/kuadrant-controller-manager-metrics-service_v1_service.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - app: kuadrant - control-plane: controller-manager - name: kuadrant-controller-manager-metrics-service -spec: - ports: - - name: metrics - port: 8080 - targetPort: metrics - selector: - app: kuadrant - control-plane: controller-manager -status: - loadBalancer: {} diff --git a/bundle/manifests/kuadrant-manager-config_v1_configmap.yaml b/bundle/manifests/kuadrant-manager-config_v1_configmap.yaml deleted file mode 100644 index 2df37d83f..000000000 --- a/bundle/manifests/kuadrant-manager-config_v1_configmap.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -data: - controller_manager_config.yaml: | - apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 - kind: ControllerManagerConfig - health: - healthProbeBindAddress: :8081 - metrics: - bindAddress: :8080 - webhook: - port: 9443 - leaderElection: - leaderElect: true - resourceName: e358d637.kuadrant.io -kind: ConfigMap -metadata: - labels: - app: kuadrant - name: kuadrant-manager-config diff --git a/bundle/manifests/kuadrant-manager-role_rbac.authorization.k8s.io_v1_role.yaml b/bundle/manifests/kuadrant-manager-role_rbac.authorization.k8s.io_v1_role.yaml deleted file mode 100644 index ab90e572e..000000000 --- a/bundle/manifests/kuadrant-manager-role_rbac.authorization.k8s.io_v1_role.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - creationTimestamp: null - labels: - app: kuadrant - name: kuadrant-manager-role -rules: -- apiGroups: - - route.openshift.io - resources: - - routes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - route.openshift.io - resources: - - routes/custom-host - verbs: - - create -- apiGroups: - - route.openshift.io - resources: - - routes/status - verbs: - - get diff --git a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml index ed4a6e37b..e066f4341 100644 --- a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml +++ b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml @@ -22,17 +22,11 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: - - kind: AuthPolicy - name: authpolicies.apim.kuadrant.io - version: v1alpha1 - description: Kuadrant is the Schema for the kuadrants API displayName: Kuadrant kind: Kuadrant name: kuadrants.kuadrant.kuadrant.io version: v1beta1 - - kind: RateLimitPolicy - name: ratelimitpolicies.apim.kuadrant.io - version: v1alpha1 description: Kuadrant Operator displayName: Kuadrant icon: @@ -43,9 +37,16 @@ spec: clusterPermissions: - rules: - apiGroups: - - apim.kuadrant.io + - "" resources: - - authpolicies + - events + verbs: + - create + - patch + - apiGroups: + - "" + resources: + - leases verbs: - create - delete @@ -55,14 +56,21 @@ spec: - update - watch - apiGroups: - - apim.kuadrant.io + - apiextensions.k8s.io resources: - - authpolicies/finalizers + - customresourcedefinitions verbs: + - create + - delete + - get + - list + - patch - update + - watch - apiGroups: - apim.kuadrant.io resources: + - authpolicies - ratelimitpolicies verbs: - create @@ -75,39 +83,48 @@ spec: - apiGroups: - apim.kuadrant.io resources: - - ratelimitpolicies/finalizers + - authpolicies/finalizers verbs: - update - apiGroups: - apim.kuadrant.io resources: - - ratelimitpolicies/status + - authpolicies/status verbs: - get - patch - update - apiGroups: - - gateway.networking.k8s.io + - apim.kuadrant.io resources: - - gateways + - ratelimitpolicies/finalizers + verbs: + - update + - apiGroups: + - apim.kuadrant.io + resources: + - ratelimitpolicies/status verbs: - get - - list - - watch + - patch + - update - apiGroups: - - gateway.networking.k8s.io + - apps resources: - - httproutes + - deployments verbs: + - create + - delete - get - list - patch - update - watch - apiGroups: - - limitador.kuadrant.io + - coordination.k8s.io resources: - - ratelimits + - configmaps + - leases verbs: - create - delete @@ -117,9 +134,11 @@ spec: - update - watch - apiGroups: - - networking.istio.io + - "" resources: - - envoyfilters + - configmaps + - serviceaccounts + - services verbs: - create - delete @@ -129,7 +148,7 @@ spec: - update - watch - apiGroups: - - networking.istio.io + - gateway.networking.k8s.io resources: - gateways verbs: @@ -137,19 +156,15 @@ spec: - list - watch - apiGroups: - - security.istio.io + - gateway.networking.k8s.io resources: - - authorizationpolicies + - httproutes verbs: - - create - - delete - get - list - patch - update - watch - serviceAccountName: kuadrant-controller-manager - - rules: - apiGroups: - install.istio.io resources: @@ -187,6 +202,89 @@ spec: - get - patch - update + - apiGroups: + - limitador.kuadrant.io + resources: + - limitadors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - limitador.kuadrant.io + resources: + - ratelimits + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.istio.io + resources: + - envoyfilters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.istio.io + resources: + - gateways + verbs: + - get + - list + - watch + - apiGroups: + - operator.authorino.kuadrant.io + resources: + - authorinos + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - security.istio.io + resources: + - authorizationpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - authentication.k8s.io resources: @@ -201,63 +299,6 @@ spec: - create serviceAccountName: kuadrant-operator-controller-manager deployments: - - name: kuadrant-controller-manager - spec: - replicas: 1 - selector: - matchLabels: - app: kuadrant - control-plane: controller-manager - strategy: {} - template: - metadata: - labels: - app: kuadrant - control-plane: controller-manager - spec: - containers: - - args: - - --config=controller_manager_config.yaml - command: - - /manager - image: quay.io/kuadrant/kuadrant-controller:latest - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - ports: - - containerPort: 8080 - name: metrics - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 100m - memory: 30Mi - requests: - cpu: 100m - memory: 20Mi - securityContext: - allowPrivilegeEscalation: false - volumeMounts: - - mountPath: /controller_manager_config.yaml - name: manager-config - subPath: controller_manager_config.yaml - securityContext: - runAsNonRoot: true - serviceAccountName: kuadrant-controller-manager - terminationGracePeriodSeconds: 10 - volumes: - - configMap: - name: kuadrant-manager-config - name: manager-config - name: kuadrant-operator-controller-manager spec: replicas: 1 @@ -317,29 +358,6 @@ spec: serviceAccountName: kuadrant-operator-controller-manager terminationGracePeriodSeconds: 10 permissions: - - rules: - - apiGroups: - - "" - - coordination.k8s.io - resources: - - configmaps - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - serviceAccountName: kuadrant-controller-manager - rules: - apiGroups: - "" diff --git a/config/dependencies/authorino/authorino.yaml b/config/dependencies/authorino/authorino.yaml deleted file mode 100644 index 49bf82e04..000000000 --- a/config/dependencies/authorino/authorino.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: operator.authorino.kuadrant.io/v1beta1 -kind: Authorino -metadata: - name: authorino - namespace: kuadrant-system -spec: - replicas: 1 - clusterWide: false - listener: - tls: - enabled: false - oidcServer: - tls: - enabled: false diff --git a/config/dependencies/controller/kustomization.template.yaml b/config/dependencies/controller/kustomization.template.yaml deleted file mode 100644 index 749dab8ce..000000000 --- a/config/dependencies/controller/kustomization.template.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: -- github.com/Kuadrant/kuadrant-controller/config/default?ref=${KUADRANT_CONTROLLER_GITREF} - -patchesStrategicMerge: - - delete-ns.yaml diff --git a/config/dependencies/controller/kustomization.yaml b/config/dependencies/controller/kustomization.yaml deleted file mode 100644 index 678c7601a..000000000 --- a/config/dependencies/controller/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: -- github.com/Kuadrant/kuadrant-controller/config/default?ref=main - -patchesStrategicMerge: - - delete-ns.yaml diff --git a/config/dependencies/gateway-api/gateway/gateway.yaml b/config/dependencies/gateway-api/gateway/gateway.yaml new file mode 100644 index 000000000..0f686be97 --- /dev/null +++ b/config/dependencies/gateway-api/gateway/gateway.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: Gateway +metadata: + labels: + istio: ingressgateway + name: istio-ingressgateway +spec: + gatewayClassName: istio + listeners: + - name: default + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: All + addresses: + - value: istio-ingressgateway.istio-system.svc.cluster.local + type: Hostname diff --git a/config/dependencies/gateway-api/gateway/kustomization.yaml b/config/dependencies/gateway-api/gateway/kustomization.yaml new file mode 100644 index 000000000..b489ae5d3 --- /dev/null +++ b/config/dependencies/gateway-api/gateway/kustomization.yaml @@ -0,0 +1,5 @@ +--- +# Adds namespace to all resources. +namespace: istio-system +resources: +- gateway.yaml diff --git a/config/dependencies/istio/default-gateway.yaml b/config/dependencies/istio/default-gateway.yaml deleted file mode 100644 index e0f4b04d9..000000000 --- a/config/dependencies/istio/default-gateway.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: kuadrant-gateway - namespace: kuadrant-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" diff --git a/config/dependencies/kustomization.yaml b/config/dependencies/kustomization.yaml index 6b9c3f56e..e8e37352d 100644 --- a/config/dependencies/kustomization.yaml +++ b/config/dependencies/kustomization.yaml @@ -1,10 +1,8 @@ namespace: kuadrant-system resources: - - controller - authorino - limitador - - gateway-api patchesStrategicMerge: - authorino/delete-ns.yaml diff --git a/config/dependencies/controller/delete-ns.yaml b/config/kuadrant-controller-manifests/delete-ns-object.yaml similarity index 95% rename from config/dependencies/controller/delete-ns.yaml rename to config/kuadrant-controller-manifests/delete-ns-object.yaml index 4283c9952..1b749eaf2 100644 --- a/config/dependencies/controller/delete-ns.yaml +++ b/config/kuadrant-controller-manifests/delete-ns-object.yaml @@ -1,3 +1,4 @@ +--- $patch: delete apiVersion: v1 kind: Namespace diff --git a/config/kuadrant-controller-manifests/delete-ns.yaml b/config/kuadrant-controller-manifests/delete-ns.yaml new file mode 100644 index 000000000..ab0d92443 --- /dev/null +++ b/config/kuadrant-controller-manifests/delete-ns.yaml @@ -0,0 +1,3 @@ +--- +- op: remove + path: /metadata/namespace diff --git a/config/kuadrant-controller-manifests/kustomization.template.yaml b/config/kuadrant-controller-manifests/kustomization.template.yaml new file mode 100644 index 000000000..7421e350d --- /dev/null +++ b/config/kuadrant-controller-manifests/kustomization.template.yaml @@ -0,0 +1,43 @@ +resources: +- github.com/Kuadrant/kuadrant-controller/config/default?ref=${KUADRANT_CONTROLLER_GITREF} + +patchesStrategicMerge: + - delete-ns-object.yaml + +patchesJson6902: +- target: + group: "" + version: v1 + kind: ServiceAccount + name: kuadrant-controller-manager + path: delete-ns.yaml +- target: + group: "rbac.authorization.k8s.io" + version: v1 + kind: Role + name: kuadrant-leader-election-role + path: delete-ns.yaml +- target: + group: "rbac.authorization.k8s.io" + version: v1 + kind: RoleBinding + name: kuadrant-leader-election-rolebinding + path: delete-ns.yaml +- target: + group: "" + version: v1 + kind: ConfigMap + name: kuadrant-manager-config + path: delete-ns.yaml +- target: + group: "" + version: v1 + kind: Service + name: kuadrant-controller-manager-metrics-service + path: delete-ns.yaml +- target: + group: "apps" + version: v1 + kind: Deployment + name: kuadrant-controller-manager + path: delete-ns.yaml diff --git a/config/kuadrant-controller-manifests/kustomization.yaml b/config/kuadrant-controller-manifests/kustomization.yaml new file mode 100644 index 000000000..09ba46b22 --- /dev/null +++ b/config/kuadrant-controller-manifests/kustomization.yaml @@ -0,0 +1,43 @@ +resources: +- github.com/Kuadrant/kuadrant-controller/config/default?ref=main + +patchesStrategicMerge: + - delete-ns-object.yaml + +patchesJson6902: +- target: + group: "" + version: v1 + kind: ServiceAccount + name: kuadrant-controller-manager + path: delete-ns.yaml +- target: + group: "rbac.authorization.k8s.io" + version: v1 + kind: Role + name: kuadrant-leader-election-role + path: delete-ns.yaml +- target: + group: "rbac.authorization.k8s.io" + version: v1 + kind: RoleBinding + name: kuadrant-leader-election-rolebinding + path: delete-ns.yaml +- target: + group: "" + version: v1 + kind: ConfigMap + name: kuadrant-manager-config + path: delete-ns.yaml +- target: + group: "" + version: v1 + kind: Service + name: kuadrant-controller-manager-metrics-service + path: delete-ns.yaml +- target: + group: "apps" + version: v1 + kind: Deployment + name: kuadrant-controller-manager + path: delete-ns.yaml diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml index 4014ba7d7..9356eeda7 100644 --- a/config/manifests/kustomization.yaml +++ b/config/manifests/kustomization.yaml @@ -2,7 +2,6 @@ # used to generate the 'manifests/' directory in a bundle. resources: - bases/kuadrant-operator.clusterserviceversion.yaml -- ../dependencies/controller - ../default - ../samples - ../scorecard diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 420ffd5b9..75eb38521 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,6 +6,135 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apim.kuadrant.io + resources: + - authpolicies + - ratelimitpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apim.kuadrant.io + resources: + - authpolicies/finalizers + verbs: + - update +- apiGroups: + - apim.kuadrant.io + resources: + - authpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - apim.kuadrant.io + resources: + - ratelimitpolicies/finalizers + verbs: + - update +- apiGroups: + - apim.kuadrant.io + resources: + - ratelimitpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - coordination.k8s.io + resources: + - configmaps + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - gateways + verbs: + - get + - list + - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - install.istio.io resources: @@ -43,3 +172,86 @@ rules: - get - patch - update +- apiGroups: + - limitador.kuadrant.io + resources: + - limitadors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - limitador.kuadrant.io + resources: + - ratelimits + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - envoyfilters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - gateways + verbs: + - get + - list + - watch +- apiGroups: + - operator.authorino.kuadrant.io + resources: + - authorinos + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterrolebindings + - clusterroles + - rolebindings + - roles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - security.istio.io + resources: + - authorizationpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/controllers/kuadrant_controller.go b/controllers/kuadrant_controller.go index 823ca8228..1184c5d40 100644 --- a/controllers/kuadrant_controller.go +++ b/controllers/kuadrant_controller.go @@ -19,20 +19,27 @@ package controllers import ( "context" "encoding/json" + "errors" "fmt" "github.com/go-logr/logr" + authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" + limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1" istioapiv1alpha1 "istio.io/api/operator/v1alpha1" iopv1alpha1 "istio.io/istio/operator/pkg/apis/istio/v1alpha1" + appsv1 "k8s.io/api/apps/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" + "github.com/kuadrant/kuadrant-operator/kuadrantcontrollermanifests" "github.com/kuadrant/kuadrant-operator/pkg/common" "github.com/kuadrant/kuadrant-operator/pkg/log" + "github.com/kuadrant/kuadrant-operator/pkg/reconcilers" ) const ( @@ -42,7 +49,7 @@ const ( // KuadrantReconciler reconciles a Kuadrant object type KuadrantReconciler struct { - client.Client + *reconcilers.BaseReconciler Scheme *runtime.Scheme } @@ -50,6 +57,26 @@ type KuadrantReconciler struct { //+kubebuilder:rbac:groups=kuadrant.kuadrant.io,resources=kuadrants/status,verbs=get;update;patch //+kubebuilder:rbac:groups=kuadrant.kuadrant.io,resources=kuadrants/finalizers,verbs=update //+kubebuilder:rbac:groups=install.istio.io,resources=istiooperators,verbs=get;list;watch;create;update;patch +//+kubebuilder:rbac:groups=limitador.kuadrant.io,resources=limitadors,verbs=get;list;watch;create;update;delete;patch +//+kubebuilder:rbac:groups=limitador.kuadrant.io,resources=ratelimits,verbs=get;list;watch;create;update;delete;patch +//+kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch;create;update;delete;patch +//+kubebuilder:rbac:groups=core,resources=serviceaccounts;configmaps;services,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles;clusterroles;rolebindings;clusterrolebindings,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=coordination.k8s.io,resources=configmaps;leases,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch +//+kubebuilder:rbac:groups="",resources=leases,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="apim.kuadrant.io",resources=authpolicies;ratelimitpolicies,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="apim.kuadrant.io",resources=authpolicies/finalizers,verbs=update +//+kubebuilder:rbac:groups="apim.kuadrant.io",resources=ratelimitpolicies/finalizers,verbs=update +//+kubebuilder:rbac:groups="apim.kuadrant.io",resources=authpolicies/status,verbs=get;patch;update +//+kubebuilder:rbac:groups="apim.kuadrant.io",resources=ratelimitpolicies/status,verbs=get;patch;update +//+kubebuilder:rbac:groups="gateway.networking.k8s.io",resources=gateways,verbs=get;list;watch +//+kubebuilder:rbac:groups="gateway.networking.k8s.io",resources=httproutes,verbs=get;list;patch;update;watch +//+kubebuilder:rbac:groups="networking.istio.io",resources=envoyfilters,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="networking.istio.io",resources=gateways,verbs=get;list;watch +//+kubebuilder:rbac:groups="security.istio.io",resources=authorizationpolicies,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=operator.authorino.kuadrant.io,resources=authorinos,verbs=get;list;watch;create;update;delete;patch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -61,7 +88,7 @@ func (r *KuadrantReconciler) Reconcile(eventCtx context.Context, req ctrl.Reques ctx := logr.NewContext(eventCtx, logger) kObj := &kuadrantv1beta1.Kuadrant{} - if err := r.Get(ctx, req.NamespacedName, kObj); err != nil { + if err := r.Client().Get(ctx, req.NamespacedName, kObj); err != nil { if apierrors.IsNotFound(err) { logger.Info("no kuadrant object found") return ctrl.Result{}, nil @@ -87,7 +114,7 @@ func (r *KuadrantReconciler) Reconcile(eventCtx context.Context, req ctrl.Reques logger.Info("removing finalizer") controllerutil.RemoveFinalizer(kObj, kuadrantFinalizer) - if err := r.Update(ctx, kObj); client.IgnoreNotFound(err) != nil { + if err := r.Client().Update(ctx, kObj); client.IgnoreNotFound(err) != nil { return ctrl.Result{}, err } return ctrl.Result{}, nil @@ -101,7 +128,7 @@ func (r *KuadrantReconciler) Reconcile(eventCtx context.Context, req ctrl.Reques if !controllerutil.ContainsFinalizer(kObj, kuadrantFinalizer) { controllerutil.AddFinalizer(kObj, kuadrantFinalizer) - if err := r.Update(ctx, kObj); client.IgnoreNotFound(err) != nil { + if err := r.Client().Update(ctx, kObj); client.IgnoreNotFound(err) != nil { return ctrl.Result{Requeue: true}, err } } @@ -136,7 +163,7 @@ func (r *KuadrantReconciler) unregisterExternalAuthorizer(ctx context.Context) e iop := &iopv1alpha1.IstioOperator{} iopKey := client.ObjectKey{Name: iopName(), Namespace: iopNamespace()} - if err := r.Get(ctx, iopKey, iop); err != nil { + if err := r.Client().Get(ctx, iopKey, iop); err != nil { // It should exists, NotFound also considered as error logger.Error(err, "failed to get istiooperator object", "key", iopKey) return err @@ -174,19 +201,19 @@ func (r *KuadrantReconciler) unregisterExternalAuthorizer(ctx context.Context) e } logger.Info("remove external authorizer from meshconfig") - if err := r.Update(ctx, iop); err != nil { + if err := r.Client().Update(ctx, iop); err != nil { return err } return nil } -func (r *KuadrantReconciler) registerExternalAuthorizer(ctx context.Context) error { +func (r *KuadrantReconciler) registerExternalAuthorizer(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) error { logger := logr.FromContext(ctx) iop := &iopv1alpha1.IstioOperator{} iopKey := client.ObjectKey{Name: iopName(), Namespace: iopNamespace()} - if err := r.Get(ctx, iopKey, iop); err != nil { + if err := r.Client().Get(ctx, iopKey, iop); err != nil { // It should exists, NotFound also considered as error logger.Error(err, "failed to get istiooperator object", "key", iopKey) return err @@ -223,7 +250,7 @@ func (r *KuadrantReconciler) registerExternalAuthorizer(ctx context.Context) err envoyExtAuthzGrpc := make(map[string]interface{}) envoyExtAuthzGrpc["port"] = 50051 - envoyExtAuthzGrpc["service"] = "authorino-authorino-authorization.kuadrant-system.svc.cluster.local" + envoyExtAuthzGrpc["service"] = fmt.Sprintf("authorino-authorino-authorization.%s.svc.cluster.local", kObj.Namespace) kuadrantExtensionProvider := make(map[string]interface{}) kuadrantExtensionProvider["name"] = extAuthorizerName @@ -231,7 +258,7 @@ func (r *KuadrantReconciler) registerExternalAuthorizer(ctx context.Context) err iop.Spec.MeshConfig["extensionProviders"] = append(extensionProviders, kuadrantExtensionProvider) logger.Info("adding external authorizer to meshconfig") - if err := r.Update(ctx, iop); err != nil { + if err := r.Client().Update(ctx, iop); err != nil { return err } @@ -239,7 +266,19 @@ func (r *KuadrantReconciler) registerExternalAuthorizer(ctx context.Context) err } func (r *KuadrantReconciler) reconcileSpec(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) (ctrl.Result, error) { - if err := r.registerExternalAuthorizer(ctx); err != nil { + if err := r.registerExternalAuthorizer(ctx, kObj); err != nil { + return ctrl.Result{}, err + } + + if err := r.reconcileLimitador(ctx, kObj); err != nil { + return ctrl.Result{}, err + } + + if err := r.reconcileKuadrantController(ctx, kObj); err != nil { + return ctrl.Result{}, err + } + + if err := r.reconcileAuthorino(ctx, kObj); err != nil { return ctrl.Result{}, err } @@ -295,9 +334,114 @@ func hasKuadrantAuthorizer(iop *iopv1alpha1.IstioOperator) bool { return false } +func (r *KuadrantReconciler) reconcileLimitador(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) error { + limitador := &limitadorv1alpha1.Limitador{ + TypeMeta: metav1.TypeMeta{ + Kind: "Limitador", + APIVersion: "limitador.kuadrant.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "limitador", + Namespace: kObj.Namespace, + }, + Spec: limitadorv1alpha1.LimitadorSpec{}, + } + + err := r.SetOwnerReference(kObj, limitador) + if err != nil { + return err + } + + return r.ReconcileResource(ctx, &limitadorv1alpha1.Limitador{}, limitador, reconcilers.CreateOnlyMutator) +} + +func (r *KuadrantReconciler) reconcileKuadrantController(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) error { + logger := logr.FromContext(ctx) + kuadrantControllerVersion, err := common.KuadrantControllerImage(ctx, r.Scheme) + if err != nil { + return err + } + logger.Info("Deploying kuadrant controller", "version", kuadrantControllerVersion) + + data, err := kuadrantcontrollermanifests.Content() + if err != nil { + return err + } + + return common.DecodeFile(ctx, data, r.Scheme, r.createOnlyInKuadrantNSCb(ctx, kObj)) +} + +func (r *KuadrantReconciler) createOnlyInKuadrantNSCb(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) common.DecodeCallback { + return func(obj runtime.Object) error { + logger := logr.FromContext(ctx) + k8sObj, ok := obj.(client.Object) + if !ok { + return errors.New("runtime.Object could not be type asserted to client.Object") + } + + // Create in Kuadrant CR's namespace + k8sObj.SetNamespace(kObj.Namespace) + err := r.SetOwnerReference(kObj, k8sObj) + if err != nil { + return err + } + + k8sObjKind := k8sObj.DeepCopyObject().GetObjectKind() + + err = r.Client().Create(ctx, k8sObj) + logger.V(1).Info("create resource", "GKV", k8sObjKind.GroupVersionKind(), "name", k8sObj.GetName(), "error", err) + if err != nil { + if apierrors.IsAlreadyExists(err) { + // Omit error + logger.Info("Already exists", "GKV", k8sObjKind.GroupVersionKind(), "name", k8sObj.GetName()) + } else { + return err + } + } + return nil + } +} + +func (r *KuadrantReconciler) reconcileAuthorino(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) error { + tmpFalse := false + authorino := &authorinov1beta1.Authorino{ + TypeMeta: metav1.TypeMeta{ + Kind: "Authorino", + APIVersion: "operator.authorino.kuadrant.io/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "authorino", + Namespace: kObj.Namespace, + }, + Spec: authorinov1beta1.AuthorinoSpec{ + ClusterWide: true, + Listener: authorinov1beta1.Listener{ + Tls: authorinov1beta1.Tls{ + Enabled: &tmpFalse, + }, + }, + OIDCServer: authorinov1beta1.OIDCServer{ + Tls: authorinov1beta1.Tls{ + Enabled: &tmpFalse, + }, + }, + }, + } + + err := r.SetOwnerReference(kObj, authorino) + if err != nil { + return err + } + + return r.ReconcileResource(ctx, &authorinov1beta1.Authorino{}, authorino, reconcilers.CreateOnlyMutator) +} + // SetupWithManager sets up the controller with the Manager. func (r *KuadrantReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&kuadrantv1beta1.Kuadrant{}). + Owns(&appsv1.Deployment{}). + Owns(&limitadorv1alpha1.Limitador{}). + Owns(&authorinov1beta1.Authorino{}). Complete(r) } diff --git a/controllers/kuadrant_status.go b/controllers/kuadrant_status.go index 3571051f6..7ea2967bb 100644 --- a/controllers/kuadrant_status.go +++ b/controllers/kuadrant_status.go @@ -5,12 +5,16 @@ import ( "fmt" "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" meta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1" "github.com/kuadrant/kuadrant-operator/pkg/common" ) @@ -21,7 +25,10 @@ const ( func (r *KuadrantReconciler) reconcileStatus(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant, specErr error) (ctrl.Result, error) { logger := logr.FromContext(ctx) - newStatus := r.calculateStatus(kObj, specErr) + newStatus, err := r.calculateStatus(ctx, kObj, specErr) + if err != nil { + return reconcile.Result{}, err + } equalStatus := kObj.Status.Equals(newStatus, logger) logger.V(1).Info("Status", "status is different", !equalStatus) @@ -41,7 +48,7 @@ func (r *KuadrantReconciler) reconcileStatus(ctx context.Context, kObj *kuadrant logger.V(1).Info("Updating Status", "sequence no:", fmt.Sprintf("sequence No: %v->%v", kObj.Status.ObservedGeneration, newStatus.ObservedGeneration)) kObj.Status = *newStatus - updateErr := r.Status().Update(ctx, kObj) + updateErr := r.Client().Status().Update(ctx, kObj) if updateErr != nil { // Ignore conflicts, resource might just be outdated. if errors.IsConflict(updateErr) { @@ -54,20 +61,23 @@ func (r *KuadrantReconciler) reconcileStatus(ctx context.Context, kObj *kuadrant return ctrl.Result{}, nil } -func (r *KuadrantReconciler) calculateStatus(kObj *kuadrantv1beta1.Kuadrant, specErr error) *kuadrantv1beta1.KuadrantStatus { +func (r *KuadrantReconciler) calculateStatus(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant, specErr error) (*kuadrantv1beta1.KuadrantStatus, error) { newStatus := &kuadrantv1beta1.KuadrantStatus{ // Copy initial conditions. Otherwise, status will always be updated Conditions: common.CopyConditions(kObj.Status.Conditions), } - availableCond := r.readyCondition(specErr) + availableCond, err := r.readyCondition(ctx, kObj, specErr) + if err != nil { + return nil, err + } meta.SetStatusCondition(&newStatus.Conditions, *availableCond) - return newStatus + return newStatus, nil } -func (r *KuadrantReconciler) readyCondition(specErr error) *metav1.Condition { +func (r *KuadrantReconciler) readyCondition(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant, specErr error) (*metav1.Condition, error) { cond := &metav1.Condition{ Type: ReadyConditionType, Status: metav1.ConditionTrue, @@ -79,7 +89,121 @@ func (r *KuadrantReconciler) readyCondition(specErr error) *metav1.Condition { cond.Status = metav1.ConditionFalse cond.Reason = "ReconcilliationError" cond.Message = specErr.Error() + return cond, nil + } + + reason, err := r.checkKuadrantControllerAvailable(ctx, kObj) + if err != nil { + return nil, err + } + if reason != nil { + cond.Status = metav1.ConditionFalse + cond.Reason = "KuadrantControllerNotReady" + cond.Message = *reason + return cond, nil + } + + reason, err = r.checkLimitadorAvailable(ctx, kObj) + if err != nil { + return nil, err + } + if reason != nil { + cond.Status = metav1.ConditionFalse + cond.Reason = "LimitadorNotReady" + cond.Message = *reason + return cond, nil + } + + reason, err = r.checkAuthorinoAvailable(ctx, kObj) + if err != nil { + return nil, err + } + if reason != nil { + cond.Status = metav1.ConditionFalse + cond.Reason = "AuthorinoNotReady" + cond.Message = *reason + return cond, nil + } + + return cond, nil +} + +func (r *KuadrantReconciler) checkKuadrantControllerAvailable(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) (*string, error) { + deployment := &appsv1.Deployment{} + dKey := client.ObjectKey{Name: "kuadrant-controller-manager", Namespace: kObj.Namespace} + err := r.Client().Get(ctx, dKey, deployment) + if err != nil && !errors.IsNotFound(err) { + return nil, err + } + + if err != nil && errors.IsNotFound(err) { + tmp := err.Error() + return &tmp, nil + } + + availableCondition := common.FindDeploymentStatusCondition(deployment.Status.Conditions, "Available") + if availableCondition == nil { + tmp := "Available condition not found" + return &tmp, nil + } + + if availableCondition.Status != corev1.ConditionTrue { + return &availableCondition.Message, nil + } + + return nil, nil +} + +func (r *KuadrantReconciler) checkLimitadorAvailable(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) (*string, error) { + // Should be implemented reading the Limitador CR's status conditions. + // Not implemented yet in the limitador's operator + deployment := &appsv1.Deployment{} + dKey := client.ObjectKey{Name: "limitador", Namespace: kObj.Namespace} + err := r.Client().Get(ctx, dKey, deployment) + if err != nil && !errors.IsNotFound(err) { + return nil, err + } + + if err != nil && errors.IsNotFound(err) { + tmp := err.Error() + return &tmp, nil + } + + availableCondition := common.FindDeploymentStatusCondition(deployment.Status.Conditions, "Available") + if availableCondition == nil { + tmp := "Available condition not found" + return &tmp, nil + } + + if availableCondition.Status != corev1.ConditionTrue { + return &availableCondition.Message, nil + } + + return nil, nil +} + +func (r *KuadrantReconciler) checkAuthorinoAvailable(ctx context.Context, kObj *kuadrantv1beta1.Kuadrant) (*string, error) { + authorino := &authorinov1beta1.Authorino{} + dKey := client.ObjectKey{Name: "authorino", Namespace: kObj.Namespace} + err := r.Client().Get(ctx, dKey, authorino) + if err != nil && !errors.IsNotFound(err) { + return nil, err + } + + if err != nil && errors.IsNotFound(err) { + tmp := err.Error() + return &tmp, nil + } + + readyCondition := common.FindAuthorinoStatusCondition(authorino.Status.Conditions, "Ready") + if readyCondition == nil { + tmp := "Ready condition not found" + return &tmp, nil + } + + if readyCondition.Status != corev1.ConditionTrue { + return &readyCondition.Message, nil } - return cond + return nil, nil } diff --git a/go.mod b/go.mod index 5eaafc65b..904671d0d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.16 require ( github.com/go-logr/logr v0.4.0 github.com/google/go-cmp v0.5.6 + github.com/kuadrant/authorino-operator v0.2.0 + github.com/kuadrant/limitador-operator v0.2.0 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.16.0 go.uber.org/zap v1.19.1 @@ -13,6 +15,8 @@ require ( istio.io/api v0.0.0-20220304035241-8c47cbbea144 // go get istio.io/istio/operator/pkg/apis/istio/v1alpha1@1.12.6 istio.io/istio v0.0.0-20220328194112-a0c7a3355331 + k8s.io/api v0.22.2 + k8s.io/apiextensions-apiserver v0.22.2 k8s.io/apimachinery v0.22.2 k8s.io/client-go v0.22.2 k8s.io/klog/v2 v2.10.0 diff --git a/go.sum b/go.sum index a163475a1..875deaf9e 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,7 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= @@ -67,22 +68,26 @@ github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW 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.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.20 h1:s8H1PbCZSqg/DH7JMlOz6YMig6htWLNPsjDdlLqCx3M= github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.15 h1:X+p2GF0GWyOiSmqohIaEeuNFNDY4I4EOlVuUQvFdWMk= github.com/Azure/go-autorest/autorest/adal v0.9.15/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= 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.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= 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= @@ -450,9 +455,11 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V 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/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 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.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= 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/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= @@ -762,6 +769,10 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kuadrant/authorino-operator v0.2.0 h1:7B1colZ1REX0ZJUnAdunnHfiqBQTILI+yfB6Epr1dYQ= +github.com/kuadrant/authorino-operator v0.2.0/go.mod h1:enEBTG0San0QUHhqZ08OXiVDkURkRE2UBEQelcNTiGI= +github.com/kuadrant/limitador-operator v0.2.0 h1:9hQ5Hmfrx2SG0+RXvwTuHIq8Mqp9L+7J1E3T7X2upgs= +github.com/kuadrant/limitador-operator v0.2.0/go.mod h1:lMtN0/84z3kHZheljpbQN9vrgfPab+Zvv/7CTU3dS+k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= @@ -908,6 +919,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +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= @@ -917,6 +929,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= @@ -1174,6 +1187,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= @@ -1211,6 +1225,7 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= 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/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -1218,10 +1233,14 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +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/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= @@ -1248,6 +1267,7 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1542,6 +1562,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1570,6 +1591,8 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/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= @@ -1596,6 +1619,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1622,6 +1646,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T 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.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +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= gomodules.xyz/jsonpatch/v3 v3.0.1/go.mod h1:CBhndykehEwTOlEfnsfJwvkFQbSN8YZFr9M+cIHAJto= @@ -1868,17 +1893,21 @@ istio.io/pkg v0.0.0-20220304035241-7fbb2b738306/go.mod h1:rJLxqU2GEnFR3cIiun1uoi k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA= k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= +k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= +k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg= k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= +k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= +k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA= k8s.io/apiextensions-apiserver v0.21.3/go.mod h1:kl6dap3Gd45+21Jnh6utCx8Z2xxLm8LGDkprcd+KbsE= k8s.io/apiextensions-apiserver v0.22.1/go.mod h1:HeGmorjtRmRLE+Q8dJu6AYRoZccvCMsghwS8XTUYb2c= k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= @@ -1886,20 +1915,24 @@ k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQR k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= +k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= +k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= +k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw= k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU= k8s.io/apiserver v0.22.1/go.mod h1:2mcM6dzSt+XndzVQJX21Gx0/Klo7Aen7i0Ai6tIa400= k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= @@ -1907,10 +1940,12 @@ k8s.io/cli-runtime v0.22.1/go.mod h1:YqwGrlXeEk15Yn3em2xzr435UGwbrCw5x+COQoTYfoo k8s.io/cli-runtime v0.22.2/go.mod h1:tkm2YeORFpbgQHEK/igqttvPTRIHFRz5kATlw53zlMI= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= +k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= +k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU= k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= @@ -1918,15 +1953,19 @@ k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= +k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U= k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= +k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc= k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ= k8s.io/component-base v0.22.1/go.mod h1:0D+Bl8rrnsPN9v0dyYvkqFfBeAd4u7n77ze+p8CMiPo= k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= @@ -1940,6 +1979,7 @@ k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1958,6 +1998,7 @@ k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= @@ -1970,7 +2011,10 @@ k8s.io/metrics v0.22.1/go.mod h1:i/ZNap89UkV1gLa26dn7fhKAdheJaKy+moOqJbiif7E= k8s.io/metrics v0.22.2/go.mod h1:GUcsBtpsqQD1tKFS/2wCKu4ZBowwRncLOJH1rgWs3uw= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210527160623-6fdb442a123b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1984,11 +2028,14 @@ rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= 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.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A= +sigs.k8s.io/controller-runtime v0.7.2/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU= +sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= sigs.k8s.io/controller-runtime v0.9.6/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA= sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc= sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= @@ -2003,6 +2050,7 @@ sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae sigs.k8s.io/mcs-api v0.1.0/go.mod h1:gGiAryeFNB4GBsq2LBmVqSgKoobLxt+p7ii/WG5QYYw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/kuadrantcontrollermanifests/assets.go b/kuadrantcontrollermanifests/assets.go new file mode 100644 index 000000000..3d849c0d5 --- /dev/null +++ b/kuadrantcontrollermanifests/assets.go @@ -0,0 +1,16 @@ +package kuadrantcontrollermanifests + +import ( + "embed" + + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Content holds kuadrant manifests +//go:embed autogenerated +var content embed.FS + +func Content() ([]byte, error) { + logf.Log.V(1).Info("Resource file", "name", "autogenerated/kuadrant-controller.yaml") + return content.ReadFile("autogenerated/kuadrant-controller.yaml") +} diff --git a/kuadrantcontrollermanifests/autogenerated/kuadrant-controller.yaml b/kuadrantcontrollermanifests/autogenerated/kuadrant-controller.yaml new file mode 100644 index 000000000..6e7f86db2 --- /dev/null +++ b/kuadrantcontrollermanifests/autogenerated/kuadrant-controller.yaml @@ -0,0 +1,877 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + labels: + app: kuadrant + name: authpolicies.apim.kuadrant.io +spec: + group: apim.kuadrant.io + names: + kind: AuthPolicy + listKind: AuthPolicyList + plural: authpolicies + singular: authpolicy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + 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: + properties: + policy: + description: Policy per action type but also per provider if using custom type. + items: + properties: + action: + description: The action to take if the request is matches with the rules. + enum: + - ALLOW + - CUSTOM + - DENY + - AUDIT + type: string + provider: + default: "" + description: Specifies detailed configuration of the CUSTOM action. Must be used only with CUSTOM action. + type: string + rules: + description: A list of rules to match the request. A match occurs when at least one rule matches the request. + items: + description: "Rule matches requests from a list of sources that perform a list of operations subject to a list of conditions. A match occurs when at least one source, operation and condition matches the request. An empty rule is always matched. \n Any string field in the rule supports Exact, Prefix, Suffix and Presence match: \n - Exact match: \"abc\" will match on value \"abc\". - Prefix match: \"abc*\" will match on value \"abc\" and \"abcd\". - Suffix match: \"*abc\" will match on value \"abc\" and \"xabc\". - Presence match: \"*\" will match when value is not empty." + properties: + from: + description: "Optional. from specifies the source of a request. \n If not set, any source is allowed." + items: + description: From includes a list or sources. + properties: + source: + description: Source specifies the source of a request. + properties: + ip_blocks: + description: "Optional. A list of IP blocks, which matches to the \"source.ip\" attribute. Populated from the source address of the IP packet. Single IP (e.g. \"1.2.3.4\") and CIDR (e.g. \"1.2.3.0/24\") are supported. \n If not set, any IP is allowed." + items: + type: string + type: array + namespaces: + description: "Optional. A list of namespaces, which matches to the \"source.namespace\" attribute. This field requires mTLS enabled. \n If not set, any namespace is allowed." + items: + type: string + type: array + not_ip_blocks: + description: Optional. A list of negative match of IP blocks. + items: + type: string + type: array + not_namespaces: + description: Optional. A list of negative match of namespaces. + items: + type: string + type: array + not_principals: + description: Optional. A list of negative match of source peer identities. + items: + type: string + type: array + not_remote_ip_blocks: + description: Optional. A list of negative match of remote IP blocks. + items: + type: string + type: array + not_request_principals: + description: Optional. A list of negative match of request identities. + items: + type: string + type: array + principals: + description: "Optional. A list of source peer identities (i.e. service account), which matches to the \"source.principal\" attribute. This field requires mTLS enabled. \n If not set, any principal is allowed." + items: + type: string + type: array + remote_ip_blocks: + description: "Optional. A list of IP blocks, which matches to the \"remote.ip\" attribute. Populated from X-Forwarded-For header or proxy protocol. To make use of this field, you must configure the numTrustedProxies field of the gatewayTopology under the meshConfig when you install Istio or using an annotation on the ingress gateway. See the documentation here: [Configuring Gateway Network Topology](https://istio.io/latest/docs/ops/configuration/traffic-management/network-topologies/). Single IP (e.g. \"1.2.3.4\") and CIDR (e.g. \"1.2.3.0/24\") are supported. \n If not set, any IP is allowed." + items: + type: string + type: array + request_principals: + description: "Optional. A list of request identities (i.e. \"iss/sub\" claims), which matches to the \"request.auth.principal\" attribute. \n If not set, any request principal is allowed." + items: + type: string + type: array + type: object + type: object + type: array + to: + description: "Optional. to specifies the operation of a request. \n If not set, any operation is allowed." + items: + description: To includes a list or operations. + properties: + operation: + description: Operation specifies the operation of a request. + properties: + hosts: + description: "Optional. A list of hosts, which matches to the \"request.host\" attribute. \n If not set, any host is allowed. Must be used only with HTTP." + items: + type: string + type: array + methods: + description: "Optional. A list of methods, which matches to the \"request.method\" attribute. For gRPC service, this will always be \"POST\". \n If not set, any method is allowed. Must be used only with HTTP." + items: + type: string + type: array + not_hosts: + description: Optional. A list of negative match of hosts. + items: + type: string + type: array + not_methods: + description: Optional. A list of negative match of methods. + items: + type: string + type: array + not_paths: + description: Optional. A list of negative match of paths. + items: + type: string + type: array + not_ports: + description: Optional. A list of negative match of ports. + items: + type: string + type: array + paths: + description: "Optional. A list of paths, which matches to the \"request.url_path\" attribute. For gRPC service, this will be the fully-qualified name in the form of \"/package.service/method\". \n If not set, any path is allowed. Must be used only with HTTP." + items: + type: string + type: array + ports: + description: "Optional. A list of ports, which matches to the \"destination.port\" attribute. \n If not set, any port is allowed." + items: + type: string + type: array + type: object + type: object + type: array + when: + description: "Optional. when specifies a list of additional conditions of a request. \n If not set, any condition is allowed." + items: + description: Condition specifies additional required attributes. + properties: + key: + description: The name of an Istio attribute. See the [full list of supported attributes](https://istio.io/docs/reference/config/security/conditions/). + type: string + not_values: + description: 'Optional. A list of negative match of values for the attribute. Note: at least one of values or not_values must be set.' + items: + type: string + type: array + values: + description: 'Optional. A list of allowed values for the attribute. Note: at least one of values or not_values must be set.' + items: + type: string + type: array + type: object + type: array + type: object + type: array + required: + - action + type: object + type: array + x-kubernetes-list-map-keys: + - action + - provider + x-kubernetes-list-type: map + targetRef: + description: TargetRef identifies an API object to apply policy to. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When unspecified, the local namespace is inferred. Even when policy targets a resource in a different namespace, it MUST only apply to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - targetRef + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + labels: + app: kuadrant + name: ratelimitpolicies.apim.kuadrant.io +spec: + group: apim.kuadrant.io + names: + kind: RateLimitPolicy + listKind: RateLimitPolicyList + plural: ratelimitpolicies + singular: ratelimitpolicy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: RateLimitPolicy is the Schema for the ratelimitpolicies 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: RateLimitPolicySpec defines the desired state of RateLimitPolicy + properties: + domain: + type: string + limits: + items: + description: RateLimitSpec defines the desired state of RateLimit + properties: + conditions: + items: + type: string + type: array + max_value: + type: integer + namespace: + type: string + seconds: + type: integer + variables: + items: + type: string + type: array + required: + - conditions + - max_value + - namespace + - seconds + - variables + type: object + type: array + rateLimits: + items: + properties: + actions: + items: + description: Action_Specifier defines one envoy rate limit action + oneOf: + - required: + - generic_key + - required: + - metadata + - required: + - remote_address + - required: + - request_headers + properties: + generic_key: + properties: + descriptor_key: + type: string + descriptor_value: + type: string + required: + - descriptor_value + type: object + metadata: + properties: + default_value: + type: string + descriptor_key: + type: string + metadata_key: + properties: + key: + type: string + path: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + required: + - key + - path + type: object + source: + description: MetadataSource https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-enum-config-route-v3-ratelimit-action-metadata-source + enum: + - DYNAMIC + - ROUTE_ENTRY + type: string + required: + - descriptor_key + - metadata_key + type: object + remote_address: + description: RemoteAddressSpec no need to specify descriptor entry is populated using the trusted address from [x-forwarded-for](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#config-http-conn-man-headers-x-forwarded-for) + type: object + request_headers: + description: RequestHeadersSpec Rate limit on request headers. + properties: + descriptor_key: + type: string + header_name: + type: string + skip_if_absent: + type: boolean + required: + - descriptor_key + - header_name + type: object + type: object + type: array + stage: + description: 'Definfing phase at which rate limits will be applied. Valid values are: PREAUTH, POSTAUTH, BOTH' + enum: + - PREAUTH + - POSTAUTH + - BOTH + type: string + required: + - stage + type: object + type: array + rules: + items: + properties: + name: + description: Name supports regex for fetching operations from routing resources For VirtualService, if route name matches, all the match requests are converted to operations internally. But specific match request names are also supported. + type: string + operations: + description: Operation specifies the operations of a request + items: + description: Each operation type has OR semantics and overall AND semantics for a match. + properties: + methods: + items: + type: string + type: array + paths: + items: + type: string + type: array + type: object + type: array + rateLimits: + items: + properties: + actions: + items: + description: Action_Specifier defines one envoy rate limit action + oneOf: + - required: + - generic_key + - required: + - metadata + - required: + - remote_address + - required: + - request_headers + properties: + generic_key: + properties: + descriptor_key: + type: string + descriptor_value: + type: string + required: + - descriptor_value + type: object + metadata: + properties: + default_value: + type: string + descriptor_key: + type: string + metadata_key: + properties: + key: + type: string + path: + items: + properties: + key: + type: string + required: + - key + type: object + type: array + required: + - key + - path + type: object + source: + description: MetadataSource https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-enum-config-route-v3-ratelimit-action-metadata-source + enum: + - DYNAMIC + - ROUTE_ENTRY + type: string + required: + - descriptor_key + - metadata_key + type: object + remote_address: + description: RemoteAddressSpec no need to specify descriptor entry is populated using the trusted address from [x-forwarded-for](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#config-http-conn-man-headers-x-forwarded-for) + type: object + request_headers: + description: RequestHeadersSpec Rate limit on request headers. + properties: + descriptor_key: + type: string + header_name: + type: string + skip_if_absent: + type: boolean + required: + - descriptor_key + - header_name + type: object + type: object + type: array + stage: + description: 'Definfing phase at which rate limits will be applied. Valid values are: PREAUTH, POSTAUTH, BOTH' + enum: + - PREAUTH + - POSTAUTH + - BOTH + type: string + required: + - stage + type: object + type: array + type: object + type: array + targetRef: + description: TargetRef identifies an API object to apply policy to. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When unspecified, the local namespace is inferred. Even when policy targets a resource in a different namespace, it MUST only apply to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - domain + - targetRef + type: object + status: + description: RateLimitPolicyStatus defines the observed state of RateLimitPolicy + properties: + conditions: + description: 'Represents the observations of a foo''s current state. Known .status.conditions.type are: "Available"' + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: ObservedGeneration reflects the generation of the most recently observed spec. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: kuadrant + name: kuadrant-controller-manager +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app: kuadrant + name: kuadrant-leader-election-role +rules: +- apiGroups: + - "" + - coordination.k8s.io + resources: + - configmaps + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app: kuadrant + name: kuadrant-manager-role +rules: +- apiGroups: + - apim.kuadrant.io + resources: + - authpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apim.kuadrant.io + resources: + - authpolicies/finalizers + verbs: + - update +- apiGroups: + - apim.kuadrant.io + resources: + - ratelimitpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apim.kuadrant.io + resources: + - ratelimitpolicies/finalizers + verbs: + - update +- apiGroups: + - apim.kuadrant.io + resources: + - ratelimitpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - gateway.networking.k8s.io + resources: + - gateways + verbs: + - get + - list + - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - limitador.kuadrant.io + resources: + - ratelimits + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - envoyfilters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.istio.io + resources: + - gateways + verbs: + - get + - list + - watch +- apiGroups: + - security.istio.io + resources: + - authorizationpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app: kuadrant + name: kuadrant-leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kuadrant-leader-election-role +subjects: +- kind: ServiceAccount + name: kuadrant-controller-manager + namespace: system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app: kuadrant + name: kuadrant-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kuadrant-manager-role +subjects: +- kind: ServiceAccount + name: kuadrant-controller-manager + namespace: system +--- +apiVersion: v1 +data: + controller_manager_config.yaml: | + apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 + kind: ControllerManagerConfig + health: + healthProbeBindAddress: :8081 + metrics: + bindAddress: :8080 + webhook: + port: 9443 + leaderElection: + leaderElect: true + resourceName: e358d637.kuadrant.io +kind: ConfigMap +metadata: + labels: + app: kuadrant + name: kuadrant-manager-config +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: kuadrant + control-plane: controller-manager + name: kuadrant-controller-manager-metrics-service +spec: + ports: + - name: metrics + port: 8080 + targetPort: metrics + selector: + app: kuadrant + control-plane: controller-manager +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: kuadrant + control-plane: controller-manager + name: kuadrant-controller-manager +spec: + replicas: 1 + selector: + matchLabels: + app: kuadrant + control-plane: controller-manager + template: + metadata: + labels: + app: kuadrant + control-plane: controller-manager + spec: + containers: + - args: + - --config=controller_manager_config.yaml + command: + - /manager + image: quay.io/kuadrant/kuadrant-controller:latest + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 8080 + name: metrics + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + volumeMounts: + - mountPath: /controller_manager_config.yaml + name: manager-config + subPath: controller_manager_config.yaml + securityContext: + runAsNonRoot: true + serviceAccountName: kuadrant-controller-manager + terminationGracePeriodSeconds: 10 + volumes: + - configMap: + name: kuadrant-manager-config + name: manager-config diff --git a/main.go b/main.go index dc179a4be..8fa0964c5 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,10 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" + authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" + limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1" istioapis "istio.io/istio/operator/pkg/apis" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -37,6 +40,7 @@ import ( "github.com/kuadrant/kuadrant-operator/controllers" "github.com/kuadrant/kuadrant-operator/pkg/common" "github.com/kuadrant/kuadrant-operator/pkg/log" + "github.com/kuadrant/kuadrant-operator/pkg/reconcilers" //+kubebuilder:scaffold:imports ) @@ -48,8 +52,10 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(limitadorv1alpha1.AddToScheme(scheme)) + utilruntime.Must(authorinov1beta1.AddToScheme(scheme)) + utilruntime.Must(apiextv1.AddToScheme(scheme)) utilruntime.Must(istioapis.AddToScheme(scheme)) - utilruntime.Must(kuadrantv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme @@ -98,9 +104,15 @@ func main() { os.Exit(1) } + kuadrantBaseReconciler := reconcilers.NewBaseReconciler( + mgr.GetClient(), mgr.GetScheme(), mgr.GetAPIReader(), + log.Log.WithName("kuadrant"), + mgr.GetEventRecorderFor("Kuadrant"), + ) + if err = (&controllers.KuadrantReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + BaseReconciler: kuadrantBaseReconciler, + Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Kuadrant") os.Exit(1) diff --git a/make/gateway-api.mk b/make/gateway-api.mk new file mode 100644 index 000000000..0ef8594aa --- /dev/null +++ b/make/gateway-api.mk @@ -0,0 +1,10 @@ + +##@ Gateway API resources + +.PHONY: deploy-gateway +deploy-gateway: kustomize ## Deploy Gateway API gateway + $(KUSTOMIZE) build config/dependencies/gateway-api/gateway | kubectl apply -f - + +.PHONY: gateway-api-install +gateway-api-install: kustomize ## Install Gateway API CRDs + $(KUSTOMIZE) build config/dependencies/gateway-api | kubectl apply -f - diff --git a/make/kind.mk b/make/kind.mk index ea13a6c61..e63ad8f32 100644 --- a/make/kind.mk +++ b/make/kind.mk @@ -14,12 +14,17 @@ kind-create-cluster: kind ## Create the "kuadrant-local" kind cluster. $(KIND) create cluster --name $(KIND_CLUSTER_NAME) .PHONY: kind-delete-cluster -kind-delete-cluster: ## Delete the "kuadrant-local" kind cluster. - $(KIND) delete cluster --name $(KIND_CLUSTER_NAME) +kind-delete-cluster: kind ## Delete the "kuadrant-local" kind cluster. + - $(KIND) delete cluster --name $(KIND_CLUSTER_NAME) .PHONY: kind-create-kuadrant-cluster kind-create-kuadrant-cluster: export IMG := quay.io/kuadrant/kuadrant-operator:dev -kind-create-kuadrant-cluster: kind-create-cluster istio-install ## Create a kind cluster with kuadrant deployed. +kind-create-kuadrant-cluster: ## Create a kind cluster with kuadrant deployed. + $(MAKE) kind-delete-cluster + $(MAKE) kind-create-cluster + $(MAKE) gateway-api-install + $(MAKE) istio-install + $(MAKE) deploy-gateway $(MAKE) docker-build $(KIND) load docker-image $(IMG) --name $(KIND_CLUSTER_NAME) $(MAKE) install diff --git a/make/kuadrant-controller-manifests.mk b/make/kuadrant-controller-manifests.mk new file mode 100644 index 000000000..07176c3df --- /dev/null +++ b/make/kuadrant-controller-manifests.mk @@ -0,0 +1,13 @@ + +##@ Kuadrant controller manifests + +.PHONY: kuadrant-controller-manifests-template +kuadrant-controller-manifests-template: export KUADRANT_CONTROLLER_GITREF := $(KUADRANT_CONTROLLER_GITREF) +kuadrant-controller-manifests-template: + envsubst \ + < $(PROJECT_PATH)/config/kuadrant-controller-manifests/kustomization.template.yaml \ + > $(PROJECT_PATH)/config/kuadrant-controller-manifests/kustomization.yaml + +.PHONY: kuadrant-controller-manifests +kuadrant-controller-manifests: kuadrant-controller-manifests-template kustomize ## Update kuadrant controller manifests. + $(KUSTOMIZE) build config/kuadrant-controller-manifests -o $(PROJECT_PATH)/kuadrantcontrollermanifests/autogenerated/kuadrant-controller.yaml diff --git a/make/verify.mk b/make/verify.mk index 25d11a5bc..19781cad7 100644 --- a/make/verify.mk +++ b/make/verify.mk @@ -16,3 +16,8 @@ verify-bundle: bundle ## Verify bundle update. .PHONY: verify-fmt verify-fmt: fmt ## Verify fmt update. git diff --exit-code ./api ./controllers + +.PHONY: verify-kuadrant-controller-manifests +verify-kuadrant-controller-manifests: kuadrant-controller-manifests ## Verify kuadrant controller manifests update. + git diff --exit-code ./kuadrantcontrollermanifests/autogenerated + [ -z "$$(git ls-files --other --exclude-standard --directory --no-empty-directory ./kuadrantcontrollermanifests/autogenerated)" ] diff --git a/pkg/common/authorino_conditions.go b/pkg/common/authorino_conditions.go new file mode 100644 index 000000000..c50a36751 --- /dev/null +++ b/pkg/common/authorino_conditions.go @@ -0,0 +1,15 @@ +package common + +import ( + authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" +) + +func FindAuthorinoStatusCondition(conditions []authorinov1beta1.Condition, conditionType string) *authorinov1beta1.Condition { + for i := range conditions { + if conditions[i].Type == authorinov1beta1.ConditionType(conditionType) { + return &conditions[i] + } + } + + return nil +} diff --git a/pkg/common/common.go b/pkg/common/common.go index bfa193d84..0c43455ce 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -1,6 +1,14 @@ package common -import "os" +import ( + "context" + "os" + + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/kuadrant/kuadrant-operator/kuadrantcontrollermanifests" +) func FetchEnv(key string, def string) string { val, ok := os.LookupEnv(key) @@ -10,3 +18,32 @@ func FetchEnv(key string, def string) string { return val } + +func KuadrantControllerImage(ctx context.Context, scheme *runtime.Scheme) (string, error) { + image := "unknown" + + parser := func(obj runtime.Object) error { + if deployment, ok := obj.(*appsv1.Deployment); ok { + if deployment.GetName() == "kuadrant-controller-manager" { + for _, container := range deployment.Spec.Template.Spec.Containers { + if container.Name == "manager" { + image = container.Image + } + } + } + } + return nil + } + + content, err := kuadrantcontrollermanifests.Content() + if err != nil { + return "", err + } + + err = DecodeFile(ctx, content, scheme, parser) + if err != nil { + return "", err + } + + return image, nil +} diff --git a/pkg/common/k8s_utils.go b/pkg/common/k8s_utils.go new file mode 100644 index 000000000..eb3e45164 --- /dev/null +++ b/pkg/common/k8s_utils.go @@ -0,0 +1,98 @@ +/* +Copyright 2021 Red Hat, Inc. + +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 common + +import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + DeleteTagAnnotation = "kuadrant.io/delete" +) + +func ObjectInfo(obj client.Object) string { + return fmt.Sprintf("%s/%s", obj.GetObjectKind().GroupVersionKind().Kind, obj.GetName()) +} + +func TagObjectToDelete(obj client.Object) { + // Add custom annotation + annotations := obj.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + obj.SetAnnotations(annotations) + } + annotations[DeleteTagAnnotation] = "true" +} + +func IsObjectTaggedToDelete(obj client.Object) bool { + annotations := obj.GetAnnotations() + if annotations == nil { + return false + } + + annotation, ok := annotations[DeleteTagAnnotation] + return ok && annotation == "true" +} + +func IsOwnedBy(owned, owner client.Object) bool { + ownerGVK := owner.GetObjectKind().GroupVersionKind() + + for _, o := range owned.GetOwnerReferences() { + oGV, err := schema.ParseGroupVersion(o.APIVersion) + if err != nil { + return false + } + + // Version needs to be checked??? + if oGV.Group == ownerGVK.Group && o.Kind == ownerGVK.Kind && owner.GetName() == o.Name { + return true + } + } + + return false +} + +// ObjectKeyListDifference computest a - b +func ObjectKeyListDifference(a, b []client.ObjectKey) []client.ObjectKey { + target := map[client.ObjectKey]bool{} + for _, x := range b { + target[x] = true + } + + result := make([]client.ObjectKey, 0) + for _, x := range a { + if _, ok := target[x]; !ok { + result = append(result, x) + } + } + + return result +} + +func FindDeploymentStatusCondition(conditions []appsv1.DeploymentCondition, conditionType string) *appsv1.DeploymentCondition { + for i := range conditions { + if conditions[i].Type == appsv1.DeploymentConditionType(conditionType) { + return &conditions[i] + } + } + + return nil +} diff --git a/pkg/common/k8s_utils_test.go b/pkg/common/k8s_utils_test.go new file mode 100644 index 000000000..aa01c724a --- /dev/null +++ b/pkg/common/k8s_utils_test.go @@ -0,0 +1,70 @@ +//go:build unit +// +build unit + +package common + +import ( + "testing" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func TestObjectKeyListDifference(t *testing.T) { + + key1 := client.ObjectKey{Namespace: "ns1", Name: "obj1"} + key2 := client.ObjectKey{Namespace: "ns2", Name: "obj2"} + key3 := client.ObjectKey{Namespace: "ns3", Name: "obj3"} + + testCases := []struct { + name string + a []client.ObjectKey + b []client.ObjectKey + expected []client.ObjectKey + }{ + { + "empty", + []client.ObjectKey{}, + []client.ObjectKey{}, + []client.ObjectKey{}, + }, + { + "a empty", + []client.ObjectKey{}, + []client.ObjectKey{key1}, + []client.ObjectKey{}, + }, + { + "b empty", + []client.ObjectKey{key1, key2}, + []client.ObjectKey{}, + []client.ObjectKey{key1, key2}, + }, + { + "equal", + []client.ObjectKey{key1, key2, key3}, + []client.ObjectKey{key1, key2, key3}, + []client.ObjectKey{}, + }, + { + "missing key2", + []client.ObjectKey{key1, key2, key3}, + []client.ObjectKey{key1, key3}, + []client.ObjectKey{key2}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(subT *testing.T) { + res := ObjectKeyListDifference(tc.a, tc.b) + if len(res) != len(tc.expected) { + subT.Errorf("expected len (%d), got (%d)", len(tc.expected), len(res)) + } + + for idx := range res { + if res[idx] != tc.expected[idx] { + subT.Errorf("expected object (index %d) does not match. Expected (%s), got (%s)", idx, tc.expected[idx], res[idx]) + } + } + }) + } +} diff --git a/pkg/common/yaml_decoder.go b/pkg/common/yaml_decoder.go new file mode 100644 index 000000000..7d3e8f0e0 --- /dev/null +++ b/pkg/common/yaml_decoder.go @@ -0,0 +1,53 @@ +package common + +import ( + "bytes" + "context" + "io" + "io/ioutil" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/util/yaml" +) + +type DecodeCallback = func(runtime.Object) error + +func DecodeFile(ctx context.Context, fileData []byte, scheme *runtime.Scheme, cb DecodeCallback) error { + logger := logr.FromContext(ctx) + codec := serializer.NewCodecFactory(scheme) + decoder := codec.UniversalDeserializer() + + // the maximum size used to buffer a doc 5M + buf := make([]byte, 5*1024*1024) + docDecoder := yaml.NewDocumentDecoder(ioutil.NopCloser(bytes.NewReader(fileData))) + + for { + n, err := docDecoder.Read(buf) + if err != nil { + if err == io.EOF { + break + } + return err + } + + if n == 0 { + // empty docs + continue + } + + docData := buf[:n] + obj, _, err := decoder.Decode(docData, nil, nil) + if err != nil { + logger.Info("Document decode error", "error", err) + continue + } + + err = cb(obj) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/reconcilers/base_reconciler.go b/pkg/reconcilers/base_reconciler.go new file mode 100644 index 000000000..65fb5d88d --- /dev/null +++ b/pkg/reconcilers/base_reconciler.go @@ -0,0 +1,199 @@ +/* +Copyright 2021 Red Hat, Inc. + +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 reconcilers + +import ( + "context" + "fmt" + "strings" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/kuadrant/kuadrant-operator/pkg/common" +) + +// MutateFn is a function which mutates the existing object into it's desired state. +type MutateFn func(existing, desired client.Object) (bool, error) + +func CreateOnlyMutator(existing, desired client.Object) (bool, error) { + return false, nil +} + +type BaseReconciler struct { + client client.Client + scheme *runtime.Scheme + apiClientReader client.Reader + logger logr.Logger + recorder record.EventRecorder +} + +// blank assignment to verify that BaseReconciler implements reconcile.Reconciler +var _ reconcile.Reconciler = &BaseReconciler{} + +func NewBaseReconciler( + client client.Client, scheme *runtime.Scheme, apiClientReader client.Reader, + logger logr.Logger, recorder record.EventRecorder) *BaseReconciler { + return &BaseReconciler{ + client: client, + scheme: scheme, + apiClientReader: apiClientReader, + logger: logger, + recorder: recorder, + } +} + +func (b *BaseReconciler) Reconcile(context.Context, ctrl.Request) (ctrl.Result, error) { + return reconcile.Result{}, nil +} + +// Client returns a split client that reads objects from +// the cache and writes to the Kubernetes APIServer +func (b *BaseReconciler) Client() client.Client { + return b.client +} + +// APIClientReader return a client that directly reads objects +// from the Kubernetes APIServer +func (b *BaseReconciler) APIClientReader() client.Reader { + return b.apiClientReader +} + +func (b *BaseReconciler) Scheme() *runtime.Scheme { + return b.scheme +} + +func (b *BaseReconciler) Logger() logr.Logger { + return b.logger +} + +func (b *BaseReconciler) EventRecorder() record.EventRecorder { + return b.recorder +} + +// ReconcileResource attempts to mutate the existing state +// in order to match the desired state. The object's desired state must be reconciled +// with the existing state inside the passed in callback MutateFn. +// +// obj: Object of the same type as the 'desired' object. +// Used to read the resource from the kubernetes cluster. +// Could be zero-valued initialized object. +// desired: Object representing the desired state +// +// It returns an error. +func (b *BaseReconciler) ReconcileResource(ctx context.Context, obj, desired client.Object, mutateFn MutateFn) error { + key := client.ObjectKeyFromObject(desired) + + if err := b.Client().Get(ctx, key, obj); err != nil { + if !errors.IsNotFound(err) { + return err + } + + // Not found + if !common.IsObjectTaggedToDelete(desired) { + return b.CreateResource(ctx, desired) + } + + // Marked for deletion and not found. Nothing to do. + return nil + } + + // item found successfully + if common.IsObjectTaggedToDelete(desired) { + return b.DeleteResource(ctx, desired) + } + + update, err := mutateFn(obj, desired) + if err != nil { + return err + } + + if update { + return b.UpdateResource(ctx, obj) + } + + return nil +} + +func (b *BaseReconciler) GetResource(ctx context.Context, objKey types.NamespacedName, obj client.Object) error { + logger := logr.FromContext(ctx) + logger.Info("get object", "kind", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), "name", objKey.Name, "namespace", objKey.Namespace) + return b.Client().Get(ctx, objKey, obj) +} + +func (b *BaseReconciler) CreateResource(ctx context.Context, obj client.Object) error { + logger := logr.FromContext(ctx) + logger.Info("create object", "kind", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), "name", obj.GetName(), "namespace", obj.GetNamespace()) + return b.Client().Create(ctx, obj) +} + +func (b *BaseReconciler) UpdateResource(ctx context.Context, obj client.Object) error { + logger := logr.FromContext(ctx) + logger.Info("update object", "kind", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), "name", obj.GetName(), "namespace", obj.GetNamespace()) + return b.Client().Update(ctx, obj) +} + +func (b *BaseReconciler) DeleteResource(ctx context.Context, obj client.Object, options ...client.DeleteOption) error { + logger := logr.FromContext(ctx) + logger.Info("delete object", "kind", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), "name", obj.GetName(), "namespace", obj.GetNamespace()) + return b.Client().Delete(ctx, obj, options...) +} + +func (b *BaseReconciler) UpdateResourceStatus(ctx context.Context, obj client.Object) error { + logger := logr.FromContext(ctx) + logger.Info("update object status", "kind", strings.Replace(fmt.Sprintf("%T", obj), "*", "", 1), "name", obj.GetName(), "namespace", obj.GetNamespace()) + return b.Client().Status().Update(ctx, obj) +} + +//SetOwnerReference sets owner as a Controller OwnerReference on owned +func (b *BaseReconciler) SetOwnerReference(owner, obj client.Object) error { + err := controllerutil.SetControllerReference(owner, obj, b.Scheme()) + if err != nil { + b.Logger().Error(err, "Error setting OwnerReference on object", + "Kind", obj.GetObjectKind().GroupVersionKind().String(), + "Namespace", obj.GetNamespace(), + "Name", obj.GetName(), + ) + } + return err +} + +//EnsureOwnerReference sets owner as a Controller OwnerReference on owned +// returns boolean to notify when the object has been updated +func (b *BaseReconciler) EnsureOwnerReference(owner, obj client.Object) (bool, error) { + changed := false + + originalSize := len(obj.GetOwnerReferences()) + err := b.SetOwnerReference(owner, obj) + if err != nil { + return false, err + } + + newSize := len(obj.GetOwnerReferences()) + if originalSize != newSize { + changed = true + } + + return changed, nil +} diff --git a/pkg/reconcilers/base_reconciler_test.go b/pkg/reconcilers/base_reconciler_test.go new file mode 100644 index 000000000..290a10d8f --- /dev/null +++ b/pkg/reconcilers/base_reconciler_test.go @@ -0,0 +1,265 @@ +//go:build unit +// +build unit + +/* +Copyright 2021 Red Hat, Inc. + +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 reconcilers + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/go-logr/logr" + . "github.com/onsi/ginkgo" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/kuadrant/kuadrant-operator/pkg/common" + "github.com/kuadrant/kuadrant-operator/pkg/log" +) + +func TestMain(m *testing.M) { + logger := log.NewLogger( + log.SetLevel(log.DebugLevel), + log.SetMode(log.ModeDev), + log.WriteTo(GinkgoWriter), + ).WithName("reconcilers_test") + log.SetLogger(logger) + os.Exit(m.Run()) +} + +func TestCreateOnlyMutator(t *testing.T) { + desired := &v1.ConfigMap{} + existing := &v1.ConfigMap{} + if changed, err := CreateOnlyMutator(desired, existing); changed || err != nil { + t.Fatal("Create only mutator returned error or changed") + } +} + +func TestBaseReconcilerCreate(t *testing.T) { + var ( + namespace = "operator-unittest" + ) + baseCtx := context.Background() + ctx := logr.NewContext(baseCtx, log.Log) + + s := scheme.Scheme + err := appsv1.AddToScheme(s) + if err != nil { + t.Fatal(err) + } + + // Objects to track in the fake client. + objs := []runtime.Object{} + + // Create a fake client to mock API calls. + cl := fake.NewFakeClient(objs...) + clientAPIReader := fake.NewFakeClient(objs...) + recorder := record.NewFakeRecorder(10000) + + baseReconciler := NewBaseReconciler(cl, s, clientAPIReader, log.Log, recorder) + + desiredConfigmap := &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "myConfigmap", + Namespace: namespace, + }, + Data: map[string]string{ + "somekey": "somevalue", + }, + } + + err = baseReconciler.ReconcileResource(ctx, &v1.ConfigMap{}, desiredConfigmap, CreateOnlyMutator) + if err != nil { + t.Fatal(err) + } + + reconciledConfigmap := &v1.ConfigMap{} + objectKey := client.ObjectKeyFromObject(desiredConfigmap) + err = cl.Get(ctx, objectKey, reconciledConfigmap) + // object must exist, that is all required to be tested + if err != nil { + t.Errorf("error fetching existing: %v", err) + } +} + +func TestBaseReconcilerUpdateNeeded(t *testing.T) { + // Test that update is done when mutator tells + var ( + name = "myConfigmap" + namespace = "operator-unittest" + ) + baseCtx := context.Background() + ctx := logr.NewContext(baseCtx, log.Log) + + s := runtime.NewScheme() + err := appsv1.AddToScheme(s) + if err != nil { + t.Fatal(err) + } + + existingConfigmap := &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Data: map[string]string{ + "somekey": "somevalue", + }, + } + + // Objects to track in the fake client. + objs := []runtime.Object{existingConfigmap} + + // Create a fake client to mock API calls. + cl := fake.NewFakeClient(objs...) + clientAPIReader := fake.NewFakeClient(objs...) + recorder := record.NewFakeRecorder(10000) + + baseReconciler := NewBaseReconciler(cl, s, clientAPIReader, log.Log, recorder) + + desiredConfigmap := &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Data: map[string]string{ + "somekey": "somevalue", + }, + } + + customMutator := func(existingObj, desiredObj client.Object) (bool, error) { + existing, ok := existingObj.(*v1.ConfigMap) + if !ok { + return false, fmt.Errorf("%T is not a *v1.ConfigMap", existingObj) + } + if existing.Data == nil { + existing.Data = map[string]string{} + } + existing.Data["customKey"] = "customValue" + return true, nil + } + + err = baseReconciler.ReconcileResource(ctx, &v1.ConfigMap{}, desiredConfigmap, customMutator) + if err != nil { + t.Fatal(err) + } + + reconciled := &v1.ConfigMap{} + objectKey := client.ObjectKeyFromObject(desiredConfigmap) + err = cl.Get(ctx, objectKey, reconciled) + if err != nil { + t.Fatalf("error fetching reconciled: %v", err) + } + + customValue, ok := reconciled.Data["customKey"] + if !ok { + t.Fatal("reconciled does not have reconciled data") + } + + if customValue != "customValue" { + t.Fatalf("reconciled have reconciled data. Expected: 'customValue', got: %s", customValue) + } +} + +func TestBaseReconcilerDelete(t *testing.T) { + var ( + resourceName = "example-resource" + namespace = "operator-unittest" + ) + baseCtx := context.Background() + ctx := logr.NewContext(baseCtx, log.Log) + + s := runtime.NewScheme() + err := appsv1.AddToScheme(s) + if err != nil { + t.Fatal(err) + } + + existing := &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Data: map[string]string{ + "somekey": "somevalue", + }, + } + + // Objects to track in the fake client. + objs := []runtime.Object{existing} + + // Create a fake client to mock API calls. + cl := fake.NewFakeClient(objs...) + clientAPIReader := fake.NewFakeClient(objs...) + recorder := record.NewFakeRecorder(10000) + + baseReconciler := NewBaseReconciler(cl, s, clientAPIReader, log.Log, recorder) + + desired := &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Data: map[string]string{ + "somekey": "somevalue", + }, + } + common.TagObjectToDelete(desired) + + err = baseReconciler.ReconcileResource(ctx, &v1.ConfigMap{}, desired, CreateOnlyMutator) + if err != nil { + t.Fatal(err) + } + + objectKey := client.ObjectKeyFromObject(desired) + reconciled := &v1.ConfigMap{} + err = cl.Get(ctx, objectKey, reconciled) + // object should not exist, that is all required to be tested + if !errors.IsNotFound(err) { + t.Fatal(err) + } +}