diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 6800d6df..13df8200 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -66,6 +66,13 @@ jobs: - name: Test run: ARCH=${{ matrix.name }} make test + - name: Test Summary + uses: test-summary/action@032c8a9cec6aaa3c20228112cae6ca10a3b29336 # v2.3 + with: + paths: | + **/*-test-report.xml + if: always() + statics: name: Static Checks runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 795b0735..9c671770 100644 --- a/.gitignore +++ b/.gitignore @@ -362,3 +362,6 @@ admin/Cargo.lock *.csr *.srl *.ext + +# test results +*-test-report.xml diff --git a/.golangci.yml b/.golangci.yml index d60126d7..31c80ebd 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -52,6 +52,10 @@ issues: - dupl - unparam - revive + - path: tests + linters: + - stylecheck + - revive # Exclude gci check for //+kubebuilder:scaffold:imports comments. Waiting to # resolve https://github.com/kedacore/keda/issues/4379 - path: operator/controllers/http/suite_test.go diff --git a/Makefile b/Makefile index 8cc5f5a0..222bca4d 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,8 @@ GIT_COMMIT ?= $(shell git rev-list -1 HEAD) GIT_COMMIT_SHORT ?= $(shell git rev-parse --short HEAD) COSIGN_FLAGS ?= -y -a GIT_HASH=${GIT_COMMIT} -a GIT_VERSION=${VERSION} -a BUILD_DATE=${DATE} +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.30 define DOMAINS basicConstraints=CA:FALSE @@ -73,13 +75,19 @@ clean-test-certs: rm -r certs || true # Test targets -test: fmt vet test-certs - go test ./... -e2e-test: +.PHONY: install-test-deps +install-test-deps: + go install github.com/jstemmer/go-junit-report/v2@latest + go install gotest.tools/gotestsum@latest + +test: fmt vet test-certs install-test-deps envtest + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" gotestsum --junitfile unit-test-report.xml + +e2e-test: install-test-deps go run -tags e2e ./tests/run-all.go -e2e-test-local: +e2e-test-local: install-test-deps SKIP_SETUP=true go run -tags e2e ./tests/run-all.go # Docker targets @@ -170,6 +178,10 @@ lint: ## Run golangci-lint against code. pre-commit: ## Run static-checks. pre-commit run --all-files +ENVTEST = $(shell pwd)/bin/setup-envtest +envtest: ## Install envtest-setup if necessary. + GOBIN=$(shell pwd)/bin go install sigs.k8s.io/controller-runtime/tools/setup-envtest + CONTROLLER_GEN = $(shell pwd)/bin/controller-gen controller-gen: ## Download controller-gen locally if necessary. GOBIN=$(shell pwd)/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.15.0 diff --git a/config/crd/bases/http.keda.sh_clusterhttpscalingsets.yaml b/config/crd/bases/http.keda.sh_clusterhttpscalingsets.yaml new file mode 100644 index 00000000..0866f59c --- /dev/null +++ b/config/crd/bases/http.keda.sh_clusterhttpscalingsets.yaml @@ -0,0 +1,361 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: clusterhttpscalingsets.http.keda.sh +spec: + group: http.keda.sh + names: + kind: ClusterHTTPScalingSet + listKind: ClusterHTTPScalingSetList + plural: clusterhttpscalingsets + shortNames: + - css + singular: clusterhttpscalingset + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterHTTPScalingSet is the Schema for the cluster httpscalingset + 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: HTTPScalingSetSpec defines the desired state of HTTPScalingSet + properties: + interceptor: + description: HTTPInterceptorSpec defines the desired state of Interceptor + component + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + type: object + autoscaling: + description: Autoscaling options for the interceptor + properties: + maxReplicas: + default: 100 + description: Maximum replicas for the interceptor + type: integer + minReplicas: + default: 3 + description: Minimum replicas for the interceptor + type: integer + target: + default: 100 + description: Target concurrent requests + type: integer + type: object + config: + description: Traffic configuration + properties: + adminPort: + description: Port to be used for admin operations + format: int32 + type: integer + connectTimeout: + description: Timeout for establishing the connection + type: string + expectContinueTimeout: + description: |- + Max amount of time the interceptor will wait + after sending request headers if the server returned an Expect: 100-continue + header + type: string + forceHTTP2: + description: Try to force HTTP2 for all requests + type: boolean + handshakeTimeout: + description: |- + Max amount of time the interceptor will + wait to establish a TLS connection + type: string + headerTimeout: + description: |- + How long to wait between when the HTTP request + is sent to the backing app and when response headers need to arrive + type: string + idleConnTimeout: + description: |- + Timeout after which a connection in the interceptor's + internal connection pool will be closed + type: string + keepAlive: + description: Interval between keepalive probes + type: string + maxIdleConnections: + description: |- + Max number of connections that can be idle in the + interceptor's internal connection pool + type: integer + pollingInterval: + description: |- + The interceptor has an internal process that periodically fetches the state + of endpoints that is running the servers it forwards to. + This is the interval (in milliseconds) representing how often to do a fetch + type: integer + proxyPort: + description: Port to be used for proxy operations + format: int32 + type: integer + waitTimeout: + description: |- + How long to wait for the backing workload + to have 1 or more replicas before connecting and sending the HTTP request. + type: string + type: object + image: + description: Container image name. + type: string + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + type: object + replicas: + description: Number of replicas for the interceptor + format: int32 + type: integer + resources: + description: |- + Compute Resources required by this interceptor. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + serviceAccountName: + default: default + description: Name of the service account to be used + type: string + type: object + scaler: + description: HTTPScalerSpec defines the desired state of Scaler component + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + type: object + config: + default: {} + description: Traffic configuration + properties: + port: + default: 9090 + description: Port to be used for proxy operations + format: int32 + type: integer + type: object + image: + description: Container image name. + type: string + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + type: object + replicas: + description: Number of replicas for the interceptor + format: int32 + type: integer + resources: + description: |- + Compute Resources required by this scaler. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + serviceAccountName: + default: default + description: Name of the service account to be used + type: string + type: object + required: + - interceptor + - scaler + type: object + status: + description: HTTPScalingSetStatus defines the observed state of HTTPScalingSet + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/http.keda.sh_httpscaledobjects.yaml b/config/crd/bases/http.keda.sh_httpscaledobjects.yaml index 2c48f3ef..a45f7df3 100644 --- a/config/crd/bases/http.keda.sh_httpscaledobjects.yaml +++ b/config/crd/bases/http.keda.sh_httpscaledobjects.yaml @@ -147,6 +147,22 @@ spec: type: string type: object type: object + scalingSet: + description: |- + ScalingSet to be used for this HTTPScaledObject, if empty, default + interceptor and scaler will be used + properties: + kind: + description: Kind of the resource being referred to. Defaults + to HTTPScalingSet. + enum: + - HTTPScalingSet + - ClusterHTTPScalingSet + type: string + name: + description: Name of the scaling set + type: string + type: object targetPendingRequests: description: (optional) DEPRECATED (use ScalingMetric instead) Target metric value diff --git a/config/crd/bases/http.keda.sh_httpscalingsets.yaml b/config/crd/bases/http.keda.sh_httpscalingsets.yaml new file mode 100644 index 00000000..713fc766 --- /dev/null +++ b/config/crd/bases/http.keda.sh_httpscalingsets.yaml @@ -0,0 +1,360 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: httpscalingsets.http.keda.sh +spec: + group: http.keda.sh + names: + kind: HTTPScalingSet + listKind: HTTPScalingSetList + plural: httpscalingsets + shortNames: + - ss + singular: httpscalingset + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: HTTPScalingSet is the Schema for the httpscalingset 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: HTTPScalingSetSpec defines the desired state of HTTPScalingSet + properties: + interceptor: + description: HTTPInterceptorSpec defines the desired state of Interceptor + component + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + type: object + autoscaling: + description: Autoscaling options for the interceptor + properties: + maxReplicas: + default: 100 + description: Maximum replicas for the interceptor + type: integer + minReplicas: + default: 3 + description: Minimum replicas for the interceptor + type: integer + target: + default: 100 + description: Target concurrent requests + type: integer + type: object + config: + description: Traffic configuration + properties: + adminPort: + description: Port to be used for admin operations + format: int32 + type: integer + connectTimeout: + description: Timeout for establishing the connection + type: string + expectContinueTimeout: + description: |- + Max amount of time the interceptor will wait + after sending request headers if the server returned an Expect: 100-continue + header + type: string + forceHTTP2: + description: Try to force HTTP2 for all requests + type: boolean + handshakeTimeout: + description: |- + Max amount of time the interceptor will + wait to establish a TLS connection + type: string + headerTimeout: + description: |- + How long to wait between when the HTTP request + is sent to the backing app and when response headers need to arrive + type: string + idleConnTimeout: + description: |- + Timeout after which a connection in the interceptor's + internal connection pool will be closed + type: string + keepAlive: + description: Interval between keepalive probes + type: string + maxIdleConnections: + description: |- + Max number of connections that can be idle in the + interceptor's internal connection pool + type: integer + pollingInterval: + description: |- + The interceptor has an internal process that periodically fetches the state + of endpoints that is running the servers it forwards to. + This is the interval (in milliseconds) representing how often to do a fetch + type: integer + proxyPort: + description: Port to be used for proxy operations + format: int32 + type: integer + waitTimeout: + description: |- + How long to wait for the backing workload + to have 1 or more replicas before connecting and sending the HTTP request. + type: string + type: object + image: + description: Container image name. + type: string + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + type: object + replicas: + description: Number of replicas for the interceptor + format: int32 + type: integer + resources: + description: |- + Compute Resources required by this interceptor. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + serviceAccountName: + default: default + description: Name of the service account to be used + type: string + type: object + scaler: + description: HTTPScalerSpec defines the desired state of Scaler component + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + type: object + config: + default: {} + description: Traffic configuration + properties: + port: + default: 9090 + description: Port to be used for proxy operations + format: int32 + type: integer + type: object + image: + description: Container image name. + type: string + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + type: object + replicas: + description: Number of replicas for the interceptor + format: int32 + type: integer + resources: + description: |- + Compute Resources required by this scaler. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + serviceAccountName: + default: default + description: Name of the service account to be used + type: string + type: object + required: + - interceptor + - scaler + type: object + status: + description: HTTPScalingSetStatus defines the observed state of HTTPScalingSet + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index a90eb961..6e3c22b8 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -2,4 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - bases/http.keda.sh_httpscaledobjects.yaml +- bases/http.keda.sh_clusterhttpscalingsets.yaml +- bases/http.keda.sh_httpscalingsets.yaml #+kubebuilder:scaffold:crdkustomizeresource diff --git a/config/operator/role.yaml b/config/operator/role.yaml index f89c4d39..6c8f2a06 100644 --- a/config/operator/role.yaml +++ b/config/operator/role.yaml @@ -4,6 +4,56 @@ kind: ClusterRole metadata: name: operator rules: +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - http.keda.sh + resources: + - clusterhttpscalingsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - http.keda.sh + resources: + - clusterhttpscalingsets/finalizers + verbs: + - update +- apiGroups: + - http.keda.sh + resources: + - clusterhttpscalingsets/status + verbs: + - get + - patch + - update - apiGroups: - http.keda.sh resources: @@ -30,6 +80,32 @@ rules: - get - patch - update +- apiGroups: + - http.keda.sh + resources: + - httpscalingsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - http.keda.sh + resources: + - httpscalingsets/finalizers + verbs: + - update +- apiGroups: + - http.keda.sh + resources: + - httpscalingsets/status + verbs: + - get + - patch + - update - apiGroups: - keda.sh resources: diff --git a/config/scaler/deployment.yaml b/config/scaler/deployment.yaml index 5527c49a..e7b3535a 100644 --- a/config/scaler/deployment.yaml +++ b/config/scaler/deployment.yaml @@ -28,8 +28,6 @@ spec: - --zap-encoder=console - --zap-time-encoding=rfc3339 env: - - name: KEDA_HTTP_SCALER_TARGET_ADMIN_DEPLOYMENT - value: "keda-http-add-on-interceptor" - name: KEDA_HTTP_SCALER_PORT value: "9090" - name: KEDA_HTTP_SCALER_TARGET_ADMIN_NAMESPACE diff --git a/go.mod b/go.mod index a5817308..ad7541a7 100644 --- a/go.mod +++ b/go.mod @@ -86,6 +86,7 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.53.0 github.com/prometheus/procfs v0.14.0 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xlab/treeprint v1.2.0 // indirect @@ -97,7 +98,7 @@ require ( go.starlark.net v0.0.0-20231121155337-90ade8b19d09 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect @@ -119,6 +120,7 @@ require ( k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect knative.dev/pkg v0.0.0-20240423132823-3c6badc82748 // indirect + sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240507051437-479b723944e3 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect sigs.k8s.io/kustomize/cmd/config v0.14.1 // indirect diff --git a/go.sum b/go.sum index 81219840..e68b474c 100644 --- a/go.sum +++ b/go.sum @@ -132,6 +132,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -268,6 +270,8 @@ knative.dev/pkg v0.0.0-20240423132823-3c6badc82748 h1:0X8ZtnOZqGPjauVLLvOyMaBOMX knative.dev/pkg v0.0.0-20240423132823-3c6badc82748/go.mod h1:Y/ufiCvMogYcpDwZJPcTRBYeBo57RaEQhY0Lq/9RKmU= sigs.k8s.io/controller-runtime v0.17.5 h1:1FI9Lm7NiOOmBsgTV36/s2XrEFXnO2C4sbg/Zme72Rw= sigs.k8s.io/controller-runtime v0.17.5/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY= +sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240507051437-479b723944e3 h1:+PIKzO0XQcRSgch0H+DQaW+C8DzSizvkcVL0XxcjoZA= +sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240507051437-479b723944e3/go.mod h1:B2xTzIWVko5xZLWDkXFS7Zo9hxX+ecdMzZ8oiTPJoRI= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= diff --git a/hack/tools.go b/hack/tools.go index f8a8742a..496f1312 100644 --- a/hack/tools.go +++ b/hack/tools.go @@ -24,5 +24,6 @@ package hack import ( _ "go.uber.org/mock/mockgen" _ "k8s.io/code-generator" + _ "sigs.k8s.io/controller-runtime/tools/setup-envtest" _ "sigs.k8s.io/kustomize/kustomize/v5" ) diff --git a/operator/apis/http/v1alpha1/httpscaledobject_types.go b/operator/apis/http/v1alpha1/httpscaledobject_types.go index 0b2b039b..beb8ce13 100644 --- a/operator/apis/http/v1alpha1/httpscaledobject_types.go +++ b/operator/apis/http/v1alpha1/httpscaledobject_types.go @@ -20,6 +20,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +type ScalingSetKind string + +const ( + HTTPScalingSetKind ScalingSetKind = "HTTPScalingSet" + ClusterHTTPScalingSetKind ScalingSetKind = "ClusterHTTPScalingSet" +) + // ScaleTargetRef contains all the details about an HTTP application to scale and route to type ScaleTargetRef struct { // +optional @@ -74,8 +81,36 @@ type RateMetricSpec struct { Granularity metav1.Duration `json:"granularity" description:"Time granularity for rate calculation"` } +// HTTPSalingSetTargetRef defines the desired scaling set to be used +type HTTPSalingSetTargetRef struct { + // Name of the scaling set + Name string `json:"name,omitempty"` + // Kind of the resource being referred to. Defaults to HTTPScalingSet. + // +kubebuilder:validation:Enum=HTTPScalingSet;ClusterHTTPScalingSet + // +optional + Kind ScalingSetKind `json:"kind,omitempty"` +} + +func (so *HTTPScaledObjectSpec) GetHTTPSalingSetTargetRef() HTTPSalingSetTargetRef { + r := HTTPSalingSetTargetRef{} + if so.ScalingSet == nil { + return r + } + + r.Name = so.ScalingSet.Name + r.Kind = ClusterHTTPScalingSetKind + if so.ScalingSet.Kind != "" { + r.Kind = so.ScalingSet.Kind + } + return r +} + // HTTPScaledObjectSpec defines the desired state of HTTPScaledObject type HTTPScaledObjectSpec struct { + // ScalingSet to be used for this HTTPScaledObject, if empty, default + // interceptor and scaler will be used + // +optional + ScalingSet *HTTPSalingSetTargetRef `json:"scalingSet,omitempty"` // The hosts to route. All requests which the "Host" header // matches any .spec.hosts (and the Request Target matches any // .spec.pathPrefixes) will be routed to the Service and Port specified in diff --git a/operator/apis/http/v1alpha1/httpscalingset_defaults.go b/operator/apis/http/v1alpha1/httpscalingset_defaults.go new file mode 100644 index 00000000..beb270ad --- /dev/null +++ b/operator/apis/http/v1alpha1/httpscalingset_defaults.go @@ -0,0 +1,151 @@ +/* +Copyright 2024 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + + "github.com/kedacore/http-add-on/pkg/build" +) + +var ( + interceptorImage = fmt.Sprintf("ghcr.io/kedacore/http-add-on-interceptor:%s", build.Version()) + scalerImage = fmt.Sprintf("ghcr.io/kedacore/http-add-on-scaler:%s", build.Version()) +) + +func (c *HTTPInterceptorSpec) GetProxyPort() int32 { + if c.Config == nil || c.Config.ProxyPort == nil { + return 8080 + } + return *c.Config.ProxyPort +} + +func (c *HTTPInterceptorSpec) GetAdminPort() int32 { + if c.Config == nil || c.Config.AdminPort == nil { + return 9090 + } + return *c.Config.AdminPort +} +func (c *HTTPInterceptorSpec) GetConnectTimeout() string { + if c.Config == nil || c.Config.ConnectTimeout == nil { + return "500ms" + } + return *c.Config.ConnectTimeout +} +func (c *HTTPInterceptorSpec) GetHeaderTimeout() string { + if c.Config == nil || c.Config.HeaderTimeout == nil { + return "500ms" + } + return *c.Config.HeaderTimeout +} +func (c *HTTPInterceptorSpec) GetWaitTimeout() string { + if c.Config == nil || c.Config.WaitTimeout == nil { + return "1500ms" + } + return *c.Config.WaitTimeout +} +func (c *HTTPInterceptorSpec) GetIdleConnTimeout() string { + if c.Config == nil || c.Config.IdleConnTimeout == nil { + return "90s" + } + return *c.Config.IdleConnTimeout +} +func (c *HTTPInterceptorSpec) GetTLSHandshakeTimeout() string { + if c.Config == nil || c.Config.TLSHandshakeTimeout == nil { + return "10s" + } + return *c.Config.TLSHandshakeTimeout +} +func (c *HTTPInterceptorSpec) GetExpectContinueTimeout() string { + if c.Config == nil || c.Config.ExpectContinueTimeout == nil { + return "1s" + } + return *c.Config.ExpectContinueTimeout +} +func (c *HTTPInterceptorSpec) GetForceHTTP2() bool { + if c.Config == nil || c.Config.ForceHTTP2 == nil { + return false + } + return *c.Config.ForceHTTP2 +} +func (c *HTTPInterceptorSpec) GetKeepAlive() string { + if c.Config == nil || c.Config.KeepAlive == nil { + return "1s" + } + return *c.Config.KeepAlive +} +func (c *HTTPInterceptorSpec) GetMaxIdleConns() int { + if c.Config == nil || c.Config.MaxIdleConns == nil { + return 100 + } + return *c.Config.MaxIdleConns +} +func (c *HTTPInterceptorSpec) GetPollingInterval() int { + if c.Config == nil || c.Config.PollingInterval == nil { + return 1000 + } + return *c.Config.PollingInterval +} + +func (c *HTTPInterceptorSpec) GetImage() string { + if c.Image == nil { + return interceptorImage + } + return *c.Image +} + +func (c *HTTPInterceptorSpec) GetLabels() map[string]string { + if c.Labels == nil { + return map[string]string{} + } + return c.Labels +} + +func (c *HTTPInterceptorSpec) GetAnnotations() map[string]string { + if c.Annotations == nil { + return map[string]string{} + } + return c.Annotations +} + +func (c *HTTPScalerSpec) GetPort() int32 { + if c.Config.Port == nil { + return 9090 + } + return *c.Config.Port +} + +func (c *HTTPScalerSpec) GetImage() string { + if c.Image == nil { + return scalerImage + } + return *c.Image +} + +func (c *HTTPScalerSpec) GetLabels() map[string]string { + if c.Labels == nil { + return map[string]string{} + } + return c.Labels +} + +func (c *HTTPScalerSpec) GetAnnotations() map[string]string { + if c.Annotations == nil { + return map[string]string{} + } + return c.Annotations +} diff --git a/operator/apis/http/v1alpha1/httpscalingset_types.go b/operator/apis/http/v1alpha1/httpscalingset_types.go new file mode 100644 index 00000000..8ec8aba8 --- /dev/null +++ b/operator/apis/http/v1alpha1/httpscalingset_types.go @@ -0,0 +1,234 @@ +/* +Copyright 2024 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// HTTPInterceptorScalingSpec defines the desired state of Interceptor autoscaling +type HTTPInterceptorScalingSpec struct { + // +kubebuilder:default=3 + // +optional + // Minimum replicas for the interceptor + MinReplicas int `json:"minReplicas"` + // +kubebuilder:default=100 + // +optional + // Maximum replicas for the interceptor + MaxReplicas int `json:"maxReplicas"` + // +kubebuilder:default=100 + // +optional + // Target concurrent requests + Target int `json:"target"` +} + +// HTTPInterceptorConfigurationSpec defines the desired state of Interceptor configuration +type HTTPInterceptorConfigurationSpec struct { + // +optional + // Port to be used for proxy operations + ProxyPort *int32 `json:"proxyPort,omitempty"` + // +optional + // Port to be used for admin operations + AdminPort *int32 `json:"adminPort,omitempty"` + // +optional + // Timeout for establishing the connection + ConnectTimeout *string `json:"connectTimeout,omitempty"` + // +optional + // How long to wait between when the HTTP request + // is sent to the backing app and when response headers need to arrive + HeaderTimeout *string `json:"headerTimeout,omitempty"` + // +optional + // How long to wait for the backing workload + // to have 1 or more replicas before connecting and sending the HTTP request. + WaitTimeout *string `json:"waitTimeout,omitempty"` + // +optional + // Timeout after which a connection in the interceptor's + // internal connection pool will be closed + IdleConnTimeout *string `json:"idleConnTimeout,omitempty"` + // +optional + // Max amount of time the interceptor will + // wait to establish a TLS connection + TLSHandshakeTimeout *string `json:"handshakeTimeout,omitempty"` + // +optional + // Max amount of time the interceptor will wait + // after sending request headers if the server returned an Expect: 100-continue + // header + ExpectContinueTimeout *string `json:"expectContinueTimeout,omitempty"` + // +optional + // Try to force HTTP2 for all requests + ForceHTTP2 *bool `json:"forceHTTP2,omitempty"` + // +optional + // Interval between keepalive probes + KeepAlive *string `json:"keepAlive,omitempty"` + // +optional + // Max number of connections that can be idle in the + // interceptor's internal connection pool + MaxIdleConns *int `json:"maxIdleConnections,omitempty"` + // +optional + // The interceptor has an internal process that periodically fetches the state + // of endpoints that is running the servers it forwards to. + // This is the interval (in milliseconds) representing how often to do a fetch + PollingInterval *int `json:"pollingInterval,omitempty"` +} + +// HTTPInterceptorSpec defines the desired state of Interceptor component +type HTTPInterceptorSpec struct { + // +optional + // Traffic configuration + Config *HTTPInterceptorConfigurationSpec `json:"config,omitempty"` + // Number of replicas for the interceptor + Replicas *int32 `json:"replicas,omitempty"` + // Container image name. + // +optional + Image *string `json:"image,omitempty"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. + // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // Autoscaling options for the interceptor + Autoscaling *HTTPInterceptorScalingSpec `json:"autoscaling,omitempty"` + // Compute Resources required by this interceptor. + // Cannot be updated. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + // Map of string keys and values that can be used to organize and categorize + // (scope and select) objects. May match selectors of replication controllers + // and services. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be + // set by external tools to store and retrieve arbitrary metadata. They are not + // queryable and should be preserved when modifying objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // +kubebuilder:default=default + // +optional + // Name of the service account to be used + ServiceAccountName string `json:"serviceAccountName"` +} + +// HTTPScalerConfigurationSpec defines the desired state of scaler configuration +type HTTPScalerConfigurationSpec struct { + // +kubebuilder:default=9090 + // +optional + // Port to be used for proxy operations + Port *int32 `json:"port,omitempty"` +} + +// HTTPScalerSpec defines the desired state of Scaler component +type HTTPScalerSpec struct { + // +kubebuilder:default={} + // +optional + // Traffic configuration + Config HTTPScalerConfigurationSpec `json:"config,omitempty"` + // Number of replicas for the interceptor + Replicas *int32 `json:"replicas,omitempty"` + // Container image name. + // +optional + Image *string `json:"image,omitempty"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + // If specified, these secrets will be passed to individual puller implementations for them to use. + // More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // Compute Resources required by this scaler. + // Cannot be updated. + // More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + // Map of string keys and values that can be used to organize and categorize + // (scope and select) objects. May match selectors of replication controllers + // and services. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Annotations is an unstructured key value map stored with a resource that may be + // set by external tools to store and retrieve arbitrary metadata. They are not + // queryable and should be preserved when modifying objects. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // +kubebuilder:default=default + // +optional + // Name of the service account to be used + ServiceAccountName string `json:"serviceAccountName"` +} + +// HTTPScalingSetSpec defines the desired state of HTTPScalingSet +type HTTPScalingSetSpec struct { + Interceptor HTTPInterceptorSpec `json:"interceptor"` + Scaler HTTPScalerSpec `json:"scaler"` +} + +// HTTPScalingSetStatus defines the observed state of HTTPScalingSet +type HTTPScalingSetStatus struct{} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=ss +// +kubebuilder:subresource:status + +// HTTPScalingSet is the Schema for the httpscalingset API +type HTTPScalingSet struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HTTPScalingSetSpec `json:"spec,omitempty"` + Status HTTPScalingSetStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// HTTPScalingSetList contains a list of HTTPScalingSetList +type HTTPScalingSetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []HTTPScalingSet `json:"items"` +} + +// +genclient +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=css,scope=Cluster +// +kubebuilder:subresource:status + +// ClusterHTTPScalingSet is the Schema for the cluster httpscalingset API +type ClusterHTTPScalingSet struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HTTPScalingSetSpec `json:"spec,omitempty"` + Status HTTPScalingSetStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// ClusterHTTPScalingSetList contains a list of ClusterHTTPScalingSet +type ClusterHTTPScalingSetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterHTTPScalingSet `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ClusterHTTPScalingSet{}, &ClusterHTTPScalingSetList{}) + SchemeBuilder.Register(&HTTPScalingSet{}, &HTTPScalingSetList{}) +} diff --git a/operator/apis/http/v1alpha1/zz_generated.deepcopy.go b/operator/apis/http/v1alpha1/zz_generated.deepcopy.go index 27d77264..3a74b2c1 100644 --- a/operator/apis/http/v1alpha1/zz_generated.deepcopy.go +++ b/operator/apis/http/v1alpha1/zz_generated.deepcopy.go @@ -21,9 +21,69 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterHTTPScalingSet) DeepCopyInto(out *ClusterHTTPScalingSet) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterHTTPScalingSet. +func (in *ClusterHTTPScalingSet) DeepCopy() *ClusterHTTPScalingSet { + if in == nil { + return nil + } + out := new(ClusterHTTPScalingSet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterHTTPScalingSet) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterHTTPScalingSetList) DeepCopyInto(out *ClusterHTTPScalingSetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterHTTPScalingSet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterHTTPScalingSetList. +func (in *ClusterHTTPScalingSetList) DeepCopy() *ClusterHTTPScalingSetList { + if in == nil { + return nil + } + out := new(ClusterHTTPScalingSetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterHTTPScalingSetList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConcurrencyMetricSpec) DeepCopyInto(out *ConcurrencyMetricSpec) { *out = *in @@ -58,6 +118,166 @@ func (in Conditions) DeepCopy() Conditions { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPInterceptorConfigurationSpec) DeepCopyInto(out *HTTPInterceptorConfigurationSpec) { + *out = *in + if in.ProxyPort != nil { + in, out := &in.ProxyPort, &out.ProxyPort + *out = new(int32) + **out = **in + } + if in.AdminPort != nil { + in, out := &in.AdminPort, &out.AdminPort + *out = new(int32) + **out = **in + } + if in.ConnectTimeout != nil { + in, out := &in.ConnectTimeout, &out.ConnectTimeout + *out = new(string) + **out = **in + } + if in.HeaderTimeout != nil { + in, out := &in.HeaderTimeout, &out.HeaderTimeout + *out = new(string) + **out = **in + } + if in.WaitTimeout != nil { + in, out := &in.WaitTimeout, &out.WaitTimeout + *out = new(string) + **out = **in + } + if in.IdleConnTimeout != nil { + in, out := &in.IdleConnTimeout, &out.IdleConnTimeout + *out = new(string) + **out = **in + } + if in.TLSHandshakeTimeout != nil { + in, out := &in.TLSHandshakeTimeout, &out.TLSHandshakeTimeout + *out = new(string) + **out = **in + } + if in.ExpectContinueTimeout != nil { + in, out := &in.ExpectContinueTimeout, &out.ExpectContinueTimeout + *out = new(string) + **out = **in + } + if in.ForceHTTP2 != nil { + in, out := &in.ForceHTTP2, &out.ForceHTTP2 + *out = new(bool) + **out = **in + } + if in.KeepAlive != nil { + in, out := &in.KeepAlive, &out.KeepAlive + *out = new(string) + **out = **in + } + if in.MaxIdleConns != nil { + in, out := &in.MaxIdleConns, &out.MaxIdleConns + *out = new(int) + **out = **in + } + if in.PollingInterval != nil { + in, out := &in.PollingInterval, &out.PollingInterval + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPInterceptorConfigurationSpec. +func (in *HTTPInterceptorConfigurationSpec) DeepCopy() *HTTPInterceptorConfigurationSpec { + if in == nil { + return nil + } + out := new(HTTPInterceptorConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPInterceptorScalingSpec) DeepCopyInto(out *HTTPInterceptorScalingSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPInterceptorScalingSpec. +func (in *HTTPInterceptorScalingSpec) DeepCopy() *HTTPInterceptorScalingSpec { + if in == nil { + return nil + } + out := new(HTTPInterceptorScalingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPInterceptorSpec) DeepCopyInto(out *HTTPInterceptorSpec) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(HTTPInterceptorConfigurationSpec) + (*in).DeepCopyInto(*out) + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + if in.Autoscaling != nil { + in, out := &in.Autoscaling, &out.Autoscaling + *out = new(HTTPInterceptorScalingSpec) + **out = **in + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPInterceptorSpec. +func (in *HTTPInterceptorSpec) DeepCopy() *HTTPInterceptorSpec { + if in == nil { + return nil + } + out := new(HTTPInterceptorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPSalingSetTargetRef) DeepCopyInto(out *HTTPSalingSetTargetRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSalingSetTargetRef. +func (in *HTTPSalingSetTargetRef) DeepCopy() *HTTPSalingSetTargetRef { + if in == nil { + return nil + } + out := new(HTTPSalingSetTargetRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPScaledObject) DeepCopyInto(out *HTTPScaledObject) { *out = *in @@ -135,6 +355,11 @@ func (in *HTTPScaledObjectList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPScaledObjectSpec) DeepCopyInto(out *HTTPScaledObjectSpec) { *out = *in + if in.ScalingSet != nil { + in, out := &in.ScalingSet, &out.ScalingSet + *out = new(HTTPSalingSetTargetRef) + **out = **in + } if in.Hosts != nil { in, out := &in.Hosts, &out.Hosts *out = make([]string, len(*in)) @@ -198,6 +423,163 @@ func (in *HTTPScaledObjectStatus) DeepCopy() *HTTPScaledObjectStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalerConfigurationSpec) DeepCopyInto(out *HTTPScalerConfigurationSpec) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalerConfigurationSpec. +func (in *HTTPScalerConfigurationSpec) DeepCopy() *HTTPScalerConfigurationSpec { + if in == nil { + return nil + } + out := new(HTTPScalerConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalerSpec) DeepCopyInto(out *HTTPScalerSpec) { + *out = *in + in.Config.DeepCopyInto(&out.Config) + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Image != nil { + in, out := &in.Image, &out.Image + *out = new(string) + **out = **in + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalerSpec. +func (in *HTTPScalerSpec) DeepCopy() *HTTPScalerSpec { + if in == nil { + return nil + } + out := new(HTTPScalerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSet) DeepCopyInto(out *HTTPScalingSet) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSet. +func (in *HTTPScalingSet) DeepCopy() *HTTPScalingSet { + if in == nil { + return nil + } + out := new(HTTPScalingSet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPScalingSet) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSetList) DeepCopyInto(out *HTTPScalingSetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]HTTPScalingSet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSetList. +func (in *HTTPScalingSetList) DeepCopy() *HTTPScalingSetList { + if in == nil { + return nil + } + out := new(HTTPScalingSetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPScalingSetList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSetSpec) DeepCopyInto(out *HTTPScalingSetSpec) { + *out = *in + in.Interceptor.DeepCopyInto(&out.Interceptor) + in.Scaler.DeepCopyInto(&out.Scaler) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSetSpec. +func (in *HTTPScalingSetSpec) DeepCopy() *HTTPScalingSetSpec { + if in == nil { + return nil + } + out := new(HTTPScalingSetSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPScalingSetStatus) DeepCopyInto(out *HTTPScalingSetStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScalingSetStatus. +func (in *HTTPScalingSetStatus) DeepCopy() *HTTPScalingSetStatus { + if in == nil { + return nil + } + out := new(HTTPScalingSetStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RateMetricSpec) DeepCopyInto(out *RateMetricSpec) { *out = *in diff --git a/operator/controllers/http/app.go b/operator/controllers/http/app.go index 2d12d296..40ebd2e7 100644 --- a/operator/controllers/http/app.go +++ b/operator/controllers/http/app.go @@ -2,6 +2,7 @@ package http import ( "context" + "fmt" "strings" "github.com/go-logr/logr" @@ -11,7 +12,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" - "github.com/kedacore/http-add-on/operator/controllers/http/config" ) var ( @@ -22,8 +22,6 @@ func (r *HTTPScaledObjectReconciler) createOrUpdateApplicationResources( ctx context.Context, logger logr.Logger, cl client.Client, - baseConfig config.Base, - externalScalerConfig config.ExternalScaler, httpso *v1alpha1.HTTPScaledObject, ) error { defer SaveStatus(context.Background(), logger, cl, httpso) @@ -68,11 +66,15 @@ func (r *HTTPScaledObjectReconciler) createOrUpdateApplicationResources( // the app deployment and the interceptor deployment. // this needs to be submitted so that KEDA will scale both the app and // interceptor + externalScalerURI, err := r.getExternalScalerURI(ctx, r.BaseConfig.CurrentNamespace, httpso) + if err != nil { + return fmt.Errorf("failed to generate the external scaler uri for HTTPScaledObject %s. %w", httpso.Name, err) + } return r.createOrUpdateScaledObject( ctx, cl, logger, - externalScalerConfig.HostName(baseConfig.CurrentNamespace), + externalScalerURI, httpso, ) } diff --git a/operator/controllers/http/clusterhttpscalingset_controller.go b/operator/controllers/http/clusterhttpscalingset_controller.go new file mode 100644 index 00000000..0464facc --- /dev/null +++ b/operator/controllers/http/clusterhttpscalingset_controller.go @@ -0,0 +1,131 @@ +/* +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 http + +import ( + "context" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + "github.com/kedacore/http-add-on/operator/controllers/util" +) + +// ClusterHTTPScalingSetReconciler reconciles a ClusterHTTPScalingSet object +// +//revive:disable-next-line:exported +//goland:noinspection GoNameStartsWithPackageName +type ClusterHTTPScalingSetReconciler struct { + client.Client + Scheme *runtime.Scheme + KEDANamespace string +} + +// +kubebuilder:rbac:groups=http.keda.sh,resources=clusterhttpscalingsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=http.keda.sh,resources=clusterhttpscalingsets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=http.keda.sh,resources=clusterhttpscalingsets/finalizers,verbs=update + +// Reconcile reconciles a newly created, deleted, or otherwise changed +// HTTPScaledObject +func (r *ClusterHTTPScalingSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx, "httpscalingset", req.NamespacedName) + logger.Info("Reconciliation start") + + httpss := &httpv1alpha1.ClusterHTTPScalingSet{} + if err := r.Client.Get(ctx, req.NamespacedName, httpss); err != nil { + if k8serrors.IsNotFound(err) { + // If the HTTPScaledObject wasn't found, it might have + // been deleted between the reconcile and the get. + // It'll automatically get garbage collected, so don't + // schedule a requeue + logger.Info("HTTPScalingSet not found, assuming it was deleted and stopping early") + return ctrl.Result{}, nil + } + // if we didn't get a not found error, log it and schedule a requeue + // with a backoff + logger.Error(err, "Getting the HTTP Scaled obj, requeueing") + return ctrl.Result{ + RequeueAfter: 500 * time.Millisecond, + }, err + } + + if httpss.GetDeletionTimestamp() != nil { + return ctrl.Result{}, finalizeScaledObject(ctx, logger, r.Client, httpss) + } + + // ensure finalizer is set on this resource + if err := ensureFinalizer(ctx, logger, r.Client, httpss); err != nil { + return ctrl.Result{}, err + } + + // we set the namespace for the new resources via ClusterHTTPScalingSet's namespace + httpss.Namespace = r.KEDANamespace + + // Create required app objects for the application defined by the CRD + err := createOrUpdateInterceptorResources(ctx, logger, r.Client, httpss, r.Scheme) + if err != nil { + return ctrl.Result{}, err + } + err = createOrUpdateExternalScalerResources( + ctx, + logger, + r.Client, + httpss, + r.Scheme, + ) + if err != nil { + return ctrl.Result{}, err + } + + // success reconciling + logger.Info("Reconcile success") + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ClusterHTTPScalingSetReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&httpv1alpha1.ClusterHTTPScalingSet{}, builder.WithPredicates( + predicate.Or( + predicate.GenerationChangedPredicate{}, + ), + )). + // Trigger a reconcile only when the Deployment spec,label or annotation changes. + // Ignore updates to Deployment status + Owns(&appsv1.Deployment{}, builder.WithPredicates( + predicate.Or( + predicate.LabelChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + util.DeploymentSpecChangedPredicate{}, + ))). + // Trigger a reconcile only when the Service spec,label or annotation changes. + // Ignore updates to Service status + Owns(&corev1.Service{}, builder.WithPredicates( + predicate.Or( + predicate.LabelChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + util.ServiceSpecChangedPredicate{}, + ))). + Complete(r) +} diff --git a/operator/controllers/http/clusterhttpscalingset_controller_test.go b/operator/controllers/http/clusterhttpscalingset_controller_test.go new file mode 100644 index 00000000..d5377a96 --- /dev/null +++ b/operator/controllers/http/clusterhttpscalingset_controller_test.go @@ -0,0 +1,251 @@ +package http + +import ( + "context" + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + + "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" +) + +var _ = Describe("ClusterHTTPScalingSetController", func() { + Describe("functional tests", func() { + It("ClusterHTTPScalingSet generates the interceptor and scaler using default values", func() { + name := "default-values" + interceptorName := fmt.Sprintf("%s-interceptor", name) + interceptorProxyServiceName, interceptorAdminServiceName := getInterceptorServiceNames(name) + + scalerName := fmt.Sprintf("%s-external-scaler", name) + scalerServiceName := fmt.Sprintf("%s-external-scaler", name) + + css := &v1alpha1.ClusterHTTPScalingSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: v1alpha1.HTTPScalingSetSpec{ + Interceptor: v1alpha1.HTTPInterceptorSpec{}, + Scaler: v1alpha1.HTTPScalerSpec{}, + }, + } + err := k8sClient.Create(context.Background(), css) + Expect(err).ToNot(HaveOccurred()) + + // Validate interceptor proxy service + interceptorProxyService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorProxyServiceName, Namespace: "keda"}, interceptorProxyService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorProxyService).ShouldNot(BeNil()) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(interceptorProxyService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("proxy")) + Expect(interceptorProxyService.Spec.Ports[0].Port).Should(Equal(int32(8080))) + + // Validate interceptor admin service + interceptorAdminService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorAdminServiceName, Namespace: "keda"}, interceptorAdminService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorAdminService).ShouldNot(BeNil()) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(interceptorAdminService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("admin")) + Expect(interceptorAdminService.Spec.Ports[0].Port).Should(Equal(int32(9090))) + + // Validate interceptor deployment + interceptorDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorName, Namespace: "keda"}, interceptorDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorDeploy).ShouldNot(BeNil()) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(*interceptorDeploy.Spec.Replicas).Should(Equal(int32(1))) + Expect(interceptorDeploy.Spec.Template.Spec.Containers[0].Resources.Requests).Should(BeNil()) + Expect(interceptorDeploy.Spec.Template.Spec.Containers[0].Resources.Limits).Should(BeNil()) + + // Validate scaler service + scalerService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerServiceName, Namespace: "keda"}, scalerService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerService).ShouldNot(BeNil()) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(scalerService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("grpc")) + Expect(scalerService.Spec.Ports[0].Port).Should(Equal(int32(9090))) + + // Validate scaler deployment + scalerDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerName, Namespace: "keda"}, scalerDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerDeploy).ShouldNot(BeNil()) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(*scalerDeploy.Spec.Replicas).Should(Equal(int32(1))) + Expect(scalerDeploy.Spec.Template.Spec.Containers[0].Resources.Requests).Should(BeNil()) + Expect(scalerDeploy.Spec.Template.Spec.Containers[0].Resources.Limits).Should(BeNil()) + }) + + It("ClusterHTTPScalingSet generates the interceptor and scaler using custom values", func() { + name := "custom-values" + interceptorName := fmt.Sprintf("%s-interceptor", name) + interceptorProxyServiceName, interceptorAdminServiceName := getInterceptorServiceNames(name) + var interceptorReplicas int32 = 3 + interceptorResouces := corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": *resource.NewQuantity(10, resource.DecimalSI), + "memory": *resource.NewQuantity(11, resource.DecimalSI), + }, + Limits: corev1.ResourceList{ + "cpu": *resource.NewQuantity(20, resource.DecimalSI), + "memory": *resource.NewQuantity(21, resource.DecimalSI), + }, + } + var interceptorProxyPort int32 = 6666 + var interceptorAdminPort int32 = 7777 + + scalerName := fmt.Sprintf("%s-external-scaler", name) + scalerServiceName := fmt.Sprintf("%s-external-scaler", name) + var scalerReplicas int32 = 2 + scalerResouces := corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": *resource.NewQuantity(30, resource.DecimalSI), + "memory": *resource.NewQuantity(31, resource.DecimalSI), + }, + Limits: corev1.ResourceList{ + "cpu": *resource.NewQuantity(40, resource.DecimalSI), + "memory": *resource.NewQuantity(41, resource.DecimalSI), + }, + } + var scalerPort int32 = 8888 + + css := &v1alpha1.ClusterHTTPScalingSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: v1alpha1.HTTPScalingSetSpec{ + Interceptor: v1alpha1.HTTPInterceptorSpec{ + Replicas: ptr.To(interceptorReplicas), + Resources: interceptorResouces, + Config: &v1alpha1.HTTPInterceptorConfigurationSpec{ + ProxyPort: ptr.To(interceptorProxyPort), + AdminPort: ptr.To(interceptorAdminPort), + }, + Labels: map[string]string{ + "interceptor-label-1": "value-1", + }, + Annotations: map[string]string{ + "interceptor-annotation-1": "value-2", + }, + }, + Scaler: v1alpha1.HTTPScalerSpec{ + Replicas: ptr.To(scalerReplicas), + Resources: scalerResouces, + Config: v1alpha1.HTTPScalerConfigurationSpec{ + Port: ptr.To(scalerPort), + }, + Labels: map[string]string{ + "scaler-label-1": "value-3", + }, + Annotations: map[string]string{ + "scaler-annotation-1": "value-4", + }, + }, + }, + } + err := k8sClient.Create(context.Background(), css) + Expect(err).ToNot(HaveOccurred()) + + // Validate interceptor proxy service + interceptorProxyService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorProxyServiceName, Namespace: "keda"}, interceptorProxyService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorProxyService).ShouldNot(BeNil()) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(interceptorProxyService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("proxy")) + Expect(interceptorProxyService.Spec.Ports[0].Port).Should(Equal(interceptorProxyPort)) + + // Validate interceptor admin service + interceptorAdminService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorAdminServiceName, Namespace: "keda"}, interceptorAdminService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorAdminService).ShouldNot(BeNil()) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(interceptorAdminService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("admin")) + Expect(interceptorAdminService.Spec.Ports[0].Port).Should(Equal(interceptorAdminPort)) + + // Validate interceptor deployment + interceptorDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorName, Namespace: "keda"}, interceptorDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorDeploy).ShouldNot(BeNil()) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(*interceptorDeploy.Spec.Replicas).Should(Equal(interceptorReplicas)) + Expect(interceptorDeploy.ObjectMeta.Labels).Should(HaveKeyWithValue("interceptor-label-1", "value-1")) + Expect(interceptorDeploy.ObjectMeta.Annotations).Should(HaveKeyWithValue("interceptor-annotation-1", "value-2")) + validateResources(interceptorResouces, interceptorDeploy.Spec.Template.Spec.Containers[0].Resources) + + // Validate scaler service + scalerService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerServiceName, Namespace: "keda"}, scalerService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerService).ShouldNot(BeNil()) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(scalerService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("grpc")) + Expect(scalerService.Spec.Ports[0].Port).Should(Equal(scalerPort)) + + // Validate scaler deployment + scalerDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerName, Namespace: "keda"}, scalerDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerDeploy).ShouldNot(BeNil()) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "ClusterHTTPScalingSet")) + Expect(*scalerDeploy.Spec.Replicas).Should(Equal(scalerReplicas)) + Expect(scalerDeploy.ObjectMeta.Labels).Should(HaveKeyWithValue("scaler-label-1", "value-3")) + Expect(scalerDeploy.ObjectMeta.Annotations).Should(HaveKeyWithValue("scaler-annotation-1", "value-4")) + validateResources(scalerResouces, scalerDeploy.Spec.Template.Spec.Containers[0].Resources) + }) + + }) +}) diff --git a/operator/controllers/http/condition_provider.go b/operator/controllers/http/condition_provider.go index 8a017238..71edf02c 100644 --- a/operator/controllers/http/condition_provider.go +++ b/operator/controllers/http/condition_provider.go @@ -17,15 +17,16 @@ func SaveStatus( ctx context.Context, logger logr.Logger, cl client.Client, - httpso *httpv1alpha1.HTTPScaledObject, + httpObject client.Object, ) { - logger.Info("Updating status on HTTPScaledObject", "resource version", httpso.ResourceVersion) + resourceType := httpObject.GetObjectKind().GroupVersionKind().String() + logger.Info("Updating status", "resource", httpObject.GetName(), "resourceType", resourceType, "resource version", httpObject.GetResourceVersion()) - err := cl.Status().Update(ctx, httpso) + err := cl.Status().Update(ctx, httpObject) if err != nil { - logger.Error(err, "failed to update status on HTTPScaledObject", "httpso", httpso) + logger.Error(err, "failed to update status", "object", httpObject) } else { - logger.Info("Updated status on HTTPScaledObject", "resource version", httpso.ResourceVersion) + logger.Info("Updated status", "resource", httpObject.GetName(), "resourceType", resourceType, "resource version", httpObject.GetResourceVersion()) } } diff --git a/operator/controllers/http/deployment.go b/operator/controllers/http/deployment.go new file mode 100644 index 00000000..73e58236 --- /dev/null +++ b/operator/controllers/http/deployment.go @@ -0,0 +1,46 @@ +package http + +import ( + "context" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// +kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete + +func createOrUpdateDeployment( + ctx context.Context, + logger logr.Logger, + cl client.Client, + deployment *appsv1.Deployment, +) error { + logger.Info("Creating Deployment", "deployment", deployment.Name) + if err := cl.Create(ctx, deployment); err != nil { + if errors.IsAlreadyExists(err) { + existingServiceKey := client.ObjectKey{ + Namespace: deployment.GetNamespace(), + Name: deployment.GetName(), + } + if err := cl.Get(ctx, existingServiceKey, &appsv1.Deployment{}); err != nil { + logger.Error( + err, + "failed to fetch existing Deployment for patching", + ) + return err + } + if err := cl.Patch(ctx, deployment, client.Merge); err != nil { + logger.Error( + err, + "failed to patch existing Deployment", + ) + return err + } + return nil + } + return err + } + return nil +} diff --git a/operator/controllers/http/finalizer.go b/operator/controllers/http/finalizer.go index 403cc3ec..17659739 100644 --- a/operator/controllers/http/finalizer.go +++ b/operator/controllers/http/finalizer.go @@ -5,33 +5,41 @@ import ( "github.com/go-logr/logr" "sigs.k8s.io/controller-runtime/pkg/client" - - httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" ) const ( + // This finalizer is deprecated and we shouldn't use it httpScaledObjectFinalizer = "httpscaledobject.http.keda.sh" + httpFinalizer = "http.keda.sh" ) -// ensureFinalizer check there is finalizer present on the ScaledObject, if not it adds one +// ensureFinalizer check there is finalizer present on the HTTP resources, if not it adds one func ensureFinalizer( ctx context.Context, logger logr.Logger, client client.Client, - httpso *httpv1alpha1.HTTPScaledObject, + httpObject client.Object, ) error { - if !contains(httpso.GetFinalizers(), httpScaledObjectFinalizer) { - logger.Info("Adding Finalizer for the ScaledObject") - httpso.SetFinalizers(append(httpso.GetFinalizers(), httpScaledObjectFinalizer)) + if !contains(httpObject.GetFinalizers(), httpFinalizer) { + logger.Info("Adding Finalizer") + // We have to ensure that the old finalizer is removed + // We can remove this code in future versions, like v0.10.0 or later + finalizers := remove(httpObject.GetFinalizers(), httpScaledObjectFinalizer) + + httpObject.SetFinalizers(append(finalizers, httpFinalizer)) // Update CR - err := client.Update(ctx, httpso) + err := client.Update(ctx, httpObject) if err != nil { logger.Error( err, - "Failed to update HTTPScaledObject with a finalizer", + "Failed to update with a finalizer", + "name", + httpObject.GetName(), + "kind", + httpObject.GetObjectKind().GroupVersionKind().String(), "finalizer", - httpScaledObjectFinalizer, + httpFinalizer, ) return err } @@ -43,21 +51,25 @@ func finalizeScaledObject( ctx context.Context, logger logr.Logger, client client.Client, - httpso *httpv1alpha1.HTTPScaledObject) error { - if contains(httpso.GetFinalizers(), httpScaledObjectFinalizer) { - httpso.SetFinalizers(remove(httpso.GetFinalizers(), httpScaledObjectFinalizer)) - if err := client.Update(ctx, httpso); err != nil { + httpObject client.Object) error { + if contains(httpObject.GetFinalizers(), httpFinalizer) { + httpObject.SetFinalizers(remove(httpObject.GetFinalizers(), httpFinalizer)) + if err := client.Update(ctx, httpObject); err != nil { logger.Error( err, "Failed to update ScaledObject after removing a finalizer", + "name", + httpObject.GetName(), + "kind", + httpObject.GetObjectKind().GroupVersionKind().String(), "finalizer", - httpScaledObjectFinalizer, + httpFinalizer, ) return err } } - logger.Info("Successfully finalized HTTPScaledObject") + logger.Info("Successfully finalized") return nil } diff --git a/operator/controllers/http/httpscaledobject_controller.go b/operator/controllers/http/httpscaledobject_controller.go index 317d69f6..24b3d0ae 100644 --- a/operator/controllers/http/httpscaledobject_controller.go +++ b/operator/controllers/http/httpscaledobject_controller.go @@ -25,13 +25,14 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" "github.com/kedacore/http-add-on/operator/controllers/http/config" "github.com/kedacore/http-add-on/operator/controllers/util" ) @@ -59,7 +60,7 @@ func (r *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Req logger := log.FromContext(ctx, "httpscaledobject", req.NamespacedName) logger.Info("Reconciliation start") - httpso := &httpv1alpha1.HTTPScaledObject{} + httpso := &v1alpha1.HTTPScaledObject{} if err := r.Client.Get(ctx, req.NamespacedName, httpso); err != nil { if k8serrors.IsNotFound(err) { // If the HTTPScaledObject wasn't found, it might have @@ -113,8 +114,6 @@ func (r *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Req ctx, logger, r.Client, - r.BaseConfig, - r.ExternalScalerConfig, httpso, ) if err != nil { @@ -129,9 +128,9 @@ func (r *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Req httpso, *SetMessage( CreateCondition( - httpv1alpha1.Ready, + v1alpha1.Ready, v1.ConditionTrue, - httpv1alpha1.HTTPScaledObjectIsReady, + v1alpha1.HTTPScaledObjectIsReady, ), "Finished object creation", ), @@ -146,7 +145,7 @@ func (r *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Req // SetupWithManager sets up the controller with the Manager. func (r *HTTPScaledObjectReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&httpv1alpha1.HTTPScaledObject{}, builder.WithPredicates( + For(&v1alpha1.HTTPScaledObject{}, builder.WithPredicates( predicate.Or( predicate.GenerationChangedPredicate{}, util.HTTPScaledObjectReadyConditionPredicate{}, @@ -162,3 +161,40 @@ func (r *HTTPScaledObjectReconciler) SetupWithManager(mgr ctrl.Manager) error { ))). Complete(r) } + +func (r *HTTPScaledObjectReconciler) getExternalScalerURI(ctx context.Context, namespace string, httpso *v1alpha1.HTTPScaledObject) (string, error) { + serviceName := r.ExternalScalerConfig.ServiceName + port := r.ExternalScalerConfig.Port + scalingSetRef := httpso.Spec.GetHTTPSalingSetTargetRef() + + // If the HTTPScaledObject defines the ScalingSet + // we have to check the kind and recover the info + // to generate the external scaler uri + if scalingSetRef.Name != "" { + scalingSetSpec := v1alpha1.HTTPScalingSetSpec{} + if scalingSetRef.Kind == v1alpha1.ClusterHTTPScalingSetKind { + scalingSet := &v1alpha1.ClusterHTTPScalingSet{} + err := r.Client.Get(ctx, types.NamespacedName{Name: httpso.Spec.ScalingSet.Name}, scalingSet) + if err != nil { + return "", err + } + scalingSetSpec = scalingSet.Spec + } else { + scalingSet := &v1alpha1.HTTPScalingSet{} + err := r.Client.Get(ctx, types.NamespacedName{Name: httpso.Spec.ScalingSet.Name, Namespace: httpso.Namespace}, scalingSet) + if err != nil { + return "", err + } + scalingSetSpec = scalingSet.Spec + namespace = httpso.Namespace + } + serviceName = fmt.Sprintf("%s-%s", scalingSetRef.Name, externalScaler) + port = scalingSetSpec.Scaler.GetPort() + } + return fmt.Sprintf( + "%s.%s:%d", + serviceName, + namespace, + port, + ), nil +} diff --git a/operator/controllers/http/httpscaledobject_controller_test.go b/operator/controllers/http/httpscaledobject_controller_test.go index 389aa256..639b7090 100644 --- a/operator/controllers/http/httpscaledobject_controller_test.go +++ b/operator/controllers/http/httpscaledobject_controller_test.go @@ -25,8 +25,6 @@ func TestHttpScaledObjectControllerWhenSkipAnnotationNotSet(t *testing.T) { testInfra.ctx, testInfra.logger, testInfra.cl, - config.Base{}, - config.ExternalScaler{}, &testInfra.httpso, ) r.NoError(err) @@ -57,8 +55,6 @@ func TestHttpScaledObjectControllerWhenSkipAnnotationSet(t *testing.T) { testInfra.ctx, testInfra.logger, testInfra.cl, - config.Base{}, - config.ExternalScaler{}, &testInfra.httpso, ) r.NoError(err) @@ -89,8 +85,6 @@ func TestHttpScaledObjectControllerWhenSkipAnnotationAddedToExistingHttpSo(t *te testInfra.ctx, testInfra.logger, testInfra.cl, - config.Base{}, - config.ExternalScaler{}, &testInfra.httpso, ) r.NoError(err) @@ -111,8 +105,6 @@ func TestHttpScaledObjectControllerWhenSkipAnnotationAddedToExistingHttpSo(t *te testInfra.ctx, testInfra.logger, testInfra.cl, - config.Base{}, - config.ExternalScaler{}, &testInfra.httpso, ) r.NoError(err) diff --git a/operator/controllers/http/httpscalingset_controller.go b/operator/controllers/http/httpscalingset_controller.go new file mode 100644 index 00000000..bcc0e6e3 --- /dev/null +++ b/operator/controllers/http/httpscalingset_controller.go @@ -0,0 +1,127 @@ +/* +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 http + +import ( + "context" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + "github.com/kedacore/http-add-on/operator/controllers/util" +) + +// HTTPScalingSetReconciler reconciles a HTTPScalingSet object +// +//revive:disable-next-line:exported +//goland:noinspection GoNameStartsWithPackageName +type HTTPScalingSetReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=http.keda.sh,resources=httpscalingsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=http.keda.sh,resources=httpscalingsets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=http.keda.sh,resources=httpscalingsets/finalizers,verbs=update + +// Reconcile reconciles a newly created, deleted, or otherwise changed +// HTTPScaledObject +func (r *HTTPScalingSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx, "httpscalingset", req.NamespacedName) + logger.Info("Reconciliation start") + + httpss := &httpv1alpha1.HTTPScalingSet{} + if err := r.Client.Get(ctx, req.NamespacedName, httpss); err != nil { + if k8serrors.IsNotFound(err) { + // If the HTTPScaledObject wasn't found, it might have + // been deleted between the reconcile and the get. + // It'll automatically get garbage collected, so don't + // schedule a requeue + logger.Info("HTTPScalingSet not found, assuming it was deleted and stopping early") + return ctrl.Result{}, nil + } + // if we didn't get a not found error, log it and schedule a requeue + // with a backoff + logger.Error(err, "Getting the HTTP Scaled obj, requeueing") + return ctrl.Result{ + RequeueAfter: 500 * time.Millisecond, + }, err + } + + if httpss.GetDeletionTimestamp() != nil { + return ctrl.Result{}, finalizeScaledObject(ctx, logger, r.Client, httpss) + } + + // ensure finalizer is set on this resource + if err := ensureFinalizer(ctx, logger, r.Client, httpss); err != nil { + return ctrl.Result{}, err + } + + // Create required app objects for the application defined by the CRD + err := createOrUpdateInterceptorResources(ctx, logger, r.Client, httpss, r.Scheme) + if err != nil { + return ctrl.Result{}, err + } + err = createOrUpdateExternalScalerResources( + ctx, + logger, + r.Client, + httpss, + r.Scheme, + ) + if err != nil { + return ctrl.Result{}, err + } + + // success reconciling + logger.Info("Reconcile success") + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *HTTPScalingSetReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&httpv1alpha1.HTTPScalingSet{}, builder.WithPredicates( + predicate.Or( + predicate.GenerationChangedPredicate{}, + ), + )). + // Trigger a reconcile only when the Deployment spec,label or annotation changes. + // Ignore updates to Deployment status + Owns(&appsv1.Deployment{}, builder.WithPredicates( + predicate.Or( + predicate.LabelChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + util.DeploymentSpecChangedPredicate{}, + ))). + // Trigger a reconcile only when the Service spec,label or annotation changes. + // Ignore updates to Service status + Owns(&corev1.Service{}, builder.WithPredicates( + predicate.Or( + predicate.LabelChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + util.ServiceSpecChangedPredicate{}, + ))). + Complete(r) +} diff --git a/operator/controllers/http/httpscalingset_controller_test.go b/operator/controllers/http/httpscalingset_controller_test.go new file mode 100644 index 00000000..925257aa --- /dev/null +++ b/operator/controllers/http/httpscalingset_controller_test.go @@ -0,0 +1,257 @@ +package http + +import ( + "context" + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + + "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" +) + +var _ = Describe("HTTPScalingSetController", func() { + Describe("functional tests", func() { + It("HTTPScalingSet generates the interceptor and scaler using default values", func() { + name := "default-values" + namesapce := "http-ss-defaults" + createNamespace(namesapce) + interceptorName := fmt.Sprintf("%s-interceptor", name) + interceptorProxyServiceName, interceptorAdminServiceName := getInterceptorServiceNames(name) + + scalerName := fmt.Sprintf("%s-external-scaler", name) + scalerServiceName := fmt.Sprintf("%s-external-scaler", name) + + css := &v1alpha1.HTTPScalingSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namesapce, + }, + Spec: v1alpha1.HTTPScalingSetSpec{ + Interceptor: v1alpha1.HTTPInterceptorSpec{}, + Scaler: v1alpha1.HTTPScalerSpec{}, + }, + } + err := k8sClient.Create(context.Background(), css) + Expect(err).ToNot(HaveOccurred()) + + // Validate interceptor proxy service + interceptorProxyService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorProxyServiceName, Namespace: namesapce}, interceptorProxyService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorProxyService).ShouldNot(BeNil()) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(interceptorProxyService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("proxy")) + Expect(interceptorProxyService.Spec.Ports[0].Port).Should(Equal(int32(8080))) + + // Validate interceptor admin service + interceptorAdminService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorAdminServiceName, Namespace: namesapce}, interceptorAdminService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorAdminService).ShouldNot(BeNil()) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(interceptorAdminService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("admin")) + Expect(interceptorAdminService.Spec.Ports[0].Port).Should(Equal(int32(9090))) + + // Validate interceptor deployment + interceptorDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorName, Namespace: namesapce}, interceptorDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorDeploy).ShouldNot(BeNil()) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(*interceptorDeploy.Spec.Replicas).Should(Equal(int32(1))) + Expect(interceptorDeploy.Spec.Template.Spec.Containers[0].Resources.Requests).Should(BeNil()) + Expect(interceptorDeploy.Spec.Template.Spec.Containers[0].Resources.Limits).Should(BeNil()) + + // Validate scaler service + scalerService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerServiceName, Namespace: namesapce}, scalerService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerService).ShouldNot(BeNil()) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(scalerService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("grpc")) + Expect(scalerService.Spec.Ports[0].Port).Should(Equal(int32(9090))) + + // Validate scaler deployment + scalerDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerName, Namespace: namesapce}, scalerDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerDeploy).ShouldNot(BeNil()) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(*scalerDeploy.Spec.Replicas).Should(Equal(int32(1))) + Expect(scalerDeploy.Spec.Template.Spec.Containers[0].Resources.Requests).Should(BeNil()) + Expect(scalerDeploy.Spec.Template.Spec.Containers[0].Resources.Limits).Should(BeNil()) + }) + + It("HTTPScalingSet generates the interceptor and scaler using custom values", func() { + name := "custom-values" + namesapce := "http-ss-custom" + createNamespace(namesapce) + interceptorName := fmt.Sprintf("%s-interceptor", name) + interceptorProxyServiceName, interceptorAdminServiceName := getInterceptorServiceNames(name) + var interceptorReplicas int32 = 3 + interceptorResouces := corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": *resource.NewQuantity(10, resource.DecimalSI), + "memory": *resource.NewQuantity(11, resource.DecimalSI), + }, + Limits: corev1.ResourceList{ + "cpu": *resource.NewQuantity(20, resource.DecimalSI), + "memory": *resource.NewQuantity(21, resource.DecimalSI), + }, + } + var interceptorProxyPort int32 = 6666 + var interceptorAdminPort int32 = 7777 + + scalerName := fmt.Sprintf("%s-external-scaler", name) + scalerServiceName := fmt.Sprintf("%s-external-scaler", name) + var scalerReplicas int32 = 2 + scalerResouces := corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + "cpu": *resource.NewQuantity(30, resource.DecimalSI), + "memory": *resource.NewQuantity(31, resource.DecimalSI), + }, + Limits: corev1.ResourceList{ + "cpu": *resource.NewQuantity(40, resource.DecimalSI), + "memory": *resource.NewQuantity(41, resource.DecimalSI), + }, + } + var scalerPort int32 = 8888 + + css := &v1alpha1.HTTPScalingSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namesapce, + }, + Spec: v1alpha1.HTTPScalingSetSpec{ + Interceptor: v1alpha1.HTTPInterceptorSpec{ + Replicas: ptr.To(interceptorReplicas), + Resources: interceptorResouces, + Config: &v1alpha1.HTTPInterceptorConfigurationSpec{ + ProxyPort: ptr.To(interceptorProxyPort), + AdminPort: ptr.To(interceptorAdminPort), + }, + Labels: map[string]string{ + "interceptor-label-1": "value-1", + }, + Annotations: map[string]string{ + "interceptor-annotation-1": "value-2", + }, + }, + Scaler: v1alpha1.HTTPScalerSpec{ + Replicas: ptr.To(scalerReplicas), + Resources: scalerResouces, + Config: v1alpha1.HTTPScalerConfigurationSpec{ + Port: ptr.To(scalerPort), + }, + Labels: map[string]string{ + "scaler-label-1": "value-3", + }, + Annotations: map[string]string{ + "scaler-annotation-1": "value-4", + }, + }, + }, + } + err := k8sClient.Create(context.Background(), css) + Expect(err).ToNot(HaveOccurred()) + + // Validate interceptor proxy service + interceptorProxyService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorProxyServiceName, Namespace: namesapce}, interceptorProxyService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorProxyService).ShouldNot(BeNil()) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorProxyService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(interceptorProxyService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("proxy")) + Expect(interceptorProxyService.Spec.Ports[0].Port).Should(Equal(interceptorProxyPort)) + + // Validate interceptor admin service + interceptorAdminService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorAdminServiceName, Namespace: namesapce}, interceptorAdminService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorAdminService).ShouldNot(BeNil()) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorAdminService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(interceptorAdminService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("admin")) + Expect(interceptorAdminService.Spec.Ports[0].Port).Should(Equal(interceptorAdminPort)) + + // Validate interceptor deployment + interceptorDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: interceptorName, Namespace: namesapce}, interceptorDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(interceptorDeploy).ShouldNot(BeNil()) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "interceptor")) + Expect(interceptorDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(*interceptorDeploy.Spec.Replicas).Should(Equal(interceptorReplicas)) + Expect(interceptorDeploy.ObjectMeta.Labels).Should(HaveKeyWithValue("interceptor-label-1", "value-1")) + Expect(interceptorDeploy.ObjectMeta.Annotations).Should(HaveKeyWithValue("interceptor-annotation-1", "value-2")) + validateResources(interceptorResouces, interceptorDeploy.Spec.Template.Spec.Containers[0].Resources) + + // Validate scaler service + scalerService := &corev1.Service{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerServiceName, Namespace: namesapce}, scalerService) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerService).ShouldNot(BeNil()) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerService.Spec.Selector).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(scalerService.Spec.Ports[0].TargetPort.StrVal).Should(Equal("grpc")) + Expect(scalerService.Spec.Ports[0].Port).Should(Equal(scalerPort)) + + // Validate scaler deployment + scalerDeploy := &v1.Deployment{} + Eventually(func() error { + return k8sClient.Get(context.Background(), types.NamespacedName{Name: scalerName, Namespace: namesapce}, scalerDeploy) + }).WithTimeout(30 * time.Second).WithPolling(5 * time.Second).ShouldNot(HaveOccurred()) + + Expect(scalerDeploy).ShouldNot(BeNil()) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set", name)) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-component", "external-scaler")) + Expect(scalerDeploy.Spec.Selector.MatchLabels).Should(HaveKeyWithValue("http.keda.sh/scaling-set-kind", "HTTPScalingSet")) + Expect(*scalerDeploy.Spec.Replicas).Should(Equal(scalerReplicas)) + Expect(scalerDeploy.ObjectMeta.Labels).Should(HaveKeyWithValue("scaler-label-1", "value-3")) + Expect(scalerDeploy.ObjectMeta.Annotations).Should(HaveKeyWithValue("scaler-annotation-1", "value-4")) + validateResources(scalerResouces, scalerDeploy.Spec.Template.Spec.Containers[0].Resources) + }) + + }) +}) diff --git a/operator/controllers/http/interceptor.go b/operator/controllers/http/interceptor.go new file mode 100644 index 00000000..57cd5c16 --- /dev/null +++ b/operator/controllers/http/interceptor.go @@ -0,0 +1,176 @@ +package http + +import ( + "context" + "fmt" + "strconv" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/kedacore/http-add-on/pkg/k8s" + "github.com/kedacore/http-add-on/pkg/util" + "github.com/kedacore/http-add-on/pkg/validator" +) + +const ( + interceptor string = "interceptor" + proxy string = "proxy" + admin string = "admin" +) + +var ( + interceptorProxyName = fmt.Sprintf("%s-%s", interceptor, proxy) + interceptorAdminName = fmt.Sprintf("%s-%s", interceptor, admin) +) + +func getInterceptorServiceNames(scalingSetName string) (proxy, admin string) { + proxy = fmt.Sprintf("%s-%s", scalingSetName, interceptorProxyName) + admin = fmt.Sprintf("%s-%s", scalingSetName, interceptorAdminName) + return +} + +func createOrUpdateInterceptorResources( + ctx context.Context, + logger logr.Logger, + cl client.Client, + httpss metav1.Object, + scheme *runtime.Scheme, +) error { + proxyServiceName, adminServiceName := getInterceptorServiceNames(httpss.GetName()) + interceptorName := fmt.Sprintf("%s-%s", httpss.GetName(), interceptor) + httpSpec := util.GetHTTPScalingSetSpecFromObject(httpss) + httpKind := util.GetHTTPScalingSetKindFromObject(httpss) + + logger = logger.WithValues( + "reconciler.appObjects", + "addObjects", + "HTTPScalingSet.name", + httpss.GetName(), + ) + selector := map[string]string{ + "http.keda.sh/scaling-set": httpss.GetName(), + "http.keda.sh/scaling-set-component": interceptor, + "http.keda.sh/scaling-set-kind": string(httpKind), + } + + proxyService := k8s.NewService(proxyServiceName, httpss.GetNamespace(), proxy, httpSpec.Interceptor.GetProxyPort(), selector) + // Set HTTPScaledObject instance as the owner and controller + if err := controllerutil.SetControllerReference(httpss, proxyService, scheme); err != nil { + return err + } + err := createOrUpdateService(ctx, logger, cl, proxyService) + if err != nil { + return err + } + adminService := k8s.NewService(adminServiceName, httpss.GetNamespace(), admin, httpSpec.Interceptor.GetAdminPort(), selector) + // Set HTTPScaledObject instance as the owner and controller + if err := controllerutil.SetControllerReference(httpss, adminService, scheme); err != nil { + return err + } + err = createOrUpdateService(ctx, logger, cl, adminService) + if err != nil { + return err + } + ports := []corev1.ContainerPort{ + { + Name: proxy, + ContainerPort: httpSpec.Interceptor.GetProxyPort(), + Protocol: "TCP", + }, + { + Name: admin, + ContainerPort: httpSpec.Interceptor.GetAdminPort(), + Protocol: "TCP", + }, + } + envs := []corev1.EnvVar{ + { + Name: validator.ScalingSetNameEnv, + Value: httpss.GetName(), + }, + { + Name: validator.ScalingSetKindEnv, + Value: string(httpKind), + }, + { + Name: "KEDA_HTTP_CURRENT_NAMESPACE", + Value: httpss.GetNamespace(), + }, + { + Name: "KEDA_HTTP_PROXY_PORT", + Value: fmt.Sprintf("%d", httpSpec.Interceptor.GetProxyPort()), + }, + { + Name: "KEDA_HTTP_ADMIN_PORT", + Value: fmt.Sprintf("%d", httpSpec.Interceptor.GetAdminPort()), + }, + { + Name: "KEDA_HTTP_CONNECT_TIMEOUT", + Value: httpSpec.Interceptor.GetConnectTimeout(), + }, + { + Name: "KEDA_HTTP_KEEP_ALIVE", + Value: httpSpec.Interceptor.GetKeepAlive(), + }, + { + Name: "KEDA_RESPONSE_HEADER_TIMEOUT", + Value: httpSpec.Interceptor.GetHeaderTimeout(), + }, + { + Name: "KEDA_CONDITION_WAIT_TIMEOUT", + Value: httpSpec.Interceptor.GetWaitTimeout(), + }, + { + Name: "KEDA_HTTP_ENDPOINTS_CACHE_POLLING_INTERVAL_MS", + Value: strconv.Itoa(httpSpec.Interceptor.GetPollingInterval()), + }, + { + Name: "KEDA_HTTP_FORCE_HTTP2", + Value: strconv.FormatBool(httpSpec.Interceptor.GetForceHTTP2()), + }, + { + Name: "KEDA_HTTP_MAX_IDLE_CONNS", + Value: fmt.Sprintf("%d", httpSpec.Interceptor.GetMaxIdleConns()), + }, + { + Name: "KEDA_HTTP_IDLE_CONN_TIMEOUT", + Value: httpSpec.Interceptor.GetIdleConnTimeout(), + }, + { + Name: "KEDA_HTTP_TLS_HANDSHAKE_TIMEOUT", + Value: httpSpec.Interceptor.GetTLSHandshakeTimeout(), + }, + { + Name: "KEDA_HTTP_EXPECT_CONTINUE_TIMEOUT", + Value: httpSpec.Interceptor.GetExpectContinueTimeout(), + }, + } + interceptorDeployment := k8s.NewDeployment( + interceptorName, + httpss.GetNamespace(), + httpSpec.Interceptor.ServiceAccountName, + httpSpec.Interceptor.GetImage(), + ports, + envs, + httpSpec.Interceptor.Replicas, + selector, + httpSpec.Interceptor.GetLabels(), + httpSpec.Interceptor.GetAnnotations(), + httpSpec.Interceptor.Resources, + ) + // Set HTTPScaledObject instance as the owner and controller + if err := controllerutil.SetControllerReference(httpss, interceptorDeployment, scheme); err != nil { + return err + } + err = createOrUpdateDeployment(ctx, logger, cl, interceptorDeployment) + if err != nil { + return err + } + + return nil +} diff --git a/operator/controllers/http/ping.go b/operator/controllers/http/ping.go deleted file mode 100644 index 362a2aed..00000000 --- a/operator/controllers/http/ping.go +++ /dev/null @@ -1,46 +0,0 @@ -package http - -import ( - "context" - "fmt" - "net/http" - - "golang.org/x/sync/errgroup" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/kedacore/http-add-on/pkg/k8s" -) - -func pingInterceptors( - ctx context.Context, - cl client.Client, - httpCl *http.Client, - ns, - interceptorSvcName, - interceptorPort string, -) error { - endpointURLs, err := k8s.EndpointsForService( - ctx, - ns, - interceptorSvcName, - interceptorPort, - k8s.EndpointsFuncForControllerClient(cl), - ) - if err != nil { - return fmt.Errorf("pingInterceptors: %w", err) - } - errGrp, _ := errgroup.WithContext(ctx) - for _, endpointURL := range endpointURLs { - endpointStr := endpointURL.String() - errGrp.Go(func() error { - fullAddr := fmt.Sprintf("%s/routing_ping", endpointStr) - resp, err := httpCl.Get(fullAddr) - if err != nil { - return err - } - resp.Body.Close() - return nil - }) - } - return errGrp.Wait() -} diff --git a/operator/controllers/http/ping_test.go b/operator/controllers/http/ping_test.go deleted file mode 100644 index 2bd4febd..00000000 --- a/operator/controllers/http/ping_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package http - -import ( - "context" - "net/http" - "testing" - - "github.com/stretchr/testify/require" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - "github.com/kedacore/http-add-on/pkg/k8s" - kedanet "github.com/kedacore/http-add-on/pkg/net" -) - -func TestPingInterceptors(t *testing.T) { - const ( - ns = "testns" - svcName = "testsvc" - ) - r := require.New(t) - // create a new server (that we can introspect later on) to act - // like a fake interceptor. we expect that pingInterceptors() - // will make requests to this server - hdl := kedanet.NewTestHTTPHandlerWrapper( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) - }), - ) - srv, url, err := kedanet.StartTestServer(hdl) - r.NoError(err) - defer srv.Close() - ctx := context.Background() - endpoints, err := k8s.FakeEndpointsForURL(url, ns, svcName, 2) - r.NoError(err) - cl := fake.NewClientBuilder().WithObjects(endpoints).Build() - r.NoError(pingInterceptors( - ctx, - cl, - srv.Client(), - ns, - svcName, - url.Port(), - )) - reqs := hdl.IncomingRequests() - r.Equal(len(endpoints.Subsets[0].Addresses), len(reqs)) -} diff --git a/operator/controllers/http/scaler.go b/operator/controllers/http/scaler.go new file mode 100644 index 00000000..8590897f --- /dev/null +++ b/operator/controllers/http/scaler.go @@ -0,0 +1,114 @@ +package http + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/kedacore/http-add-on/pkg/k8s" + "github.com/kedacore/http-add-on/pkg/util" + "github.com/kedacore/http-add-on/pkg/validator" +) + +const ( + externalScaler string = "external-scaler" + grpc string = "grpc" +) + +func createOrUpdateExternalScalerResources( + ctx context.Context, + logger logr.Logger, + cl client.Client, + httpss metav1.Object, + scheme *runtime.Scheme, +) error { + scalerName := fmt.Sprintf("%s-%s", httpss.GetName(), externalScaler) + _, adminServiceName := getInterceptorServiceNames(httpss.GetName()) + httpSpec := util.GetHTTPScalingSetSpecFromObject(httpss) + httpKind := util.GetHTTPScalingSetKindFromObject(httpss) + + logger = logger.WithValues( + "reconciler.appObjects", + "addObjects", + "HTTPScalingSet.name", + httpss.GetName(), + ) + // defer SaveStatus(context.Background(), logger, cl, httpss) + selector := map[string]string{ + "http.keda.sh/scaling-set": httpss.GetName(), + "http.keda.sh/scaling-set-component": externalScaler, + "http.keda.sh/scaling-set-kind": string(httpKind), + } + + scalerService := k8s.NewService(scalerName, httpss.GetNamespace(), grpc, httpSpec.Scaler.GetPort(), selector) + // Set HTTPScaledObject instance as the owner and controller + if err := controllerutil.SetControllerReference(httpss, scalerService, scheme); err != nil { + return err + } + err := createOrUpdateService(ctx, logger, cl, scalerService) + if err != nil { + return err + } + ports := []corev1.ContainerPort{ + { + Name: grpc, + ContainerPort: httpSpec.Scaler.GetPort(), + Protocol: "TCP", + }, + } + envs := []corev1.EnvVar{ + { + Name: validator.ScalingSetNameEnv, + Value: httpss.GetName(), + }, + { + Name: validator.ScalingSetKindEnv, + Value: string(httpKind), + }, + { + Name: "KEDA_HTTP_SCALER_TARGET_ADMIN_NAMESPACE", + Value: httpss.GetNamespace(), + }, + { + Name: "KEDA_HTTP_SCALER_PORT", + Value: fmt.Sprintf("%d", httpSpec.Scaler.GetPort()), + }, + { + Name: "KEDA_HTTP_SCALER_TARGET_ADMIN_SERVICE", + Value: adminServiceName, + }, + { + Name: "KEDA_HTTP_SCALER_TARGET_ADMIN_PORT", + Value: fmt.Sprintf("%d", httpSpec.Interceptor.GetAdminPort()), + }, + } + scalerDeployment := k8s.NewDeployment( + scalerName, + httpss.GetNamespace(), + httpSpec.Scaler.ServiceAccountName, + httpSpec.Scaler.GetImage(), + ports, + envs, + httpSpec.Scaler.Replicas, + selector, + httpSpec.Scaler.GetLabels(), + httpSpec.Scaler.GetAnnotations(), + httpSpec.Scaler.Resources, + ) + // Set HTTPScaledObject instance as the owner and controller + if err := controllerutil.SetControllerReference(httpss, scalerDeployment, scheme); err != nil { + return err + } + err = createOrUpdateDeployment(ctx, logger, cl, scalerDeployment) + if err != nil { + return err + } + + return nil +} diff --git a/operator/controllers/http/service.go b/operator/controllers/http/service.go new file mode 100644 index 00000000..b1eb2580 --- /dev/null +++ b/operator/controllers/http/service.go @@ -0,0 +1,44 @@ +package http + +import ( + "context" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete + +func createOrUpdateService( + ctx context.Context, + logger logr.Logger, + cl client.Client, + service *corev1.Service, +) error { + logger.Info("Creating Service", "service", service.Name) + if err := cl.Create(ctx, service); err != nil { + if errors.IsAlreadyExists(err) { + existingServiceKey := client.ObjectKey{ + Namespace: service.GetNamespace(), + Name: service.GetName(), + } + if err := cl.Get(ctx, existingServiceKey, &corev1.Service{}); err != nil { + logger.Error( + err, + "failed to fetch existing Service for patching", + ) + return err + } + if err := cl.Patch(ctx, service, client.Merge); err != nil { + logger.Error( + err, + "failed to patch existing Service", + ) + return err + } + } + } + return nil +} diff --git a/operator/controllers/http/suite_test.go b/operator/controllers/http/suite_test.go index 53e1d781..1ff9c268 100644 --- a/operator/controllers/http/suite_test.go +++ b/operator/controllers/http/suite_test.go @@ -17,18 +17,24 @@ package http import ( "context" + "path/filepath" "testing" + "time" "github.com/go-logr/logr" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -39,50 +45,107 @@ import ( // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -// var cfg *rest.Config -// var k8sClient client.Client -// var testEnv *envtest.Environment +var cfg *rest.Config +var testEnv *envtest.Environment +var k8sClient client.Client -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) +var ctx context.Context +var cancel context.CancelFunc - RunSpecs(t, "Controller Suite") +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + suiteConfig, reporterConfig := GinkgoConfiguration() + reporterConfig.JUnitReport = "functional-test-report.xml" + RunSpecs(t, "Controllers Suite", suiteConfig, reporterConfig) } var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) By("bootstrapping test environment") - // testEnv = &envtest.Environment{ - // CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, - // ErrorIfCRDPathMissing: true, - // } + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } var err error - // cfg is defined in this file globally. - // cfg, err = testEnv.Start() - // Expect(err).NotTo(HaveOccurred()) - // Expect(cfg).NotTo(BeNil()) - - err = httpv1alpha1.AddToScheme(clientgoscheme.Scheme) + done := make(chan interface{}) + go func() { + defer GinkgoRecover() + cfg, err = testEnv.Start() + close(done) + }() + Eventually(done).WithTimeout(time.Minute).Should(BeClosed()) + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = kedav1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = kedav1alpha1.AddToScheme(clientgoscheme.Scheme) + err = httpv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme - // k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - // Expect(err).NotTo(HaveOccurred()) - // Expect(k8sClient).NotTo(BeNil()) + ctx, cancel = context.WithCancel(context.Background()) + + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + err = (&ClusterHTTPScalingSetReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + KEDANamespace: "keda", + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + err = (&HTTPScalingSetReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + createNamespace("keda") + + go func() { + err = k8sManager.Start(ctx) + Expect(err).ToNot(HaveOccurred()) + }() }) var _ = AfterSuite(func() { By("tearing down the test environment") - // err := testEnv.Stop() - // Expect(err).NotTo(HaveOccurred()) + + // stop k8sManager + cancel() + + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) }) +func validateResources(expected, value corev1.ResourceRequirements) { + Expect(expected.Requests.Cpu().AsApproximateFloat64()).Should(BeEquivalentTo(value.Requests.Cpu().AsApproximateFloat64())) + Expect(expected.Requests.Memory().AsApproximateFloat64()).Should(BeEquivalentTo(value.Requests.Memory().AsApproximateFloat64())) + Expect(expected.Limits.Cpu().AsApproximateFloat64()).Should(BeEquivalentTo(value.Limits.Cpu().AsApproximateFloat64())) + Expect(expected.Limits.Memory().AsApproximateFloat64()).Should(BeEquivalentTo(value.Limits.Memory().AsApproximateFloat64())) +} + +func createNamespace(name string) { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + err := k8sClient.Create(ctx, ns) + Expect(err).ToNot(HaveOccurred()) +} + type commonTestInfra struct { ns string appName string @@ -94,7 +157,7 @@ type commonTestInfra struct { func newCommonTestInfra(namespace, appName string) *commonTestInfra { localScheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(localScheme)) + utilruntime.Must(scheme.AddToScheme(localScheme)) utilruntime.Must(httpv1alpha1.AddToScheme(localScheme)) utilruntime.Must(kedav1alpha1.AddToScheme(localScheme)) @@ -134,7 +197,7 @@ func newCommonTestInfra(namespace, appName string) *commonTestInfra { func newCommonTestInfraWithSkipScaledObjectCreation(namespace, appName string) *commonTestInfra { localScheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(localScheme)) + utilruntime.Must(scheme.AddToScheme(localScheme)) utilruntime.Must(httpv1alpha1.AddToScheme(localScheme)) utilruntime.Must(kedav1alpha1.AddToScheme(localScheme)) diff --git a/operator/controllers/util/predicate.go b/operator/controllers/util/predicate.go index f1df5734..a2ee554d 100644 --- a/operator/controllers/util/predicate.go +++ b/operator/controllers/util/predicate.go @@ -2,6 +2,8 @@ package util import ( kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" @@ -50,3 +52,25 @@ func (ScaledObjectSpecChangedPredicate) Update(e event.UpdateEvent) bool { return !equality.Semantic.DeepDerivative(newObj.Spec, oldObj.Spec) } + +type DeploymentSpecChangedPredicate struct { + predicate.Funcs +} + +func (DeploymentSpecChangedPredicate) Update(e event.UpdateEvent) bool { + newObj := e.ObjectNew.(*appsv1.Deployment) + oldObj := e.ObjectOld.(*appsv1.Deployment) + + return !equality.Semantic.DeepDerivative(newObj.Spec, oldObj.Spec) +} + +type ServiceSpecChangedPredicate struct { + predicate.Funcs +} + +func (ServiceSpecChangedPredicate) Update(e event.UpdateEvent) bool { + newObj := e.ObjectNew.(*corev1.Service) + oldObj := e.ObjectOld.(*corev1.Service) + + return !equality.Semantic.DeepDerivative(newObj.Spec, oldObj.Spec) +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go new file mode 100644 index 00000000..29377cac --- /dev/null +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go @@ -0,0 +1,195 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + scheme "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterHTTPScalingSetsGetter has a method to return a ClusterHTTPScalingSetInterface. +// A group's client should implement this interface. +type ClusterHTTPScalingSetsGetter interface { + ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetInterface +} + +// ClusterHTTPScalingSetInterface has methods to work with ClusterHTTPScalingSet resources. +type ClusterHTTPScalingSetInterface interface { + Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterHTTPScalingSet, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterHTTPScalingSetList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterHTTPScalingSet, err error) + ClusterHTTPScalingSetExpansion +} + +// clusterHTTPScalingSets implements ClusterHTTPScalingSetInterface +type clusterHTTPScalingSets struct { + client rest.Interface + ns string +} + +// newClusterHTTPScalingSets returns a ClusterHTTPScalingSets +func newClusterHTTPScalingSets(c *HttpV1alpha1Client, namespace string) *clusterHTTPScalingSets { + return &clusterHTTPScalingSets{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the clusterHTTPScalingSet, and returns the corresponding clusterHTTPScalingSet object, and an error if there is any. +func (c *clusterHTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Get(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterHTTPScalingSets that match those selectors. +func (c *clusterHTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterHTTPScalingSetList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterHTTPScalingSetList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterHTTPScalingSets. +func (c *clusterHTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterHTTPScalingSet and creates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *clusterHTTPScalingSets) Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Post(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterHTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterHTTPScalingSet and updates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *clusterHTTPScalingSets) Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(clusterHTTPScalingSet.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterHTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterHTTPScalingSets) UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(clusterHTTPScalingSet.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterHTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterHTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *clusterHTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterHTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterHTTPScalingSet. +func (c *clusterHTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + result = &v1alpha1.ClusterHTTPScalingSet{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("clusterhttpscalingsets"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_clusterhttpscalingset.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_clusterhttpscalingset.go new file mode 100644 index 00000000..a0b9613e --- /dev/null +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_clusterhttpscalingset.go @@ -0,0 +1,141 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeClusterHTTPScalingSets implements ClusterHTTPScalingSetInterface +type FakeClusterHTTPScalingSets struct { + Fake *FakeHttpV1alpha1 + ns string +} + +var clusterhttpscalingsetsResource = v1alpha1.SchemeGroupVersion.WithResource("clusterhttpscalingsets") + +var clusterhttpscalingsetsKind = v1alpha1.SchemeGroupVersion.WithKind("ClusterHTTPScalingSet") + +// Get takes name of the clusterHTTPScalingSet, and returns the corresponding clusterHTTPScalingSet object, and an error if there is any. +func (c *FakeClusterHTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(clusterhttpscalingsetsResource, c.ns, name), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// List takes label and field selectors, and returns the list of ClusterHTTPScalingSets that match those selectors. +func (c *FakeClusterHTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ClusterHTTPScalingSetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(clusterhttpscalingsetsResource, clusterhttpscalingsetsKind, c.ns, opts), &v1alpha1.ClusterHTTPScalingSetList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ClusterHTTPScalingSetList{ListMeta: obj.(*v1alpha1.ClusterHTTPScalingSetList).ListMeta} + for _, item := range obj.(*v1alpha1.ClusterHTTPScalingSetList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested clusterHTTPScalingSets. +func (c *FakeClusterHTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(clusterhttpscalingsetsResource, c.ns, opts)) + +} + +// Create takes the representation of a clusterHTTPScalingSet and creates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *FakeClusterHTTPScalingSets) Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(clusterhttpscalingsetsResource, c.ns, clusterHTTPScalingSet), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// Update takes the representation of a clusterHTTPScalingSet and updates it. Returns the server's representation of the clusterHTTPScalingSet, and an error, if there is any. +func (c *FakeClusterHTTPScalingSets) Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(clusterhttpscalingsetsResource, c.ns, clusterHTTPScalingSet), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeClusterHTTPScalingSets) UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(clusterhttpscalingsetsResource, "status", c.ns, clusterHTTPScalingSet), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} + +// Delete takes name of the clusterHTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *FakeClusterHTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(clusterhttpscalingsetsResource, c.ns, name, opts), &v1alpha1.ClusterHTTPScalingSet{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusterHTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(clusterhttpscalingsetsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.ClusterHTTPScalingSetList{}) + return err +} + +// Patch applies the patch and returns the patched clusterHTTPScalingSet. +func (c *FakeClusterHTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ClusterHTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(clusterhttpscalingsetsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ClusterHTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), err +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go index ec48f0a0..9317b326 100644 --- a/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go @@ -28,10 +28,18 @@ type FakeHttpV1alpha1 struct { *testing.Fake } +func (c *FakeHttpV1alpha1) ClusterHTTPScalingSets(namespace string) v1alpha1.ClusterHTTPScalingSetInterface { + return &FakeClusterHTTPScalingSets{c, namespace} +} + func (c *FakeHttpV1alpha1) HTTPScaledObjects(namespace string) v1alpha1.HTTPScaledObjectInterface { return &FakeHTTPScaledObjects{c, namespace} } +func (c *FakeHttpV1alpha1) HTTPScalingSets(namespace string) v1alpha1.HTTPScalingSetInterface { + return &FakeHTTPScalingSets{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeHttpV1alpha1) RESTClient() rest.Interface { diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscalingset.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscalingset.go new file mode 100644 index 00000000..078c2c2c --- /dev/null +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscalingset.go @@ -0,0 +1,141 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeHTTPScalingSets implements HTTPScalingSetInterface +type FakeHTTPScalingSets struct { + Fake *FakeHttpV1alpha1 + ns string +} + +var httpscalingsetsResource = v1alpha1.SchemeGroupVersion.WithResource("httpscalingsets") + +var httpscalingsetsKind = v1alpha1.SchemeGroupVersion.WithKind("HTTPScalingSet") + +// Get takes name of the hTTPScalingSet, and returns the corresponding hTTPScalingSet object, and an error if there is any. +func (c *FakeHTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(httpscalingsetsResource, c.ns, name), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// List takes label and field selectors, and returns the list of HTTPScalingSets that match those selectors. +func (c *FakeHTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScalingSetList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(httpscalingsetsResource, httpscalingsetsKind, c.ns, opts), &v1alpha1.HTTPScalingSetList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.HTTPScalingSetList{ListMeta: obj.(*v1alpha1.HTTPScalingSetList).ListMeta} + for _, item := range obj.(*v1alpha1.HTTPScalingSetList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested hTTPScalingSets. +func (c *FakeHTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(httpscalingsetsResource, c.ns, opts)) + +} + +// Create takes the representation of a hTTPScalingSet and creates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *FakeHTTPScalingSets) Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(httpscalingsetsResource, c.ns, hTTPScalingSet), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// Update takes the representation of a hTTPScalingSet and updates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *FakeHTTPScalingSets) Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(httpscalingsetsResource, c.ns, hTTPScalingSet), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeHTTPScalingSets) UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(httpscalingsetsResource, "status", c.ns, hTTPScalingSet), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} + +// Delete takes name of the hTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *FakeHTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(httpscalingsetsResource, c.ns, name, opts), &v1alpha1.HTTPScalingSet{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeHTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(httpscalingsetsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.HTTPScalingSetList{}) + return err +} + +// Patch applies the patch and returns the patched hTTPScalingSet. +func (c *FakeHTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScalingSet, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(httpscalingsetsResource, c.ns, name, pt, data, subresources...), &v1alpha1.HTTPScalingSet{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.HTTPScalingSet), err +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/generated_expansion.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/generated_expansion.go index cfff4742..fd9e32fb 100644 --- a/operator/generated/clientset/versioned/typed/http/v1alpha1/generated_expansion.go +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/generated_expansion.go @@ -18,4 +18,8 @@ limitations under the License. package v1alpha1 +type ClusterHTTPScalingSetExpansion interface{} + type HTTPScaledObjectExpansion interface{} + +type HTTPScalingSetExpansion interface{} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/http_client.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/http_client.go index 6a3766e4..c45d44a9 100644 --- a/operator/generated/clientset/versioned/typed/http/v1alpha1/http_client.go +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/http_client.go @@ -28,7 +28,9 @@ import ( type HttpV1alpha1Interface interface { RESTClient() rest.Interface + ClusterHTTPScalingSetsGetter HTTPScaledObjectsGetter + HTTPScalingSetsGetter } // HttpV1alpha1Client is used to interact with features provided by the http group. @@ -36,10 +38,18 @@ type HttpV1alpha1Client struct { restClient rest.Interface } +func (c *HttpV1alpha1Client) ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetInterface { + return newClusterHTTPScalingSets(c, namespace) +} + func (c *HttpV1alpha1Client) HTTPScaledObjects(namespace string) HTTPScaledObjectInterface { return newHTTPScaledObjects(c, namespace) } +func (c *HttpV1alpha1Client) HTTPScalingSets(namespace string) HTTPScalingSetInterface { + return newHTTPScalingSets(c, namespace) +} + // NewForConfig creates a new HttpV1alpha1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/httpscalingset.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/httpscalingset.go new file mode 100644 index 00000000..644c24fe --- /dev/null +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/httpscalingset.go @@ -0,0 +1,195 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + scheme "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// HTTPScalingSetsGetter has a method to return a HTTPScalingSetInterface. +// A group's client should implement this interface. +type HTTPScalingSetsGetter interface { + HTTPScalingSets(namespace string) HTTPScalingSetInterface +} + +// HTTPScalingSetInterface has methods to work with HTTPScalingSet resources. +type HTTPScalingSetInterface interface { + Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (*v1alpha1.HTTPScalingSet, error) + Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) + UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.HTTPScalingSet, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HTTPScalingSetList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScalingSet, err error) + HTTPScalingSetExpansion +} + +// hTTPScalingSets implements HTTPScalingSetInterface +type hTTPScalingSets struct { + client rest.Interface + ns string +} + +// newHTTPScalingSets returns a HTTPScalingSets +func newHTTPScalingSets(c *HttpV1alpha1Client, namespace string) *hTTPScalingSets { + return &hTTPScalingSets{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the hTTPScalingSet, and returns the corresponding hTTPScalingSet object, and an error if there is any. +func (c *hTTPScalingSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of HTTPScalingSets that match those selectors. +func (c *hTTPScalingSets) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScalingSetList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.HTTPScalingSetList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested hTTPScalingSets. +func (c *hTTPScalingSets) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a hTTPScalingSet and creates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *hTTPScalingSets) Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Post(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a hTTPScalingSet and updates it. Returns the server's representation of the hTTPScalingSet, and an error, if there is any. +func (c *hTTPScalingSets) Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(hTTPScalingSet.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *hTTPScalingSets) UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Put(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(hTTPScalingSet.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hTTPScalingSet). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the hTTPScalingSet and deletes it. Returns an error if one occurs. +func (c *hTTPScalingSets) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *hTTPScalingSets) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("httpscalingsets"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched hTTPScalingSet. +func (c *hTTPScalingSets) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScalingSet, err error) { + result = &v1alpha1.HTTPScalingSet{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("httpscalingsets"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/clusterhttpscalingset.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/clusterhttpscalingset.go new file mode 100644 index 00000000..959f3e71 --- /dev/null +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/clusterhttpscalingset.go @@ -0,0 +1,237 @@ +// /* +// Copyright 2023 The KEDA Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: operator/generated/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go +// +// Generated by this command: +// +// mockgen -copyright_file=hack/boilerplate.go.txt -destination=operator/generated/clientset/versioned/typed/http/v1alpha1/mock/clusterhttpscalingset.go -package=mock -source=operator/generated/clientset/versioned/typed/http/v1alpha1/clusterhttpscalingset.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + v1alpha10 "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/typed/http/v1alpha1" + gomock "go.uber.org/mock/gomock" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" +) + +// MockClusterHTTPScalingSetsGetter is a mock of ClusterHTTPScalingSetsGetter interface. +type MockClusterHTTPScalingSetsGetter struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetsGetterMockRecorder +} + +// MockClusterHTTPScalingSetsGetterMockRecorder is the mock recorder for MockClusterHTTPScalingSetsGetter. +type MockClusterHTTPScalingSetsGetterMockRecorder struct { + mock *MockClusterHTTPScalingSetsGetter +} + +// NewMockClusterHTTPScalingSetsGetter creates a new mock instance. +func NewMockClusterHTTPScalingSetsGetter(ctrl *gomock.Controller) *MockClusterHTTPScalingSetsGetter { + mock := &MockClusterHTTPScalingSetsGetter{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetsGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetsGetter) EXPECT() *MockClusterHTTPScalingSetsGetterMockRecorder { + return m.recorder +} + +// ClusterHTTPScalingSets mocks base method. +func (m *MockClusterHTTPScalingSetsGetter) ClusterHTTPScalingSets(namespace string) v1alpha10.ClusterHTTPScalingSetInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClusterHTTPScalingSets", namespace) + ret0, _ := ret[0].(v1alpha10.ClusterHTTPScalingSetInterface) + return ret0 +} + +// ClusterHTTPScalingSets indicates an expected call of ClusterHTTPScalingSets. +func (mr *MockClusterHTTPScalingSetsGetterMockRecorder) ClusterHTTPScalingSets(namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterHTTPScalingSets", reflect.TypeOf((*MockClusterHTTPScalingSetsGetter)(nil).ClusterHTTPScalingSets), namespace) +} + +// MockClusterHTTPScalingSetInterface is a mock of ClusterHTTPScalingSetInterface interface. +type MockClusterHTTPScalingSetInterface struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetInterfaceMockRecorder +} + +// MockClusterHTTPScalingSetInterfaceMockRecorder is the mock recorder for MockClusterHTTPScalingSetInterface. +type MockClusterHTTPScalingSetInterfaceMockRecorder struct { + mock *MockClusterHTTPScalingSetInterface +} + +// NewMockClusterHTTPScalingSetInterface creates a new mock instance. +func NewMockClusterHTTPScalingSetInterface(ctrl *gomock.Controller) *MockClusterHTTPScalingSetInterface { + mock := &MockClusterHTTPScalingSetInterface{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetInterface) EXPECT() *MockClusterHTTPScalingSetInterfaceMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockClusterHTTPScalingSetInterface) Create(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.CreateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, clusterHTTPScalingSet, opts) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) Create(ctx, clusterHTTPScalingSet, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).Create), ctx, clusterHTTPScalingSet, opts) +} + +// Delete mocks base method. +func (m *MockClusterHTTPScalingSetInterface) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, name, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) Delete(ctx, name, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).Delete), ctx, name, opts) +} + +// DeleteCollection mocks base method. +func (m *MockClusterHTTPScalingSetInterface) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCollection", ctx, opts, listOpts) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCollection indicates an expected call of DeleteCollection. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) DeleteCollection(ctx, opts, listOpts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).DeleteCollection), ctx, opts, listOpts) +} + +// Get mocks base method. +func (m *MockClusterHTTPScalingSetInterface) Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, name, opts) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) Get(ctx, name, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).Get), ctx, name, opts) +} + +// List mocks base method. +func (m *MockClusterHTTPScalingSetInterface) List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ClusterHTTPScalingSetList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, opts) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSetList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) List(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).List), ctx, opts) +} + +// Patch mocks base method. +func (m *MockClusterHTTPScalingSetInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, name, pt, data, opts} + for _, a := range subresources { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Patch indicates an expected call of Patch. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) Patch(ctx, name, pt, data, opts any, subresources ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, name, pt, data, opts}, subresources...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).Patch), varargs...) +} + +// Update mocks base method. +func (m *MockClusterHTTPScalingSetInterface) Update(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, clusterHTTPScalingSet, opts) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) Update(ctx, clusterHTTPScalingSet, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).Update), ctx, clusterHTTPScalingSet, opts) +} + +// UpdateStatus mocks base method. +func (m *MockClusterHTTPScalingSetInterface) UpdateStatus(ctx context.Context, clusterHTTPScalingSet *v1alpha1.ClusterHTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateStatus", ctx, clusterHTTPScalingSet, opts) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateStatus indicates an expected call of UpdateStatus. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) UpdateStatus(ctx, clusterHTTPScalingSet, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).UpdateStatus), ctx, clusterHTTPScalingSet, opts) +} + +// Watch mocks base method. +func (m *MockClusterHTTPScalingSetInterface) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", ctx, opts) + ret0, _ := ret[0].(watch.Interface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Watch indicates an expected call of Watch. +func (mr *MockClusterHTTPScalingSetInterfaceMockRecorder) Watch(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockClusterHTTPScalingSetInterface)(nil).Watch), ctx, opts) +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/generated_expansion.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/generated_expansion.go index d6ebb862..d6f587ac 100644 --- a/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/generated_expansion.go +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/generated_expansion.go @@ -30,6 +30,29 @@ import ( gomock "go.uber.org/mock/gomock" ) +// MockClusterHTTPScalingSetExpansion is a mock of ClusterHTTPScalingSetExpansion interface. +type MockClusterHTTPScalingSetExpansion struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetExpansionMockRecorder +} + +// MockClusterHTTPScalingSetExpansionMockRecorder is the mock recorder for MockClusterHTTPScalingSetExpansion. +type MockClusterHTTPScalingSetExpansionMockRecorder struct { + mock *MockClusterHTTPScalingSetExpansion +} + +// NewMockClusterHTTPScalingSetExpansion creates a new mock instance. +func NewMockClusterHTTPScalingSetExpansion(ctrl *gomock.Controller) *MockClusterHTTPScalingSetExpansion { + mock := &MockClusterHTTPScalingSetExpansion{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetExpansionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetExpansion) EXPECT() *MockClusterHTTPScalingSetExpansionMockRecorder { + return m.recorder +} + // MockHTTPScaledObjectExpansion is a mock of HTTPScaledObjectExpansion interface. type MockHTTPScaledObjectExpansion struct { ctrl *gomock.Controller @@ -52,3 +75,26 @@ func NewMockHTTPScaledObjectExpansion(ctrl *gomock.Controller) *MockHTTPScaledOb func (m *MockHTTPScaledObjectExpansion) EXPECT() *MockHTTPScaledObjectExpansionMockRecorder { return m.recorder } + +// MockHTTPScalingSetExpansion is a mock of HTTPScalingSetExpansion interface. +type MockHTTPScalingSetExpansion struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetExpansionMockRecorder +} + +// MockHTTPScalingSetExpansionMockRecorder is the mock recorder for MockHTTPScalingSetExpansion. +type MockHTTPScalingSetExpansionMockRecorder struct { + mock *MockHTTPScalingSetExpansion +} + +// NewMockHTTPScalingSetExpansion creates a new mock instance. +func NewMockHTTPScalingSetExpansion(ctrl *gomock.Controller) *MockHTTPScalingSetExpansion { + mock := &MockHTTPScalingSetExpansion{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetExpansionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetExpansion) EXPECT() *MockHTTPScalingSetExpansionMockRecorder { + return m.recorder +} diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/http_client.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/http_client.go index d65b725b..a9f77567 100644 --- a/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/http_client.go +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/http_client.go @@ -57,6 +57,20 @@ func (m *MockHttpV1alpha1Interface) EXPECT() *MockHttpV1alpha1InterfaceMockRecor return m.recorder } +// ClusterHTTPScalingSets mocks base method. +func (m *MockHttpV1alpha1Interface) ClusterHTTPScalingSets(namespace string) v1alpha1.ClusterHTTPScalingSetInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClusterHTTPScalingSets", namespace) + ret0, _ := ret[0].(v1alpha1.ClusterHTTPScalingSetInterface) + return ret0 +} + +// ClusterHTTPScalingSets indicates an expected call of ClusterHTTPScalingSets. +func (mr *MockHttpV1alpha1InterfaceMockRecorder) ClusterHTTPScalingSets(namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterHTTPScalingSets", reflect.TypeOf((*MockHttpV1alpha1Interface)(nil).ClusterHTTPScalingSets), namespace) +} + // HTTPScaledObjects mocks base method. func (m *MockHttpV1alpha1Interface) HTTPScaledObjects(namespace string) v1alpha1.HTTPScaledObjectInterface { m.ctrl.T.Helper() @@ -71,6 +85,20 @@ func (mr *MockHttpV1alpha1InterfaceMockRecorder) HTTPScaledObjects(namespace any return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPScaledObjects", reflect.TypeOf((*MockHttpV1alpha1Interface)(nil).HTTPScaledObjects), namespace) } +// HTTPScalingSets mocks base method. +func (m *MockHttpV1alpha1Interface) HTTPScalingSets(namespace string) v1alpha1.HTTPScalingSetInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HTTPScalingSets", namespace) + ret0, _ := ret[0].(v1alpha1.HTTPScalingSetInterface) + return ret0 +} + +// HTTPScalingSets indicates an expected call of HTTPScalingSets. +func (mr *MockHttpV1alpha1InterfaceMockRecorder) HTTPScalingSets(namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPScalingSets", reflect.TypeOf((*MockHttpV1alpha1Interface)(nil).HTTPScalingSets), namespace) +} + // RESTClient mocks base method. func (m *MockHttpV1alpha1Interface) RESTClient() rest.Interface { m.ctrl.T.Helper() diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/httpscalingset.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/httpscalingset.go new file mode 100644 index 00000000..d6684fc2 --- /dev/null +++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/mock/httpscalingset.go @@ -0,0 +1,237 @@ +// /* +// Copyright 2023 The KEDA Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: operator/generated/clientset/versioned/typed/http/v1alpha1/httpscalingset.go +// +// Generated by this command: +// +// mockgen -copyright_file=hack/boilerplate.go.txt -destination=operator/generated/clientset/versioned/typed/http/v1alpha1/mock/httpscalingset.go -package=mock -source=operator/generated/clientset/versioned/typed/http/v1alpha1/httpscalingset.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + v1alpha10 "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/typed/http/v1alpha1" + gomock "go.uber.org/mock/gomock" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" +) + +// MockHTTPScalingSetsGetter is a mock of HTTPScalingSetsGetter interface. +type MockHTTPScalingSetsGetter struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetsGetterMockRecorder +} + +// MockHTTPScalingSetsGetterMockRecorder is the mock recorder for MockHTTPScalingSetsGetter. +type MockHTTPScalingSetsGetterMockRecorder struct { + mock *MockHTTPScalingSetsGetter +} + +// NewMockHTTPScalingSetsGetter creates a new mock instance. +func NewMockHTTPScalingSetsGetter(ctrl *gomock.Controller) *MockHTTPScalingSetsGetter { + mock := &MockHTTPScalingSetsGetter{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetsGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetsGetter) EXPECT() *MockHTTPScalingSetsGetterMockRecorder { + return m.recorder +} + +// HTTPScalingSets mocks base method. +func (m *MockHTTPScalingSetsGetter) HTTPScalingSets(namespace string) v1alpha10.HTTPScalingSetInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HTTPScalingSets", namespace) + ret0, _ := ret[0].(v1alpha10.HTTPScalingSetInterface) + return ret0 +} + +// HTTPScalingSets indicates an expected call of HTTPScalingSets. +func (mr *MockHTTPScalingSetsGetterMockRecorder) HTTPScalingSets(namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPScalingSets", reflect.TypeOf((*MockHTTPScalingSetsGetter)(nil).HTTPScalingSets), namespace) +} + +// MockHTTPScalingSetInterface is a mock of HTTPScalingSetInterface interface. +type MockHTTPScalingSetInterface struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetInterfaceMockRecorder +} + +// MockHTTPScalingSetInterfaceMockRecorder is the mock recorder for MockHTTPScalingSetInterface. +type MockHTTPScalingSetInterfaceMockRecorder struct { + mock *MockHTTPScalingSetInterface +} + +// NewMockHTTPScalingSetInterface creates a new mock instance. +func NewMockHTTPScalingSetInterface(ctrl *gomock.Controller) *MockHTTPScalingSetInterface { + mock := &MockHTTPScalingSetInterface{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetInterface) EXPECT() *MockHTTPScalingSetInterfaceMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockHTTPScalingSetInterface) Create(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.CreateOptions) (*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, hTTPScalingSet, opts) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) Create(ctx, hTTPScalingSet, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).Create), ctx, hTTPScalingSet, opts) +} + +// Delete mocks base method. +func (m *MockHTTPScalingSetInterface) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, name, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) Delete(ctx, name, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).Delete), ctx, name, opts) +} + +// DeleteCollection mocks base method. +func (m *MockHTTPScalingSetInterface) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCollection", ctx, opts, listOpts) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCollection indicates an expected call of DeleteCollection. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) DeleteCollection(ctx, opts, listOpts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).DeleteCollection), ctx, opts, listOpts) +} + +// Get mocks base method. +func (m *MockHTTPScalingSetInterface) Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, name, opts) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) Get(ctx, name, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).Get), ctx, name, opts) +} + +// List mocks base method. +func (m *MockHTTPScalingSetInterface) List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HTTPScalingSetList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, opts) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSetList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) List(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).List), ctx, opts) +} + +// Patch mocks base method. +func (m *MockHTTPScalingSetInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, name, pt, data, opts} + for _, a := range subresources { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Patch indicates an expected call of Patch. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) Patch(ctx, name, pt, data, opts any, subresources ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, name, pt, data, opts}, subresources...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).Patch), varargs...) +} + +// Update mocks base method. +func (m *MockHTTPScalingSetInterface) Update(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, hTTPScalingSet, opts) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) Update(ctx, hTTPScalingSet, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).Update), ctx, hTTPScalingSet, opts) +} + +// UpdateStatus mocks base method. +func (m *MockHTTPScalingSetInterface) UpdateStatus(ctx context.Context, hTTPScalingSet *v1alpha1.HTTPScalingSet, opts v1.UpdateOptions) (*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateStatus", ctx, hTTPScalingSet, opts) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateStatus indicates an expected call of UpdateStatus. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) UpdateStatus(ctx, hTTPScalingSet, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).UpdateStatus), ctx, hTTPScalingSet, opts) +} + +// Watch mocks base method. +func (m *MockHTTPScalingSetInterface) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", ctx, opts) + ret0, _ := ret[0].(watch.Interface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Watch indicates an expected call of Watch. +func (mr *MockHTTPScalingSetInterfaceMockRecorder) Watch(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockHTTPScalingSetInterface)(nil).Watch), ctx, opts) +} diff --git a/operator/generated/informers/externalversions/generic.go b/operator/generated/informers/externalversions/generic.go index b190b7fe..20c59267 100644 --- a/operator/generated/informers/externalversions/generic.go +++ b/operator/generated/informers/externalversions/generic.go @@ -53,8 +53,12 @@ func (f *genericInformer) Lister() cache.GenericLister { func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=http, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("clusterhttpscalingsets"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().ClusterHTTPScalingSets().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("httpscaledobjects"): return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().HTTPScaledObjects().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("httpscalingsets"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().HTTPScalingSets().Informer()}, nil } diff --git a/operator/generated/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go b/operator/generated/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go new file mode 100644 index 00000000..cddbfc78 --- /dev/null +++ b/operator/generated/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + versioned "github.com/kedacore/http-add-on/operator/generated/clientset/versioned" + internalinterfaces "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/kedacore/http-add-on/operator/generated/listers/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ClusterHTTPScalingSetInformer provides access to a shared informer and lister for +// ClusterHTTPScalingSets. +type ClusterHTTPScalingSetInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ClusterHTTPScalingSetLister +} + +type clusterHTTPScalingSetInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewClusterHTTPScalingSetInformer constructs a new informer for ClusterHTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewClusterHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterHTTPScalingSetInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterHTTPScalingSetInformer constructs a new informer for ClusterHTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredClusterHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().ClusterHTTPScalingSets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().ClusterHTTPScalingSets(namespace).Watch(context.TODO(), options) + }, + }, + &httpv1alpha1.ClusterHTTPScalingSet{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterHTTPScalingSetInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterHTTPScalingSetInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterHTTPScalingSetInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&httpv1alpha1.ClusterHTTPScalingSet{}, f.defaultInformer) +} + +func (f *clusterHTTPScalingSetInformer) Lister() v1alpha1.ClusterHTTPScalingSetLister { + return v1alpha1.NewClusterHTTPScalingSetLister(f.Informer().GetIndexer()) +} diff --git a/operator/generated/informers/externalversions/http/v1alpha1/httpscalingset.go b/operator/generated/informers/externalversions/http/v1alpha1/httpscalingset.go new file mode 100644 index 00000000..acb89486 --- /dev/null +++ b/operator/generated/informers/externalversions/http/v1alpha1/httpscalingset.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + versioned "github.com/kedacore/http-add-on/operator/generated/clientset/versioned" + internalinterfaces "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/kedacore/http-add-on/operator/generated/listers/http/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// HTTPScalingSetInformer provides access to a shared informer and lister for +// HTTPScalingSets. +type HTTPScalingSetInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.HTTPScalingSetLister +} + +type hTTPScalingSetInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewHTTPScalingSetInformer constructs a new informer for HTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredHTTPScalingSetInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredHTTPScalingSetInformer constructs a new informer for HTTPScalingSet type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredHTTPScalingSetInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().HTTPScalingSets(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.HttpV1alpha1().HTTPScalingSets(namespace).Watch(context.TODO(), options) + }, + }, + &httpv1alpha1.HTTPScalingSet{}, + resyncPeriod, + indexers, + ) +} + +func (f *hTTPScalingSetInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredHTTPScalingSetInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *hTTPScalingSetInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&httpv1alpha1.HTTPScalingSet{}, f.defaultInformer) +} + +func (f *hTTPScalingSetInformer) Lister() v1alpha1.HTTPScalingSetLister { + return v1alpha1.NewHTTPScalingSetLister(f.Informer().GetIndexer()) +} diff --git a/operator/generated/informers/externalversions/http/v1alpha1/interface.go b/operator/generated/informers/externalversions/http/v1alpha1/interface.go index 1f0fb945..0ca039af 100644 --- a/operator/generated/informers/externalversions/http/v1alpha1/interface.go +++ b/operator/generated/informers/externalversions/http/v1alpha1/interface.go @@ -24,8 +24,12 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // ClusterHTTPScalingSets returns a ClusterHTTPScalingSetInformer. + ClusterHTTPScalingSets() ClusterHTTPScalingSetInformer // HTTPScaledObjects returns a HTTPScaledObjectInformer. HTTPScaledObjects() HTTPScaledObjectInformer + // HTTPScalingSets returns a HTTPScalingSetInformer. + HTTPScalingSets() HTTPScalingSetInformer } type version struct { @@ -39,7 +43,17 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// ClusterHTTPScalingSets returns a ClusterHTTPScalingSetInformer. +func (v *version) ClusterHTTPScalingSets() ClusterHTTPScalingSetInformer { + return &clusterHTTPScalingSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // HTTPScaledObjects returns a HTTPScaledObjectInformer. func (v *version) HTTPScaledObjects() HTTPScaledObjectInformer { return &hTTPScaledObjectInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// HTTPScalingSets returns a HTTPScalingSetInformer. +func (v *version) HTTPScalingSets() HTTPScalingSetInformer { + return &hTTPScalingSetInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/operator/generated/informers/externalversions/http/v1alpha1/mock/clusterhttpscalingset.go b/operator/generated/informers/externalversions/http/v1alpha1/mock/clusterhttpscalingset.go new file mode 100644 index 00000000..f97a7ed9 --- /dev/null +++ b/operator/generated/informers/externalversions/http/v1alpha1/mock/clusterhttpscalingset.go @@ -0,0 +1,86 @@ +// /* +// Copyright 2023 The KEDA Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: operator/generated/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go +// +// Generated by this command: +// +// mockgen -copyright_file=hack/boilerplate.go.txt -destination=operator/generated/informers/externalversions/http/v1alpha1/mock/clusterhttpscalingset.go -package=mock -source=operator/generated/informers/externalversions/http/v1alpha1/clusterhttpscalingset.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + v1alpha1 "github.com/kedacore/http-add-on/operator/generated/listers/http/v1alpha1" + gomock "go.uber.org/mock/gomock" + cache "k8s.io/client-go/tools/cache" +) + +// MockClusterHTTPScalingSetInformer is a mock of ClusterHTTPScalingSetInformer interface. +type MockClusterHTTPScalingSetInformer struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetInformerMockRecorder +} + +// MockClusterHTTPScalingSetInformerMockRecorder is the mock recorder for MockClusterHTTPScalingSetInformer. +type MockClusterHTTPScalingSetInformerMockRecorder struct { + mock *MockClusterHTTPScalingSetInformer +} + +// NewMockClusterHTTPScalingSetInformer creates a new mock instance. +func NewMockClusterHTTPScalingSetInformer(ctrl *gomock.Controller) *MockClusterHTTPScalingSetInformer { + mock := &MockClusterHTTPScalingSetInformer{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetInformerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetInformer) EXPECT() *MockClusterHTTPScalingSetInformerMockRecorder { + return m.recorder +} + +// Informer mocks base method. +func (m *MockClusterHTTPScalingSetInformer) Informer() cache.SharedIndexInformer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Informer") + ret0, _ := ret[0].(cache.SharedIndexInformer) + return ret0 +} + +// Informer indicates an expected call of Informer. +func (mr *MockClusterHTTPScalingSetInformerMockRecorder) Informer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Informer", reflect.TypeOf((*MockClusterHTTPScalingSetInformer)(nil).Informer)) +} + +// Lister mocks base method. +func (m *MockClusterHTTPScalingSetInformer) Lister() v1alpha1.ClusterHTTPScalingSetLister { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Lister") + ret0, _ := ret[0].(v1alpha1.ClusterHTTPScalingSetLister) + return ret0 +} + +// Lister indicates an expected call of Lister. +func (mr *MockClusterHTTPScalingSetInformerMockRecorder) Lister() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lister", reflect.TypeOf((*MockClusterHTTPScalingSetInformer)(nil).Lister)) +} diff --git a/operator/generated/informers/externalversions/http/v1alpha1/mock/httpscalingset.go b/operator/generated/informers/externalversions/http/v1alpha1/mock/httpscalingset.go new file mode 100644 index 00000000..5f2f6656 --- /dev/null +++ b/operator/generated/informers/externalversions/http/v1alpha1/mock/httpscalingset.go @@ -0,0 +1,86 @@ +// /* +// Copyright 2023 The KEDA Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: operator/generated/informers/externalversions/http/v1alpha1/httpscalingset.go +// +// Generated by this command: +// +// mockgen -copyright_file=hack/boilerplate.go.txt -destination=operator/generated/informers/externalversions/http/v1alpha1/mock/httpscalingset.go -package=mock -source=operator/generated/informers/externalversions/http/v1alpha1/httpscalingset.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + v1alpha1 "github.com/kedacore/http-add-on/operator/generated/listers/http/v1alpha1" + gomock "go.uber.org/mock/gomock" + cache "k8s.io/client-go/tools/cache" +) + +// MockHTTPScalingSetInformer is a mock of HTTPScalingSetInformer interface. +type MockHTTPScalingSetInformer struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetInformerMockRecorder +} + +// MockHTTPScalingSetInformerMockRecorder is the mock recorder for MockHTTPScalingSetInformer. +type MockHTTPScalingSetInformerMockRecorder struct { + mock *MockHTTPScalingSetInformer +} + +// NewMockHTTPScalingSetInformer creates a new mock instance. +func NewMockHTTPScalingSetInformer(ctrl *gomock.Controller) *MockHTTPScalingSetInformer { + mock := &MockHTTPScalingSetInformer{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetInformerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetInformer) EXPECT() *MockHTTPScalingSetInformerMockRecorder { + return m.recorder +} + +// Informer mocks base method. +func (m *MockHTTPScalingSetInformer) Informer() cache.SharedIndexInformer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Informer") + ret0, _ := ret[0].(cache.SharedIndexInformer) + return ret0 +} + +// Informer indicates an expected call of Informer. +func (mr *MockHTTPScalingSetInformerMockRecorder) Informer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Informer", reflect.TypeOf((*MockHTTPScalingSetInformer)(nil).Informer)) +} + +// Lister mocks base method. +func (m *MockHTTPScalingSetInformer) Lister() v1alpha1.HTTPScalingSetLister { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Lister") + ret0, _ := ret[0].(v1alpha1.HTTPScalingSetLister) + return ret0 +} + +// Lister indicates an expected call of Lister. +func (mr *MockHTTPScalingSetInformerMockRecorder) Lister() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lister", reflect.TypeOf((*MockHTTPScalingSetInformer)(nil).Lister)) +} diff --git a/operator/generated/informers/externalversions/http/v1alpha1/mock/interface.go b/operator/generated/informers/externalversions/http/v1alpha1/mock/interface.go index 60081225..537068dd 100644 --- a/operator/generated/informers/externalversions/http/v1alpha1/mock/interface.go +++ b/operator/generated/informers/externalversions/http/v1alpha1/mock/interface.go @@ -56,6 +56,20 @@ func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { return m.recorder } +// ClusterHTTPScalingSets mocks base method. +func (m *MockInterface) ClusterHTTPScalingSets() v1alpha1.ClusterHTTPScalingSetInformer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClusterHTTPScalingSets") + ret0, _ := ret[0].(v1alpha1.ClusterHTTPScalingSetInformer) + return ret0 +} + +// ClusterHTTPScalingSets indicates an expected call of ClusterHTTPScalingSets. +func (mr *MockInterfaceMockRecorder) ClusterHTTPScalingSets() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterHTTPScalingSets", reflect.TypeOf((*MockInterface)(nil).ClusterHTTPScalingSets)) +} + // HTTPScaledObjects mocks base method. func (m *MockInterface) HTTPScaledObjects() v1alpha1.HTTPScaledObjectInformer { m.ctrl.T.Helper() @@ -69,3 +83,17 @@ func (mr *MockInterfaceMockRecorder) HTTPScaledObjects() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPScaledObjects", reflect.TypeOf((*MockInterface)(nil).HTTPScaledObjects)) } + +// HTTPScalingSets mocks base method. +func (m *MockInterface) HTTPScalingSets() v1alpha1.HTTPScalingSetInformer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HTTPScalingSets") + ret0, _ := ret[0].(v1alpha1.HTTPScalingSetInformer) + return ret0 +} + +// HTTPScalingSets indicates an expected call of HTTPScalingSets. +func (mr *MockInterfaceMockRecorder) HTTPScalingSets() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPScalingSets", reflect.TypeOf((*MockInterface)(nil).HTTPScalingSets)) +} diff --git a/operator/generated/listers/http/v1alpha1/clusterhttpscalingset.go b/operator/generated/listers/http/v1alpha1/clusterhttpscalingset.go new file mode 100644 index 00000000..5c02c456 --- /dev/null +++ b/operator/generated/listers/http/v1alpha1/clusterhttpscalingset.go @@ -0,0 +1,99 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterHTTPScalingSetLister helps list ClusterHTTPScalingSets. +// All objects returned here must be treated as read-only. +type ClusterHTTPScalingSetLister interface { + // List lists all ClusterHTTPScalingSets in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) + // ClusterHTTPScalingSets returns an object that can list and get ClusterHTTPScalingSets. + ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetNamespaceLister + ClusterHTTPScalingSetListerExpansion +} + +// clusterHTTPScalingSetLister implements the ClusterHTTPScalingSetLister interface. +type clusterHTTPScalingSetLister struct { + indexer cache.Indexer +} + +// NewClusterHTTPScalingSetLister returns a new ClusterHTTPScalingSetLister. +func NewClusterHTTPScalingSetLister(indexer cache.Indexer) ClusterHTTPScalingSetLister { + return &clusterHTTPScalingSetLister{indexer: indexer} +} + +// List lists all ClusterHTTPScalingSets in the indexer. +func (s *clusterHTTPScalingSetLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterHTTPScalingSet)) + }) + return ret, err +} + +// ClusterHTTPScalingSets returns an object that can list and get ClusterHTTPScalingSets. +func (s *clusterHTTPScalingSetLister) ClusterHTTPScalingSets(namespace string) ClusterHTTPScalingSetNamespaceLister { + return clusterHTTPScalingSetNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ClusterHTTPScalingSetNamespaceLister helps list and get ClusterHTTPScalingSets. +// All objects returned here must be treated as read-only. +type ClusterHTTPScalingSetNamespaceLister interface { + // List lists all ClusterHTTPScalingSets in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) + // Get retrieves the ClusterHTTPScalingSet from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.ClusterHTTPScalingSet, error) + ClusterHTTPScalingSetNamespaceListerExpansion +} + +// clusterHTTPScalingSetNamespaceLister implements the ClusterHTTPScalingSetNamespaceLister +// interface. +type clusterHTTPScalingSetNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ClusterHTTPScalingSets in the indexer for a given namespace. +func (s clusterHTTPScalingSetNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterHTTPScalingSet, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterHTTPScalingSet)) + }) + return ret, err +} + +// Get retrieves the ClusterHTTPScalingSet from the indexer for a given namespace and name. +func (s clusterHTTPScalingSetNamespaceLister) Get(name string) (*v1alpha1.ClusterHTTPScalingSet, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("clusterhttpscalingset"), name) + } + return obj.(*v1alpha1.ClusterHTTPScalingSet), nil +} diff --git a/operator/generated/listers/http/v1alpha1/expansion_generated.go b/operator/generated/listers/http/v1alpha1/expansion_generated.go index 85dc9c17..ed0d0468 100644 --- a/operator/generated/listers/http/v1alpha1/expansion_generated.go +++ b/operator/generated/listers/http/v1alpha1/expansion_generated.go @@ -18,6 +18,14 @@ limitations under the License. package v1alpha1 +// ClusterHTTPScalingSetListerExpansion allows custom methods to be added to +// ClusterHTTPScalingSetLister. +type ClusterHTTPScalingSetListerExpansion interface{} + +// ClusterHTTPScalingSetNamespaceListerExpansion allows custom methods to be added to +// ClusterHTTPScalingSetNamespaceLister. +type ClusterHTTPScalingSetNamespaceListerExpansion interface{} + // HTTPScaledObjectListerExpansion allows custom methods to be added to // HTTPScaledObjectLister. type HTTPScaledObjectListerExpansion interface{} @@ -25,3 +33,11 @@ type HTTPScaledObjectListerExpansion interface{} // HTTPScaledObjectNamespaceListerExpansion allows custom methods to be added to // HTTPScaledObjectNamespaceLister. type HTTPScaledObjectNamespaceListerExpansion interface{} + +// HTTPScalingSetListerExpansion allows custom methods to be added to +// HTTPScalingSetLister. +type HTTPScalingSetListerExpansion interface{} + +// HTTPScalingSetNamespaceListerExpansion allows custom methods to be added to +// HTTPScalingSetNamespaceLister. +type HTTPScalingSetNamespaceListerExpansion interface{} diff --git a/operator/generated/listers/http/v1alpha1/httpscalingset.go b/operator/generated/listers/http/v1alpha1/httpscalingset.go new file mode 100644 index 00000000..ea75aeb0 --- /dev/null +++ b/operator/generated/listers/http/v1alpha1/httpscalingset.go @@ -0,0 +1,99 @@ +/* +Copyright 2023 The KEDA Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// HTTPScalingSetLister helps list HTTPScalingSets. +// All objects returned here must be treated as read-only. +type HTTPScalingSetLister interface { + // List lists all HTTPScalingSets in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) + // HTTPScalingSets returns an object that can list and get HTTPScalingSets. + HTTPScalingSets(namespace string) HTTPScalingSetNamespaceLister + HTTPScalingSetListerExpansion +} + +// hTTPScalingSetLister implements the HTTPScalingSetLister interface. +type hTTPScalingSetLister struct { + indexer cache.Indexer +} + +// NewHTTPScalingSetLister returns a new HTTPScalingSetLister. +func NewHTTPScalingSetLister(indexer cache.Indexer) HTTPScalingSetLister { + return &hTTPScalingSetLister{indexer: indexer} +} + +// List lists all HTTPScalingSets in the indexer. +func (s *hTTPScalingSetLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPScalingSet)) + }) + return ret, err +} + +// HTTPScalingSets returns an object that can list and get HTTPScalingSets. +func (s *hTTPScalingSetLister) HTTPScalingSets(namespace string) HTTPScalingSetNamespaceLister { + return hTTPScalingSetNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// HTTPScalingSetNamespaceLister helps list and get HTTPScalingSets. +// All objects returned here must be treated as read-only. +type HTTPScalingSetNamespaceLister interface { + // List lists all HTTPScalingSets in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) + // Get retrieves the HTTPScalingSet from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.HTTPScalingSet, error) + HTTPScalingSetNamespaceListerExpansion +} + +// hTTPScalingSetNamespaceLister implements the HTTPScalingSetNamespaceLister +// interface. +type hTTPScalingSetNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all HTTPScalingSets in the indexer for a given namespace. +func (s hTTPScalingSetNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScalingSet, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.HTTPScalingSet)) + }) + return ret, err +} + +// Get retrieves the HTTPScalingSet from the indexer for a given namespace and name. +func (s hTTPScalingSetNamespaceLister) Get(name string) (*v1alpha1.HTTPScalingSet, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("httpscalingset"), name) + } + return obj.(*v1alpha1.HTTPScalingSet), nil +} diff --git a/operator/generated/listers/http/v1alpha1/mock/clusterhttpscalingset.go b/operator/generated/listers/http/v1alpha1/mock/clusterhttpscalingset.go new file mode 100644 index 00000000..f820aa16 --- /dev/null +++ b/operator/generated/listers/http/v1alpha1/mock/clusterhttpscalingset.go @@ -0,0 +1,141 @@ +// /* +// Copyright 2023 The KEDA Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: operator/generated/listers/http/v1alpha1/clusterhttpscalingset.go +// +// Generated by this command: +// +// mockgen -copyright_file=hack/boilerplate.go.txt -destination=operator/generated/listers/http/v1alpha1/mock/clusterhttpscalingset.go -package=mock -source=operator/generated/listers/http/v1alpha1/clusterhttpscalingset.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + v1alpha10 "github.com/kedacore/http-add-on/operator/generated/listers/http/v1alpha1" + gomock "go.uber.org/mock/gomock" + labels "k8s.io/apimachinery/pkg/labels" +) + +// MockClusterHTTPScalingSetLister is a mock of ClusterHTTPScalingSetLister interface. +type MockClusterHTTPScalingSetLister struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetListerMockRecorder +} + +// MockClusterHTTPScalingSetListerMockRecorder is the mock recorder for MockClusterHTTPScalingSetLister. +type MockClusterHTTPScalingSetListerMockRecorder struct { + mock *MockClusterHTTPScalingSetLister +} + +// NewMockClusterHTTPScalingSetLister creates a new mock instance. +func NewMockClusterHTTPScalingSetLister(ctrl *gomock.Controller) *MockClusterHTTPScalingSetLister { + mock := &MockClusterHTTPScalingSetLister{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetLister) EXPECT() *MockClusterHTTPScalingSetListerMockRecorder { + return m.recorder +} + +// ClusterHTTPScalingSets mocks base method. +func (m *MockClusterHTTPScalingSetLister) ClusterHTTPScalingSets(namespace string) v1alpha10.ClusterHTTPScalingSetNamespaceLister { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ClusterHTTPScalingSets", namespace) + ret0, _ := ret[0].(v1alpha10.ClusterHTTPScalingSetNamespaceLister) + return ret0 +} + +// ClusterHTTPScalingSets indicates an expected call of ClusterHTTPScalingSets. +func (mr *MockClusterHTTPScalingSetListerMockRecorder) ClusterHTTPScalingSets(namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterHTTPScalingSets", reflect.TypeOf((*MockClusterHTTPScalingSetLister)(nil).ClusterHTTPScalingSets), namespace) +} + +// List mocks base method. +func (m *MockClusterHTTPScalingSetLister) List(selector labels.Selector) ([]*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", selector) + ret0, _ := ret[0].([]*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockClusterHTTPScalingSetListerMockRecorder) List(selector any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClusterHTTPScalingSetLister)(nil).List), selector) +} + +// MockClusterHTTPScalingSetNamespaceLister is a mock of ClusterHTTPScalingSetNamespaceLister interface. +type MockClusterHTTPScalingSetNamespaceLister struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetNamespaceListerMockRecorder +} + +// MockClusterHTTPScalingSetNamespaceListerMockRecorder is the mock recorder for MockClusterHTTPScalingSetNamespaceLister. +type MockClusterHTTPScalingSetNamespaceListerMockRecorder struct { + mock *MockClusterHTTPScalingSetNamespaceLister +} + +// NewMockClusterHTTPScalingSetNamespaceLister creates a new mock instance. +func NewMockClusterHTTPScalingSetNamespaceLister(ctrl *gomock.Controller) *MockClusterHTTPScalingSetNamespaceLister { + mock := &MockClusterHTTPScalingSetNamespaceLister{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetNamespaceListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetNamespaceLister) EXPECT() *MockClusterHTTPScalingSetNamespaceListerMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockClusterHTTPScalingSetNamespaceLister) Get(name string) (*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", name) + ret0, _ := ret[0].(*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockClusterHTTPScalingSetNamespaceListerMockRecorder) Get(name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClusterHTTPScalingSetNamespaceLister)(nil).Get), name) +} + +// List mocks base method. +func (m *MockClusterHTTPScalingSetNamespaceLister) List(selector labels.Selector) ([]*v1alpha1.ClusterHTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", selector) + ret0, _ := ret[0].([]*v1alpha1.ClusterHTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockClusterHTTPScalingSetNamespaceListerMockRecorder) List(selector any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClusterHTTPScalingSetNamespaceLister)(nil).List), selector) +} diff --git a/operator/generated/listers/http/v1alpha1/mock/expansion_generated.go b/operator/generated/listers/http/v1alpha1/mock/expansion_generated.go index c2c41e48..460988ad 100644 --- a/operator/generated/listers/http/v1alpha1/mock/expansion_generated.go +++ b/operator/generated/listers/http/v1alpha1/mock/expansion_generated.go @@ -30,6 +30,52 @@ import ( gomock "go.uber.org/mock/gomock" ) +// MockClusterHTTPScalingSetListerExpansion is a mock of ClusterHTTPScalingSetListerExpansion interface. +type MockClusterHTTPScalingSetListerExpansion struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetListerExpansionMockRecorder +} + +// MockClusterHTTPScalingSetListerExpansionMockRecorder is the mock recorder for MockClusterHTTPScalingSetListerExpansion. +type MockClusterHTTPScalingSetListerExpansionMockRecorder struct { + mock *MockClusterHTTPScalingSetListerExpansion +} + +// NewMockClusterHTTPScalingSetListerExpansion creates a new mock instance. +func NewMockClusterHTTPScalingSetListerExpansion(ctrl *gomock.Controller) *MockClusterHTTPScalingSetListerExpansion { + mock := &MockClusterHTTPScalingSetListerExpansion{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetListerExpansionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetListerExpansion) EXPECT() *MockClusterHTTPScalingSetListerExpansionMockRecorder { + return m.recorder +} + +// MockClusterHTTPScalingSetNamespaceListerExpansion is a mock of ClusterHTTPScalingSetNamespaceListerExpansion interface. +type MockClusterHTTPScalingSetNamespaceListerExpansion struct { + ctrl *gomock.Controller + recorder *MockClusterHTTPScalingSetNamespaceListerExpansionMockRecorder +} + +// MockClusterHTTPScalingSetNamespaceListerExpansionMockRecorder is the mock recorder for MockClusterHTTPScalingSetNamespaceListerExpansion. +type MockClusterHTTPScalingSetNamespaceListerExpansionMockRecorder struct { + mock *MockClusterHTTPScalingSetNamespaceListerExpansion +} + +// NewMockClusterHTTPScalingSetNamespaceListerExpansion creates a new mock instance. +func NewMockClusterHTTPScalingSetNamespaceListerExpansion(ctrl *gomock.Controller) *MockClusterHTTPScalingSetNamespaceListerExpansion { + mock := &MockClusterHTTPScalingSetNamespaceListerExpansion{ctrl: ctrl} + mock.recorder = &MockClusterHTTPScalingSetNamespaceListerExpansionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterHTTPScalingSetNamespaceListerExpansion) EXPECT() *MockClusterHTTPScalingSetNamespaceListerExpansionMockRecorder { + return m.recorder +} + // MockHTTPScaledObjectListerExpansion is a mock of HTTPScaledObjectListerExpansion interface. type MockHTTPScaledObjectListerExpansion struct { ctrl *gomock.Controller @@ -75,3 +121,49 @@ func NewMockHTTPScaledObjectNamespaceListerExpansion(ctrl *gomock.Controller) *M func (m *MockHTTPScaledObjectNamespaceListerExpansion) EXPECT() *MockHTTPScaledObjectNamespaceListerExpansionMockRecorder { return m.recorder } + +// MockHTTPScalingSetListerExpansion is a mock of HTTPScalingSetListerExpansion interface. +type MockHTTPScalingSetListerExpansion struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetListerExpansionMockRecorder +} + +// MockHTTPScalingSetListerExpansionMockRecorder is the mock recorder for MockHTTPScalingSetListerExpansion. +type MockHTTPScalingSetListerExpansionMockRecorder struct { + mock *MockHTTPScalingSetListerExpansion +} + +// NewMockHTTPScalingSetListerExpansion creates a new mock instance. +func NewMockHTTPScalingSetListerExpansion(ctrl *gomock.Controller) *MockHTTPScalingSetListerExpansion { + mock := &MockHTTPScalingSetListerExpansion{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetListerExpansionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetListerExpansion) EXPECT() *MockHTTPScalingSetListerExpansionMockRecorder { + return m.recorder +} + +// MockHTTPScalingSetNamespaceListerExpansion is a mock of HTTPScalingSetNamespaceListerExpansion interface. +type MockHTTPScalingSetNamespaceListerExpansion struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetNamespaceListerExpansionMockRecorder +} + +// MockHTTPScalingSetNamespaceListerExpansionMockRecorder is the mock recorder for MockHTTPScalingSetNamespaceListerExpansion. +type MockHTTPScalingSetNamespaceListerExpansionMockRecorder struct { + mock *MockHTTPScalingSetNamespaceListerExpansion +} + +// NewMockHTTPScalingSetNamespaceListerExpansion creates a new mock instance. +func NewMockHTTPScalingSetNamespaceListerExpansion(ctrl *gomock.Controller) *MockHTTPScalingSetNamespaceListerExpansion { + mock := &MockHTTPScalingSetNamespaceListerExpansion{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetNamespaceListerExpansionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetNamespaceListerExpansion) EXPECT() *MockHTTPScalingSetNamespaceListerExpansionMockRecorder { + return m.recorder +} diff --git a/operator/generated/listers/http/v1alpha1/mock/httpscalingset.go b/operator/generated/listers/http/v1alpha1/mock/httpscalingset.go new file mode 100644 index 00000000..7ac0f184 --- /dev/null +++ b/operator/generated/listers/http/v1alpha1/mock/httpscalingset.go @@ -0,0 +1,141 @@ +// /* +// Copyright 2023 The KEDA Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// */ +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: operator/generated/listers/http/v1alpha1/httpscalingset.go +// +// Generated by this command: +// +// mockgen -copyright_file=hack/boilerplate.go.txt -destination=operator/generated/listers/http/v1alpha1/mock/httpscalingset.go -package=mock -source=operator/generated/listers/http/v1alpha1/httpscalingset.go +// + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" + v1alpha10 "github.com/kedacore/http-add-on/operator/generated/listers/http/v1alpha1" + gomock "go.uber.org/mock/gomock" + labels "k8s.io/apimachinery/pkg/labels" +) + +// MockHTTPScalingSetLister is a mock of HTTPScalingSetLister interface. +type MockHTTPScalingSetLister struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetListerMockRecorder +} + +// MockHTTPScalingSetListerMockRecorder is the mock recorder for MockHTTPScalingSetLister. +type MockHTTPScalingSetListerMockRecorder struct { + mock *MockHTTPScalingSetLister +} + +// NewMockHTTPScalingSetLister creates a new mock instance. +func NewMockHTTPScalingSetLister(ctrl *gomock.Controller) *MockHTTPScalingSetLister { + mock := &MockHTTPScalingSetLister{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetLister) EXPECT() *MockHTTPScalingSetListerMockRecorder { + return m.recorder +} + +// HTTPScalingSets mocks base method. +func (m *MockHTTPScalingSetLister) HTTPScalingSets(namespace string) v1alpha10.HTTPScalingSetNamespaceLister { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HTTPScalingSets", namespace) + ret0, _ := ret[0].(v1alpha10.HTTPScalingSetNamespaceLister) + return ret0 +} + +// HTTPScalingSets indicates an expected call of HTTPScalingSets. +func (mr *MockHTTPScalingSetListerMockRecorder) HTTPScalingSets(namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPScalingSets", reflect.TypeOf((*MockHTTPScalingSetLister)(nil).HTTPScalingSets), namespace) +} + +// List mocks base method. +func (m *MockHTTPScalingSetLister) List(selector labels.Selector) ([]*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", selector) + ret0, _ := ret[0].([]*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockHTTPScalingSetListerMockRecorder) List(selector any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockHTTPScalingSetLister)(nil).List), selector) +} + +// MockHTTPScalingSetNamespaceLister is a mock of HTTPScalingSetNamespaceLister interface. +type MockHTTPScalingSetNamespaceLister struct { + ctrl *gomock.Controller + recorder *MockHTTPScalingSetNamespaceListerMockRecorder +} + +// MockHTTPScalingSetNamespaceListerMockRecorder is the mock recorder for MockHTTPScalingSetNamespaceLister. +type MockHTTPScalingSetNamespaceListerMockRecorder struct { + mock *MockHTTPScalingSetNamespaceLister +} + +// NewMockHTTPScalingSetNamespaceLister creates a new mock instance. +func NewMockHTTPScalingSetNamespaceLister(ctrl *gomock.Controller) *MockHTTPScalingSetNamespaceLister { + mock := &MockHTTPScalingSetNamespaceLister{ctrl: ctrl} + mock.recorder = &MockHTTPScalingSetNamespaceListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHTTPScalingSetNamespaceLister) EXPECT() *MockHTTPScalingSetNamespaceListerMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockHTTPScalingSetNamespaceLister) Get(name string) (*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", name) + ret0, _ := ret[0].(*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockHTTPScalingSetNamespaceListerMockRecorder) Get(name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockHTTPScalingSetNamespaceLister)(nil).Get), name) +} + +// List mocks base method. +func (m *MockHTTPScalingSetNamespaceLister) List(selector labels.Selector) ([]*v1alpha1.HTTPScalingSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", selector) + ret0, _ := ret[0].([]*v1alpha1.HTTPScalingSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockHTTPScalingSetNamespaceListerMockRecorder) List(selector any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockHTTPScalingSetNamespaceLister)(nil).List), selector) +} diff --git a/operator/main.go b/operator/main.go index 3c298127..bebfc401 100644 --- a/operator/main.go +++ b/operator/main.go @@ -119,6 +119,24 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "HTTPScaledObject") os.Exit(1) } + + if err = (&httpcontrollers.HTTPScalingSetReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "HTTPScalingSet") + os.Exit(1) + } + + if err = (&httpcontrollers.ClusterHTTPScalingSetReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + KEDANamespace: baseConfig.CurrentNamespace, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "HTTPScalingSet") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/pkg/k8s/deployment.go b/pkg/k8s/deployment.go new file mode 100644 index 00000000..7853700f --- /dev/null +++ b/pkg/k8s/deployment.go @@ -0,0 +1,50 @@ +package k8s + +import ( + "golang.org/x/exp/maps" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func NewDeployment(name, namespace, serviceAccount, image string, ports []corev1.ContainerPort, envs []corev1.EnvVar, replicas *int32, selector, labels, anntotaions map[string]string, resources corev1.ResourceRequirements) *appsv1.Deployment { + if labels == nil { + labels = map[string]string{} + } + if anntotaions == nil { + anntotaions = map[string]string{} + } + maps.Copy(labels, selector) + return &appsv1.Deployment{ + ObjectMeta: v1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: labels, + Annotations: anntotaions, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: replicas, + Selector: &v1.LabelSelector{ + MatchLabels: selector, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Labels: labels, + Annotations: anntotaions, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: serviceAccount, + Containers: []corev1.Container{ + { + Name: name, + Image: image, + Ports: ports, + Resources: resources, + Env: envs, + }, + }, + }, + }, + }, + } +} diff --git a/pkg/k8s/service.go b/pkg/k8s/service.go new file mode 100644 index 00000000..c92afc8d --- /dev/null +++ b/pkg/k8s/service.go @@ -0,0 +1,31 @@ +package k8s + +import ( + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func NewService(name, namespace, portName string, port int32, selector map[string]string) *corev1.Service { + return &corev1.Service{ + ObjectMeta: v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: portName, + Port: port, + TargetPort: intstr.IntOrString{ + StrVal: portName, + Type: intstr.String, + }, + Protocol: "TCP", + }, + }, + Type: corev1.ServiceTypeClusterIP, + Selector: selector, + }, + } +} diff --git a/pkg/routing/table.go b/pkg/routing/table.go index 4cf8a530..65189206 100644 --- a/pkg/routing/table.go +++ b/pkg/routing/table.go @@ -17,6 +17,7 @@ import ( "github.com/kedacore/http-add-on/pkg/k8s" "github.com/kedacore/http-add-on/pkg/queue" "github.com/kedacore/http-add-on/pkg/util" + "github.com/kedacore/http-add-on/pkg/validator" ) var ( @@ -46,7 +47,6 @@ type table struct { func NewTable(sharedInformerFactory externalversions.SharedInformerFactory, namespace string, counter queue.Counter) (Table, error) { httpScaledObjects := informershttpv1alpha1.New(sharedInformerFactory, namespace, nil).HTTPScaledObjects() - t := table{ httpScaledObjects: make(map[types.NamespacedName]*httpv1alpha1.HTTPScaledObject), memorySignaler: util.NewSignaler(), @@ -150,6 +150,9 @@ func (t *table) OnAdd(obj interface{}, _ bool) { if !ok { return } + if !validator.IsManagedByThisScalingSet(httpScaledObject) { + return + } key := *k8s.NamespacedNameFromObject(httpScaledObject) window := time.Minute @@ -175,11 +178,25 @@ func (t *table) OnUpdate(oldObj interface{}, newObj interface{}) { return } oldKey := *k8s.NamespacedNameFromObject(oldHTTPSO) - newHTTPSO, ok := newObj.(*httpv1alpha1.HTTPScaledObject) if !ok { return } + defer t.memorySignaler.Signal() + + if !validator.IsManagedByThisScalingSet(newHTTPSO) { + // If the old HTTPSO was managed by this + // instance, we have to delete it + if validator.IsManagedByThisScalingSet(oldHTTPSO) { + t.httpScaledObjectsMutex.Lock() + defer t.httpScaledObjectsMutex.Unlock() + + delete(t.httpScaledObjects, oldKey) + t.queueCounter.RemoveKey(oldKey.String()) + } + return + } + newKey := *k8s.NamespacedNameFromObject(newHTTPSO) window := time.Minute @@ -191,15 +208,12 @@ func (t *table) OnUpdate(oldObj interface{}, newObj interface{}) { } t.queueCounter.UpdateBuckets(newKey.String(), window, granualrity) - mustDelete := oldKey != newKey - defer t.memorySignaler.Signal() - t.httpScaledObjectsMutex.Lock() defer t.httpScaledObjectsMutex.Unlock() t.httpScaledObjects[newKey] = newHTTPSO - if mustDelete { + if oldKey != newKey { delete(t.httpScaledObjects, oldKey) t.queueCounter.RemoveKey(oldKey.String()) } @@ -210,6 +224,9 @@ func (t *table) OnDelete(obj interface{}) { if !ok { return } + if !validator.IsManagedByThisScalingSet(httpScaledObject) { + return + } key := *k8s.NamespacedNameFromObject(httpScaledObject) defer t.memorySignaler.Signal() diff --git a/pkg/util/conversions.go b/pkg/util/conversions.go new file mode 100644 index 00000000..e2ff6812 --- /dev/null +++ b/pkg/util/conversions.go @@ -0,0 +1,29 @@ +package util + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" +) + +func GetHTTPScalingSetSpecFromObject(httpss metav1.Object) *v1alpha1.HTTPScalingSetSpec { + var httpSpec *v1alpha1.HTTPScalingSetSpec + switch obj := httpss.(type) { + case *v1alpha1.ClusterHTTPScalingSet: + httpSpec = &obj.Spec + case *v1alpha1.HTTPScalingSet: + httpSpec = &obj.Spec + } + return httpSpec +} + +func GetHTTPScalingSetKindFromObject(httpss metav1.Object) v1alpha1.ScalingSetKind { + var kind v1alpha1.ScalingSetKind + switch httpss.(type) { + case *v1alpha1.ClusterHTTPScalingSet: + kind = v1alpha1.ClusterHTTPScalingSetKind + case *v1alpha1.HTTPScalingSet: + kind = v1alpha1.HTTPScalingSetKind + } + return kind +} diff --git a/pkg/validator/scalingset_validatior.go b/pkg/validator/scalingset_validatior.go new file mode 100644 index 00000000..f0723a8d --- /dev/null +++ b/pkg/validator/scalingset_validatior.go @@ -0,0 +1,33 @@ +package validator + +import ( + "os" + + httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" +) + +const ScalingSetNameEnv = "KEDA_HTTP_SCALING_SET_NAME" +const ScalingSetKindEnv = "KEDA_HTTP_SCALING_SET_KIND" + +var ( + selfHTTPScalingSetName = "" + selfHTTPScalingSetKind = "" +) + +func init() { + val, exist := os.LookupEnv(ScalingSetNameEnv) + if exist { + selfHTTPScalingSetName = val + } + val, exist = os.LookupEnv(ScalingSetKindEnv) + if exist { + selfHTTPScalingSetKind = val + } +} + +func IsManagedByThisScalingSet(httpScaledObject *httpv1alpha1.HTTPScaledObject) bool { + scalingSet := httpScaledObject.Spec.GetHTTPSalingSetTargetRef() + + return scalingSet.Name == selfHTTPScalingSetName && + string(scalingSet.Kind) == selfHTTPScalingSetKind +} diff --git a/pkg/validator/scalingset_validatior_test.go b/pkg/validator/scalingset_validatior_test.go new file mode 100644 index 00000000..36caa09f --- /dev/null +++ b/pkg/validator/scalingset_validatior_test.go @@ -0,0 +1,90 @@ +package validator + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1" +) + +func TestIsManagedByThisScalingSet(t *testing.T) { + var testCases = []struct { + name string + localScalingSetName string + localScalingSetKind string + httpScaledObjectScalingSet *v1alpha1.HTTPSalingSetTargetRef + result bool + }{ + { + name: "not defined in component and not set in HTTPScaledObject", + localScalingSetName: "", + localScalingSetKind: "", + httpScaledObjectScalingSet: nil, + result: true, + }, + { + name: "not defined in component and set in HTTPScaledObject", + localScalingSetName: "", + localScalingSetKind: "", + httpScaledObjectScalingSet: &v1alpha1.HTTPSalingSetTargetRef{Name: "ss-1"}, + result: false, + }, + { + name: "defined in component and not set in HTTPScaledObject", + localScalingSetName: "ss-0", + localScalingSetKind: "", + httpScaledObjectScalingSet: nil, + result: false, + }, + { + name: "defined in component and set in HTTPScaledObject (different values)", + localScalingSetName: "ss-0", + localScalingSetKind: "", + httpScaledObjectScalingSet: &v1alpha1.HTTPSalingSetTargetRef{Name: "ss-1"}, + result: false, + }, + { + name: "defined in component, set in HTTPScaledObject, and kind matches (unset)", + localScalingSetName: "ss-0", + localScalingSetKind: string(v1alpha1.ClusterHTTPScalingSetKind), + httpScaledObjectScalingSet: &v1alpha1.HTTPSalingSetTargetRef{Name: "ss-0"}, + result: true, + }, + { + name: "defined in component, set in HTTPScaledObject, and kind matches (set)", + localScalingSetName: "ss-0", + localScalingSetKind: string(v1alpha1.HTTPScalingSetKind), + httpScaledObjectScalingSet: &v1alpha1.HTTPSalingSetTargetRef{Name: "ss-0", Kind: v1alpha1.HTTPScalingSetKind}, + result: true, + }, + { + name: "defined in component, set in HTTPScaledObject, and kind doesn't match (set)", + localScalingSetName: "ss-0", + localScalingSetKind: string(v1alpha1.ClusterHTTPScalingSetKind), + httpScaledObjectScalingSet: &v1alpha1.HTTPSalingSetTargetRef{Name: "ss-0", Kind: v1alpha1.HTTPScalingSetKind}, + result: false, + }, + { + name: "defined in component, set in HTTPScaledObject, and kind doesn't match (unset)", + localScalingSetName: "ss-0", + localScalingSetKind: string(v1alpha1.HTTPScalingSetKind), + httpScaledObjectScalingSet: &v1alpha1.HTTPSalingSetTargetRef{Name: "ss-0"}, + result: false, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + httpso := &v1alpha1.HTTPScaledObject{ + Spec: v1alpha1.HTTPScaledObjectSpec{ + ScalingSet: tt.httpScaledObjectScalingSet, + }, + } + selfHTTPScalingSetName = tt.localScalingSetName + selfHTTPScalingSetKind = tt.localScalingSetKind + result := IsManagedByThisScalingSet(httpso) + assert.Equal(t, tt.result, result) + }) + } +} diff --git a/scaler/config.go b/scaler/config.go index cce3f2e1..67cfad09 100644 --- a/scaler/config.go +++ b/scaler/config.go @@ -16,8 +16,6 @@ type config struct { TargetNamespace string `envconfig:"KEDA_HTTP_SCALER_TARGET_ADMIN_NAMESPACE" required:"true"` // TargetService is the name of the service to issue metrics RPC requests to interceptors TargetService string `envconfig:"KEDA_HTTP_SCALER_TARGET_ADMIN_SERVICE" required:"true"` - // TargetDeployment is the name of the deployment to issue metrics RPC requests to interceptors - TargetDeployment string `envconfig:"KEDA_HTTP_SCALER_TARGET_ADMIN_DEPLOYMENT" required:"true"` // TargetPort is the port on TargetService to which to issue metrics RPC requests to // interceptors TargetPort int `envconfig:"KEDA_HTTP_SCALER_TARGET_ADMIN_PORT" required:"true"` diff --git a/scaler/handlers.go b/scaler/handlers.go index 0f7af970..a385b894 100644 --- a/scaler/handlers.go +++ b/scaler/handlers.go @@ -20,6 +20,7 @@ import ( informershttpv1alpha1 "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/http/v1alpha1" "github.com/kedacore/http-add-on/pkg/k8s" "github.com/kedacore/http-add-on/pkg/util" + "github.com/kedacore/http-add-on/pkg/validator" ) const ( @@ -147,6 +148,13 @@ func (e *impl) GetMetricSpec( lggr.Error(err, "unable to get HTTPScaledObject", "name", sor.Name, "namespace", sor.Namespace) return nil, err } + + if !validator.IsManagedByThisScalingSet(httpso) { + err := fmt.Errorf("the HTTPScaledObject %s is not for this ScalingSet", httpso.Name) + lggr.Error(err, "invalid configuration") + return nil, err + } + targetValue := int64(ptr.Deref(httpso.Spec.TargetPendingRequests, 100)) if httpso.Spec.ScalingMetric != nil { @@ -218,6 +226,12 @@ func (e *impl) GetMetrics( return nil, err } + if !validator.IsManagedByThisScalingSet(httpso) { + err := fmt.Errorf("the HTTPScaledObject %s is not for this ScalingSet", httpso.Name) + lggr.Error(err, "invalid configuration") + return nil, err + } + key := namespacedName.String() count := e.pinger.counts()[key] diff --git a/scaler/main.go b/scaler/main.go index 2a4d55b6..253ffc39 100644 --- a/scaler/main.go +++ b/scaler/main.go @@ -44,7 +44,6 @@ func main() { grpcPort := cfg.GRPCPort namespace := cfg.TargetNamespace svcName := cfg.TargetService - deplName := cfg.TargetDeployment targetPortStr := fmt.Sprintf("%d", cfg.TargetPort) targetPendingRequests := cfg.TargetPendingRequests @@ -69,7 +68,6 @@ func main() { k8s.EndpointsFuncForK8sClientset(k8sCl), namespace, svcName, - deplName, targetPortStr, ) diff --git a/scaler/queue_pinger.go b/scaler/queue_pinger.go index 9320bdca..23563ecc 100644 --- a/scaler/queue_pinger.go +++ b/scaler/queue_pinger.go @@ -42,16 +42,15 @@ const ( // // context // go pinger.start(ctx, ticker) type queuePinger struct { - getEndpointsFn k8s.GetEndpointsFunc - interceptorNS string - interceptorSvcName string - interceptorServiceName string - adminPort string - pingMut *sync.RWMutex - lastPingTime time.Time - allCounts map[string]queue.Count - lggr logr.Logger - status PingerStatus + getEndpointsFn k8s.GetEndpointsFunc + interceptorNS string + interceptorSvcName string + adminPort string + pingMut *sync.RWMutex + lastPingTime time.Time + allCounts map[string]queue.Count + lggr logr.Logger + status PingerStatus } func newQueuePinger( @@ -59,19 +58,17 @@ func newQueuePinger( getEndpointsFn k8s.GetEndpointsFunc, ns, svcName, - deplName, adminPort string, ) *queuePinger { pingMut := new(sync.RWMutex) pinger := &queuePinger{ - getEndpointsFn: getEndpointsFn, - interceptorNS: ns, - interceptorSvcName: svcName, - interceptorServiceName: deplName, - adminPort: adminPort, - pingMut: pingMut, - lggr: lggr, - allCounts: map[string]queue.Count{}, + getEndpointsFn: getEndpointsFn, + interceptorNS: ns, + interceptorSvcName: svcName, + adminPort: adminPort, + pingMut: pingMut, + lggr: lggr, + allCounts: map[string]queue.Count{}, } return pinger } @@ -82,7 +79,7 @@ func (q *queuePinger) start( ticker *time.Ticker, endpCache k8s.EndpointsCache, ) error { - endpoWatchIface, err := endpCache.Watch(q.interceptorNS, q.interceptorServiceName) + endpoWatchIface, err := endpCache.Watch(q.interceptorNS, q.interceptorSvcName) if err != nil { return err } diff --git a/scaler/queue_pinger_test.go b/scaler/queue_pinger_test.go index 202bd9cb..6a373fb3 100644 --- a/scaler/queue_pinger_test.go +++ b/scaler/queue_pinger_test.go @@ -23,7 +23,6 @@ func TestCounts(t *testing.T) { const ( ns = "testns" svcName = "testsvc" - deplName = "testdepl" tickDur = 10 * time.Millisecond numEndpoints = 3 ) @@ -69,7 +68,6 @@ func TestCounts(t *testing.T) { }, ns, svcName, - deplName, srvURL.Port(), ) @@ -156,7 +154,6 @@ func TestFetchAndSaveCounts(t *testing.T) { endpointsFn, ns, svcName, - deplName, srvURL.Port(), ) @@ -304,7 +301,6 @@ func newFakeQueuePinger( }, "testns", "testsvc", - "testdepl", opts.port, ) return ticker, pinger, nil diff --git a/tests/checks/scaling_set/clustered/cluster_scaling_set_test.go b/tests/checks/scaling_set/clustered/cluster_scaling_set_test.go new file mode 100644 index 00000000..eadfcd04 --- /dev/null +++ b/tests/checks/scaling_set/clustered/cluster_scaling_set_test.go @@ -0,0 +1,57 @@ +//go:build e2e +// +build e2e + +package scaling_set_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/client-go/kubernetes" + + scaling_set_helper "github.com/kedacore/http-add-on/tests/checks/scaling_set/helper" + . "github.com/kedacore/http-add-on/tests/helper" +) + +const ( + testName = "cluster-scaling-set-test" + clusterScope = true +) + +func TestCheck(t *testing.T) { + // setup + t.Log("--- setting up ---") + // Create kubernetes resources + kc := GetKubernetesClient(t) + data, templates := scaling_set_helper.GetTemplateData(testName, clusterScope) + CreateKubernetesResources(t, kc, data.TestNamespace, data, templates) + + scaling_set_helper.WaitForScalingSetComponents(t, kc, data) + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, data.DeploymentName, data.TestNamespace, data.MinReplicas, 6, 10), + "replica count should be %d after 1 minutes", data.MinReplicas) + + testScaleOut(t, kc, data) + testScaleIn(t, kc, data) + + // cleanup + DeleteKubernetesResources(t, data.TestNamespace, data, templates) +} + +func testScaleOut(t *testing.T, kc *kubernetes.Clientset, data scaling_set_helper.TemplateData) { + t.Log("--- testing scale out ---") + + loadTemplate := scaling_set_helper.GetLoadJobTemplate() + KubectlApplyWithTemplate(t, data, loadTemplate.Name, loadTemplate.Config) + + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, data.DeploymentName, data.TestNamespace, data.MaxReplicas, 6, 20), + "replica count should be %d after 2 minutes", data.MaxReplicas) +} + +func testScaleIn(t *testing.T, kc *kubernetes.Clientset, data scaling_set_helper.TemplateData) { + t.Log("--- testing scale out ---") + + loadTemplate := scaling_set_helper.GetLoadJobTemplate() + KubectlDeleteWithTemplate(t, data, loadTemplate.Name, loadTemplate.Config) + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, data.DeploymentName, data.TestNamespace, data.MinReplicas, 18, 10), + "replica count should be %d after 3 minutes", data.MinReplicas) +} diff --git a/tests/checks/scaling_set/helper/scaling_set_helper.go b/tests/checks/scaling_set/helper/scaling_set_helper.go new file mode 100644 index 00000000..079d826d --- /dev/null +++ b/tests/checks/scaling_set/helper/scaling_set_helper.go @@ -0,0 +1,280 @@ +//go:build e2e +// +build e2e + +package scaling_set_helper + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/client-go/kubernetes" + + . "github.com/kedacore/http-add-on/tests/helper" +) + +var ( + minReplicaCount = 0 + maxReplicaCount = 2 +) + +type TemplateData struct { + TestNamespace string + DeploymentName string + ServiceName string + HTTPScaledObjectName string + HTTPScalingSetName string + HTTPScalingSetKind string + HTTPInterceptorService string + HTTPInterceptorNamespace string + ClusterScoped bool + Host string + MinReplicas int + MaxReplicas int +} + +const ( + serviceTemplate = ` +apiVersion: v1 +kind: Service +metadata: + name: {{.ServiceName}} + namespace: {{.TestNamespace}} + labels: + app: {{.DeploymentName}} +spec: + ports: + - port: 8080 + targetPort: http + protocol: TCP + name: http + selector: + app: {{.DeploymentName}} +` + + deploymentTemplate = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{.DeploymentName}} + namespace: {{.TestNamespace}} + labels: + app: {{.DeploymentName}} +spec: + replicas: 0 + selector: + matchLabels: + app: {{.DeploymentName}} + template: + metadata: + labels: + app: {{.DeploymentName}} + spec: + containers: + - name: {{.DeploymentName}} + image: registry.k8s.io/e2e-test-images/agnhost:2.45 + args: + - netexec + ports: + - name: http + containerPort: 8080 + protocol: TCP + readinessProbe: + httpGet: + path: / + port: http +` + + loadJobTemplate = ` +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-request + namespace: {{.TestNamespace}} +spec: + template: + spec: + terminationGracePeriodSeconds: 0 + containers: + - name: apache-ab + image: ghcr.io/kedacore/tests-apache-ab + imagePullPolicy: Always + args: + - "-n" + - "20000" + - "-c" + - "1" + - "-H" + - "Host: {{.Host}}" + - "http://{{.HTTPInterceptorService}}.{{.HTTPInterceptorNamespace}}:8080/" + restartPolicy: Never + activeDeadlineSeconds: 600 + backoffLimit: 5 +` + + httpScaledObjectTemplate = ` +kind: HTTPScaledObject +apiVersion: http.keda.sh/v1alpha1 +metadata: + name: {{.HTTPScaledObjectName}} + namespace: {{.TestNamespace}} +spec: + scalingSet: + name: {{.HTTPScalingSetName}} + kind: {{.HTTPScalingSetKind}} + hosts: + - {{.Host}} + scalingMetric: + requestRate: + granularity: 1s + targetValue: 2 + window: 30s + scaledownPeriod: 0 + scaleTargetRef: + name: {{.DeploymentName}} + service: {{.ServiceName}} + port: 8080 + replicas: + min: {{ .MinReplicas }} + max: {{ .MaxReplicas }} +` + + httpClusterScalingSetTemplate = ` +kind: ClusterHTTPScalingSet +apiVersion: http.keda.sh/v1alpha1 +metadata: + name: {{.HTTPScalingSetName}} +spec: + interceptor: + replicas: 1 + serviceAccountName: keda-http-add-on-interceptor + scaler: + serviceAccountName: keda-http-add-on-scaler +` + + httpScalingSetServiceAccount = ` +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{.HTTPScalingSetName}} + namespace: {{.TestNamespace}} +` + httpScalingSetClusterRole = ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{.HTTPScalingSetName}} +rules: +- apiGroups: + - "" + resources: + - endpoints + verbs: + - get + - list + - watch +- apiGroups: + - http.keda.sh + resources: + - httpscaledobjects + verbs: + - get + - list + - watch +` + + httpScalingSetClusterRoleBinding = ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{.HTTPScalingSetName}} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{.HTTPScalingSetName}} +subjects: +- kind: ServiceAccount + name: {{.HTTPScalingSetName}} + namespace: {{.TestNamespace}} + ` + + httpScalingSetTemplate = ` +kind: HTTPScalingSet +apiVersion: http.keda.sh/v1alpha1 +metadata: + name: {{.HTTPScalingSetName}} + namespace: {{.TestNamespace}} +spec: + interceptor: + replicas: 1 + serviceAccountName: {{.HTTPScalingSetName}} + image: docker.io/jorturfer/http-add-on-interceptor:isolate + scaler: + serviceAccountName: {{.HTTPScalingSetName}} + image: docker.io/jorturfer/http-add-on-scaler:isolate +` +) + +func GetTemplateData(testName string, clusterScoped bool) (TemplateData, []Template) { + namespace := fmt.Sprintf("%s-ns", testName) + deploymentName := fmt.Sprintf("%s-deployment", testName) + serviceName := fmt.Sprintf("%s-service", testName) + httpScaledObjectName := fmt.Sprintf("%s-http-so", testName) + scalingSetName := fmt.Sprintf("%s-ss", testName) + interceptorServiceName := fmt.Sprintf("%s-interceptor-proxy", scalingSetName) + + templateData := TemplateData{ + TestNamespace: namespace, + DeploymentName: deploymentName, + ServiceName: serviceName, + HTTPScaledObjectName: httpScaledObjectName, + HTTPScalingSetName: scalingSetName, + HTTPScalingSetKind: "HTTPScalingSet", + Host: testName, + MinReplicas: minReplicaCount, + MaxReplicas: maxReplicaCount, + ClusterScoped: clusterScoped, + HTTPInterceptorService: interceptorServiceName, + HTTPInterceptorNamespace: namespace, + } + + templates := []Template{ + {Name: "deploymentTemplate", Config: deploymentTemplate}, + {Name: "serviceNameTemplate", Config: serviceTemplate}, + {Name: "httpScaledObjectTemplate", Config: httpScaledObjectTemplate}, + } + + if clusterScoped { + templateData.HTTPInterceptorNamespace = KEDANamespace + templateData.HTTPScalingSetKind = "ClusterHTTPScalingSet" + templates = append(templates, Template{Name: "httpClusterScalingSetTemplate", Config: httpClusterScalingSetTemplate}) + } else { + templates = append(templates, Template{Name: "httpScalingSetTemplate", Config: httpScalingSetTemplate}) + templates = append(templates, Template{Name: "httpScalingSetServiceAccount", Config: httpScalingSetServiceAccount}) + templates = append(templates, Template{Name: "httpScalingSetClusterRole", Config: httpScalingSetClusterRole}) + templates = append(templates, Template{Name: "httpScalingSetClusterRoleBinding", Config: httpScalingSetClusterRoleBinding}) + } + + return templateData, templates +} + +func GetLoadJobTemplate() Template { + return Template{Name: "loadJobTemplate", Config: loadJobTemplate} +} + +func WaitForScalingSetComponents(t *testing.T, kc *kubernetes.Clientset, data TemplateData) { + interceptorName := fmt.Sprintf("%s-interceptor", data.HTTPScalingSetName) + scalerName := fmt.Sprintf("%s-external-scaler", data.HTTPScalingSetName) + namespace := data.TestNamespace + if data.ClusterScoped { + namespace = KEDANamespace + } + + // Wait for interceptor + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, interceptorName, namespace, 1, 6, 10), + "replica count should be %d after 1 minutes", 1) + + // Wait for scaler + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, scalerName, namespace, 1, 6, 10), + "replica count should be %d after 1 minutes", 1) +} diff --git a/tests/checks/scaling_set/namespaced/namespace_scaling_set_test.go b/tests/checks/scaling_set/namespaced/namespace_scaling_set_test.go new file mode 100644 index 00000000..16c9bf13 --- /dev/null +++ b/tests/checks/scaling_set/namespaced/namespace_scaling_set_test.go @@ -0,0 +1,57 @@ +//go:build e2e +// +build e2e + +package scaling_set_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/client-go/kubernetes" + + scaling_set_helper "github.com/kedacore/http-add-on/tests/checks/scaling_set/helper" + . "github.com/kedacore/http-add-on/tests/helper" +) + +const ( + testName = "namespaced-scaling-set-test" + clusterScope = false +) + +func TestCheck(t *testing.T) { + // setup + t.Log("--- setting up ---") + // Create kubernetes resources + kc := GetKubernetesClient(t) + data, templates := scaling_set_helper.GetTemplateData(testName, clusterScope) + CreateKubernetesResources(t, kc, data.TestNamespace, data, templates) + + scaling_set_helper.WaitForScalingSetComponents(t, kc, data) + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, data.DeploymentName, data.TestNamespace, data.MinReplicas, 6, 10), + "replica count should be %d after 1 minutes", data.MinReplicas) + + testScaleOut(t, kc, data) + testScaleIn(t, kc, data) + + // cleanup + DeleteKubernetesResources(t, data.TestNamespace, data, templates) +} + +func testScaleOut(t *testing.T, kc *kubernetes.Clientset, data scaling_set_helper.TemplateData) { + t.Log("--- testing scale out ---") + + loadTemplate := scaling_set_helper.GetLoadJobTemplate() + KubectlApplyWithTemplate(t, data, loadTemplate.Name, loadTemplate.Config) + + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, data.DeploymentName, data.TestNamespace, data.MaxReplicas, 6, 20), + "replica count should be %d after 2 minutes", data.MaxReplicas) +} + +func testScaleIn(t *testing.T, kc *kubernetes.Clientset, data scaling_set_helper.TemplateData) { + t.Log("--- testing scale out ---") + + loadTemplate := scaling_set_helper.GetLoadJobTemplate() + KubectlDeleteWithTemplate(t, data, loadTemplate.Name, loadTemplate.Config) + assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, data.DeploymentName, data.TestNamespace, data.MinReplicas, 18, 10), + "replica count should be %d after 3 minutes", data.MinReplicas) +}