Repository: kedacore/http-add-on
Pull request #627 (2023-03-08 17:29:55)

chore: use GH_AUTOMATION_PAT instead of GH_PROJECT_AUTOMATION_AUTH (#627)Co-authored-by: Tom Kerkhove <kerkhove.tom@gmail.com>
### Checklist

- [x] Commits are signed with Developer Certificate of Origin (DCO)






Patch:
diff --git a/.github/workflows/auto-add-issues-to-project.yml b/.github/workflows/auto-add-issues-to-project.yml
index ee78a05..3ca1522 100644
--- a/.github/workflows/auto-add-issues-to-project.yml
+++ b/.github/workflows/auto-add-issues-to-project.yml
@@ -9,7 +9,7 @@ jobs:
     steps:
       - name: Get project data
         env:
-          GITHUB_TOKEN: ${{ secrets.GH_PROJECT_AUTOMATION_AUTH }}
+          GITHUB_TOKEN: ${{ secrets.GH_AUTOMATION_PAT }}
           ORGANIZATION: kedacore
           # This refers to our backlog project: https://github.com/orgs/kedacore/projects/6/views/1
           PROJECT_NUMBER: 6
@@ -34,7 +34,7 @@ jobs:
           echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
       - name: Add issue to project
         env:
-          GITHUB_TOKEN: ${{ secrets.GH_PROJECT_AUTOMATION_AUTH }}
+          GITHUB_TOKEN: ${{ secrets.GH_AUTOMATION_PAT }}
           ISSUE_ID: ${{ github.event.issue.node_id }}
         run: |
           item_id="$( gh api graphql -f query='
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a232b20..cf1b4d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@ This changelog keeps track of work items that have been completed and are ready
 
 - **General**: Automatically tag Docker image with commit SHA ([#567](https://github.com/kedacore/http-add-on/issues/567))
 - **RBAC**: Introduce fine-grained permissions per component and reduce required permissions ([#612](https://github.com/kedacore/http-add-on/issues/612))
+- **Operator**: Migrate project to Kubebuilder v3 ([#625](https://github.com/kedacore/http-add-on/issues/625))
 
 ### Fixes
 
diff --git a/Makefile b/Makefile
index c09f056..d0334cd 100644
--- a/Makefile
+++ b/Makefile
@@ -85,13 +85,24 @@ publish-multiarch: publish-operator-multiarch publish-interceptor-multiarch publ
 
 # Development
 
-manifests: controller-gen ## Generate ClusterRole and CustomResourceDefinition objects for core componenets.
-	$(CONTROLLER_GEN) crd:crdVersions=v1 rbac:roleName=keda-http-add-on paths="./operator/..." output:crd:artifacts:config=config/crd/bases
-	$(CONTROLLER_GEN) crd:crdVersions=v1 rbac:roleName=keda-http-add-on-scaler paths="./scaler/..." output:rbac:artifacts:config=config/scaler
-	$(CONTROLLER_GEN) crd:crdVersions=v1 rbac:roleName=keda-http-add-on-interceptor paths="./interceptor/..." output:rbac:artifacts:config=config/interceptor
+generate: codegen manifests ## Generate code and manifests.
 
-verify-manifests:
-	./hack/verify-manifests.sh
+verify: verify-codegen verify-manifests ## Verify code and manifests.
+
+codegen: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
+	$(CONTROLLER_GEN) object:headerFile='hack/boilerplate.go.txt' paths='./...'
+	./hack/update-codegen.sh
+
+verify-codegen: ## Verify code is up to date.
+	./hack/verify-codegen.sh
+
+manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
+	$(CONTROLLER_GEN) crd rbac:roleName='operator' webhook paths='./operator/...' output:crd:artifacts:config='config/crd/bases' output:rbac:artifacts:config='config/operator'
+	$(CONTROLLER_GEN) crd rbac:roleName='scaler' webhook paths='./scaler/...' output:rbac:artifacts:config='config/scaler'
+	$(CONTROLLER_GEN) crd rbac:roleName='interceptor' webhook paths='./interceptor/...' output:rbac:artifacts:config='config/interceptor'
+
+verify-manifests: ## Verify manifests are up to date.
+	./hack/verify-codegen.sh
 
 fmt: ## Run go fmt against code.
 	go fmt ./...
@@ -127,8 +138,7 @@ deploy: manifests kustomize ## Deploy to the K8s cluster specified in ~/.kube/co
 	cd config/operator && \
 	$(KUSTOMIZE) edit set image ghcr.io/kedacore/http-add-on-operator=${IMAGE_OPERATOR_VERSIONED_TAG}
 
-	@sed -i".out" -e 's@version:[ ].*@version: $(VERSION)@g' config/default/kustomize-config/metadataLabelTransformer.yaml
-	rm -rf config/default/kustomize-config/metadataLabelTransformer.yaml.out
+	cat <<< $$(sed -E 's|(^[[:space:]]+app\.kubernetes\.io/version:).*|\1 $(VERSION)|g' config/default/kustomization.yaml) > config/default/kustomization.yaml
 	$(KUSTOMIZE) build config/default | kubectl apply -f -
 
 undeploy:
diff --git a/README.md b/README.md
index e4e3410..a6218b5 100644
--- a/README.md
+++ b/README.md
@@ -20,9 +20,9 @@
 
 The KEDA HTTP Add-on allows Kubernetes users to automatically scale their HTTP servers up and down (including to/from zero) based on incoming HTTP traffic. Please see our [use cases document](./docs/use_cases.md) to learn more about how and why you would use this project.
 
-| 🚧 **Project status: on-hold/beta** 🚧|
+| 🚧 **Project status: beta** 🚧|
 |---------------------------------------------|
-| :loudspeaker: **KEDA is actively looking for contributors to help maintain the add-on and maintainers will make a decision about future of the add-on at the end of November.** Learn more in [this discussion](https://github.com/kedacore/http-add-on/discussions/523).<br/><br/>⚠ The HTTP Add-on currently is in [beta](https://github.com/kedacore/http-add-on/releases/latest). We can't yet recommend it for production usage because we are still developing and testing it. It may have "rough edges" including missing documentation, bugs and other issues. It is currently provided as-is without support. |
+| :loudspeaker: **KEDA is actively relying on community contributions to help grow & maintain the add-on. The KEDA maintainers are assisting the community to evolve the add-on but not directly responsible for it.** Feel free to [open a new discussion](https://github.com/kedacore/http-add-on/discussions/new/choose) in case of questions.<br/><br/>⚠ The HTTP Add-on currently is in [beta](https://github.com/kedacore/http-add-on/releases/latest). We can't yet recommend it for production usage because we are still developing and testing it. It may have "rough edges" including missing documentation, bugs and other issues. It is currently provided as-is without support. |
 
 ## HTTP Autoscaling Made Simple
 
diff --git a/config/crd/bases/http.keda.sh_httpscaledobjects.yaml b/config/crd/bases/http.keda.sh_httpscaledobjects.yaml
index ac80645..6b01f0b 100644
--- a/config/crd/bases/http.keda.sh_httpscaledobjects.yaml
+++ b/config/crd/bases/http.keda.sh_httpscaledobjects.yaml
@@ -42,6 +42,7 @@ spec:
     name: v1alpha1
     schema:
       openAPIV3Schema:
+        description: HTTPScaledObject is the Schema for the httpscaledobjects API
         properties:
           apiVersion:
             description: 'APIVersion defines the versioned schema of this representation
@@ -113,16 +114,15 @@ spec:
             description: HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
             properties:
               conditions:
-                description: List of auditable conditions of the operator
+                description: Conditions of the operator
                 items:
-                  description: Condition to store the condition state
+                  description: HTTPScaledObjectCondition stores the condition state
                   properties:
                     message:
-                      description: A human readable message indicating details about
-                        the transition.
+                      description: Message indicating details about the transition.
                       type: string
                     reason:
-                      description: The reason for the condition's last transition.
+                      description: Reason for the condition's last transition.
                       enum:
                       - ErrorCreatingAppScaledObject
                       - AppScaledObjectCreated
@@ -142,11 +142,11 @@ spec:
                       description: Type of condition
                       enum:
                       - Created
+                      - Terminated
                       - Error
                       - Pending
-                      - Unknown
                       - Terminating
-                      - Terminated
+                      - Unknown
                       - Ready
                       type: string
                   required:
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
index 698b5e3..a90eb96 100644
--- a/config/crd/kustomization.yaml
+++ b/config/crd/kustomization.yaml
@@ -1,7 +1,5 @@
-# This kustomization.yaml is not intended to be run by itself,
-# since it depends on service name and namespace that are out of this kustomize package.
-# It should be run by config/default
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
 resources:
 - bases/http.keda.sh_httpscaledobjects.yaml
-
-# +kubebuilder:scaffold:crdkustomizeresource
+#+kubebuilder:scaffold:crdkustomizeresource
diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml
index 8f95826..a84cd2b 100644
--- a/config/default/kustomization.yaml
+++ b/config/default/kustomization.yaml
@@ -1,32 +1,21 @@
-# Adds namespace to all resources.
-#namespace: keda
-
-# Value of this field is prepended to the
-# names of all resources, e.g. a deployment named
-# "wordpress" becomes "alices-wordpress".
-# Note that it should also match with the prefix (text before '-') of the namespace
-# field above.
-#namePrefix: keda-
-
-# Labels to add to all resources and selectors.
-#commonLabels:
-#  someName: someValue
-
-# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
-#- ../prometheus
-
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
-# Need this transformer to mitigate a problem with inserting labels into selectors,
-# until this issue is solved: https://github.com/kubernetes-sigs/kustomize/issues/1009
-transformers:
-- kustomize-config/metadataLabelTransformer.yaml
-
 resources:
 - ../crd
 - ../interceptor
 - ../operator
-- ../rbac
 - ../scaler
-- ../service_account
-- ../shared
+namespace: keda
+namePrefix: keda-http-add-on-
+labels:
+- includeSelectors: true
+  includeTemplates: true
+  pairs:
+    app.kubernetes.io/name: http
+    app.kubernetes.io/component: add-on
+    app.kubernetes.io/part-of: keda
+- includeSelectors: false
+  includeTemplates: false
+  pairs:
+    app.kubernetes.io/version: HEAD
+    app.kubernetes.io/managed-by: kustomize
diff --git a/config/default/kustomize-config/metadataLabelTransformer.yaml b/config/default/kustomize-config/metadataLabelTransformer.yaml
deleted file mode 100644
index 7f286a6..0000000
--- a/config/default/kustomize-config/metadataLabelTransformer.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: builtin
-kind: LabelTransformer
-metadata:
-  name: metadataLabelTransformer
-labels:
-  app.kubernetes.io/version: main
-  app.kubernetes.io/part-of: keda-http-add-on
-fieldSpecs:
-- path: metadata/labels
-  create: true
diff --git a/config/interceptor/admin.service.yaml b/config/interceptor/admin.service.yaml
new file mode 100644
index 0000000..6c8bc0c
--- /dev/null
+++ b/config/interceptor/admin.service.yaml
@@ -0,0 +1,11 @@
+# TODO(pedrotorres): remove after implementing new routing table
+apiVersion: v1
+kind: Service
+metadata:
+  name: interceptor-admin
+spec:
+  ports:
+  - name: admin
+    protocol: TCP
+    port: 9090
+    targetPort: admin
diff --git a/config/interceptor/deployment.yaml b/config/interceptor/deployment.yaml
new file mode 100644
index 0000000..bf0fa2b
--- /dev/null
+++ b/config/interceptor/deployment.yaml
@@ -0,0 +1,79 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: interceptor
+spec:
+  replicas: 3
+  template:
+    spec:
+      affinity:
+        nodeAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+            nodeSelectorTerms:
+            - matchExpressions:
+              - key: kubernetes.io/os
+                operator: In
+                values:
+                - linux
+              - key: kubernetes.io/arch
+                operator: In
+                values:
+                - amd64
+                - arm64
+      containers:
+      - name: interceptor
+        image: ghcr.io/kedacore/http-add-on-interceptor
+        env:
+        - name: KEDA_HTTP_ROUTING_TABLE_UPDATE_DURATION_MS
+          value: "500"
+        - name: KEDA_HTTP_CURRENT_NAMESPACE
+          value: "keda"
+        - name: KEDA_HTTP_PROXY_PORT
+          value: "8080"
+        - name: KEDA_HTTP_ADMIN_PORT
+          value: "9090"
+        - name: KEDA_HTTP_CONNECT_TIMEOUT
+          value: "500ms"
+        - name: KEDA_HTTP_KEEP_ALIVE
+          value: "1s"
+        - name: KEDA_RESPONSE_HEADER_TIMEOUT
+          value: "500ms"
+        - name: KEDA_CONDITION_WAIT_TIMEOUT
+          value: "20s"
+        - name: KEDA_HTTP_DEPLOYMENT_CACHE_POLLING_INTERVAL_MS
+          value: "1000"
+        - name: KEDA_HTTP_FORCE_HTTP2
+          value: "false"
+        - name: KEDA_HTTP_MAX_IDLE_CONNS
+          value: "100"
+        - name: KEDA_HTTP_IDLE_CONN_TIMEOUT
+          value: "90s"
+        - name: KEDA_HTTP_TLS_HANDSHAKE_TIMEOUT
+          value: "10s"
+        - name: KEDA_HTTP_EXPECT_CONTINUE_TIMEOUT
+          value: "1s"
+        ports:
+        # TODO(pedrotorres): remove after implementing new routing table
+        - name: admin
+          containerPort: 9090
+        - name: proxy
+          containerPort: 8080
+        # TODO(pedrotorres): set better default values avoiding overcommitment
+        resources:
+          requests:
+            cpu: 100m
+            memory: 100Mi
+          limits:
+            cpu: 1000m
+            memory: 1000Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          capabilities:
+            drop:
+            - ALL
+          seccompProfile:
+            type: RuntimeDefault
+      serviceAccountName: interceptor
+      terminationGracePeriodSeconds: 10
diff --git a/config/interceptor/interceptor.yaml b/config/interceptor/interceptor.yaml
deleted file mode 100644
index f9cefa0..0000000
--- a/config/interceptor/interceptor.yaml
+++ /dev/null
@@ -1,88 +0,0 @@
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: keda-http-add-on-interceptor
-  namespace: keda
-  labels:
-    control-plane: interceptor
-    app: keda-http-add-on
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/component: interceptor
-    app.kubernetes.io/part-of: keda-http-add-on
-spec:
-  replicas: 3
-  selector:
-    matchLabels:
-      control-plane: interceptor
-      app: keda-http-add-on
-  template:
-    metadata:
-      labels:
-        control-plane: interceptor
-        app: keda-http-add-on
-      name: keda-http-add-on-interceptor
-    spec:
-      securityContext:
-        runAsNonRoot: true
-      serviceAccountName: keda-http-add-on-interceptor
-      containers:
-        - name: interceptor
-          image: ghcr.io/kedacore/http-add-on-interceptor:latest
-          command:
-            - /interceptor
-          imagePullPolicy: IfNotPresent
-          resources:
-            requests:
-              cpu: 100m
-              memory: 100Mi
-            limits:
-              cpu: 1000m
-              memory: 1000Mi
-          ports:
-          - containerPort: 9090
-            name: inter-admin
-          - containerPort: 8080
-            name: inter-proxy
-          env:
-          - name: KEDA_HTTP_ROUTING_TABLE_UPDATE_DURATION_MS
-            value: "500"
-          - name: KEDA_HTTP_CURRENT_NAMESPACE
-            value: "keda"
-          - name: KEDA_HTTP_PROXY_PORT
-            value: "8080"
-          - name: KEDA_HTTP_ADMIN_PORT
-            value: "9090"
-          - name: KEDA_HTTP_CONNECT_TIMEOUT
-            value: "500ms"
-          - name: KEDA_HTTP_KEEP_ALIVE
-            value: "1s"
-          - name: KEDA_RESPONSE_HEADER_TIMEOUT
-            value: "500ms"
-          - name: KEDA_CONDITION_WAIT_TIMEOUT
-            value: "20s"
-          - name: KEDA_HTTP_DEPLOYMENT_CACHE_POLLING_INTERVAL_MS
-            value: "1000"
-          - name: KEDA_HTTP_FORCE_HTTP2
-            value: "false"
-          - name: KEDA_HTTP_MAX_IDLE_CONNS
-            value: "100"
-          - name: KEDA_HTTP_IDLE_CONN_TIMEOUT
-            value: "90s"
-          - name: KEDA_HTTP_TLS_HANDSHAKE_TIMEOUT
-            value: "10s"
-          - name: KEDA_HTTP_EXPECT_CONTINUE_TIMEOUT
-            value: "1s"
-          securityContext:
-            runAsNonRoot: true
-            capabilities:
-              drop:
-              - ALL
-            allowPrivilegeEscalation: false
-            readOnlyRootFilesystem: true
-            seccompProfile:
-              type: RuntimeDefault
-      terminationGracePeriodSeconds: 10
-      nodeSelector:
-        kubernetes.io/os: linux
diff --git a/config/interceptor/kustomization.yaml b/config/interceptor/kustomization.yaml
index e9e20b4..fce9640 100644
--- a/config/interceptor/kustomization.yaml
+++ b/config/interceptor/kustomization.yaml
@@ -1,14 +1,14 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
 resources:
-- interceptor.yaml
-- service-admin.yaml
-- service-proxy.yaml
+- deployment.yaml
 - role.yaml
 - role_binding.yaml
+- admin.service.yaml
+- proxy.service.yaml
 - service_account.yaml
-
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-images:
-- name: ghcr.io/kedacore/http-add-on-interceptor
-  newName: ghcr.io/kedacore/http-add-on-interceptor
-  newTag: main
+labels:
+- includeSelectors: true
+  includeTemplates: true
+  pairs:
+    app.kubernetes.io/instance: interceptor
diff --git a/config/interceptor/proxy.service.yaml b/config/interceptor/proxy.service.yaml
new file mode 100644
index 0000000..ad741db
--- /dev/null
+++ b/config/interceptor/proxy.service.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: interceptor-proxy
+spec:
+  type: ClusterIP
+  ports:
+  - name: proxy
+    protocol: TCP
+    port: 8080
+    targetPort: proxy
diff --git a/config/interceptor/role.yaml b/config/interceptor/role.yaml
index 6b105b4..3d5e0d9 100644
--- a/config/interceptor/role.yaml
+++ b/config/interceptor/role.yaml
@@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
   creationTimestamp: null
-  name: keda-http-add-on-interceptor
+  name: interceptor
 rules:
 - apiGroups:
   - apps
@@ -18,7 +18,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
 metadata:
   creationTimestamp: null
-  name: keda-http-add-on-interceptor
+  name: interceptor
   namespace: keda
 rules:
 - apiGroups:
diff --git a/config/interceptor/role_binding.yaml b/config/interceptor/role_binding.yaml
index 5b39945..25459fc 100644
--- a/config/interceptor/role_binding.yaml
+++ b/config/interceptor/role_binding.yaml
@@ -1,26 +1,24 @@
+---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
-  name: keda-http-add-on-interceptor
+  name: interceptor
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
-  name: keda-http-add-on-interceptor
+  name: interceptor
 subjects:
 - kind: ServiceAccount
-  name: keda-http-add-on-interceptor
-  namespace: keda
+  name: interceptor
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
 metadata:
-  name: keda-http-add-on-interceptor
-  namespace: keda
+  name: interceptor
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
-  name: keda-http-add-on-interceptor
+  name: interceptor
 subjects:
 - kind: ServiceAccount
-  name: keda-http-add-on-interceptor
-  namespace: keda
+  name: interceptor
diff --git a/config/interceptor/service-admin.yaml b/config/interceptor/service-admin.yaml
deleted file mode 100644
index 314a433..0000000
--- a/config/interceptor/service-admin.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    control-plane: interceptor-admin
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-interceptor-admin
-  namespace: keda
-spec:
-  ports:
-  - name: https
-    port: 9090
-    targetPort: inter-admin
-  selector:
-    control-plane: interceptor
-    app: keda-http-add-on
diff --git a/config/interceptor/service-proxy.yaml b/config/interceptor/service-proxy.yaml
deleted file mode 100644
index 1e6bc6b..0000000
--- a/config/interceptor/service-proxy.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    control-plane: interceptor-proxy
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-interceptor-proxy
-  namespace: keda
-spec:
-  ports:
-  - name: https
-    port: 8080
-    targetPort: inter-proxy
-  selector:
-    control-plane: interceptor
-    app: keda-http-add-on
diff --git a/config/interceptor/service_account.yaml b/config/interceptor/service_account.yaml
index 9074dac..21be11b 100644
--- a/config/interceptor/service_account.yaml
+++ b/config/interceptor/service_account.yaml
@@ -1,9 +1,4 @@
 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  labels:
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-interceptor
-  namespace: keda
+  name: interceptor
diff --git a/config/operator/auth_proxy_role.yaml b/config/operator/auth_proxy_role.yaml
deleted file mode 100644
index 618f5e4..0000000
--- a/config/operator/auth_proxy_role.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: proxy-role
-rules:
-- apiGroups: ["authentication.k8s.io"]
-  resources:
-  - tokenreviews
-  verbs: ["create"]
-- apiGroups: ["authorization.k8s.io"]
-  resources:
-  - subjectaccessreviews
-  verbs: ["create"]
diff --git a/config/operator/auth_proxy_role_binding.yaml b/config/operator/auth_proxy_role_binding.yaml
deleted file mode 100644
index c115a04..0000000
--- a/config/operator/auth_proxy_role_binding.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: proxy-rolebinding
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: proxy-role
-subjects:
-- kind: ServiceAccount
-  name: keda-http-add-on
-  namespace: keda
diff --git a/config/operator/config_map.yaml b/config/operator/config_map.yaml
new file mode 100644
index 0000000..209f0bf
--- /dev/null
+++ b/config/operator/config_map.yaml
@@ -0,0 +1,7 @@
+# TODO(pedrotorres): remove after implementing new routing table
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: routing-table
+data:
+  routing-table: "{}"
diff --git a/config/operator/deployment.yaml b/config/operator/deployment.yaml
new file mode 100644
index 0000000..e84fcaf
--- /dev/null
+++ b/config/operator/deployment.yaml
@@ -0,0 +1,67 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: operator
+spec:
+  template:
+    spec:
+      affinity:
+        nodeAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+            nodeSelectorTerms:
+            - matchExpressions:
+              - key: kubernetes.io/os
+                operator: In
+                values:
+                - linux
+              - key: kubernetes.io/arch
+                operator: In
+                values:
+                - amd64
+                - arm64
+      containers:
+      - name: operator
+        image: ghcr.io/kedacore/http-add-on-operator
+        args:
+        - --leader-elect
+        # TODO(pedrotorres): remove after implementing new routing table
+        - --admin-port=9090
+        env:
+        # TODO(pedrotorres): remove after implementing new routing table
+        - name: KEDAHTTP_INTERCEPTOR_SERVICE
+          value: "keda-http-add-on-interceptor-admin"
+        - name: KEDAHTTP_OPERATOR_EXTERNAL_SCALER_SERVICE
+          value: "keda-http-add-on-external-scaler"
+        - name: KEDAHTTP_OPERATOR_EXTERNAL_SCALER_PORT
+          value: "9090"
+        - name: KEDAHTTP_INTERCEPTOR_ADMIN_PORT
+          value: "9090"
+        - name: KEDAHTTP_INTERCEPTOR_PROXY_PORT
+          value: "8080"
+        - name: KEDA_HTTP_OPERATOR_NAMESPACE
+          value: "keda"
+        - name: KEDA_HTTP_OPERATOR_WATCH_NAMESPACE
+          value: ""
+        # TODO(pedrotorres): remove after implementing new routing table
+        ports:
+        - name: admin
+          containerPort: 9090
+        # TODO(pedrotorres): set better default values avoiding overcommitment
+        resources:
+          requests:
+            cpu: 100m
+            memory: 100Mi
+          limits:
+            cpu: 1000m
+            memory: 1000Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          capabilities:
+            drop:
+            - ALL
+          seccompProfile:
+            type: RuntimeDefault
+      serviceAccountName: operator
+      terminationGracePeriodSeconds: 10
diff --git a/config/operator/kustomization.yaml b/config/operator/kustomization.yaml
index dfb038f..726690b 100644
--- a/config/operator/kustomization.yaml
+++ b/config/operator/kustomization.yaml
@@ -1,13 +1,14 @@
-resources:
-- auth_proxy_role_binding.yaml
-- auth_proxy_role.yaml
-- operator.yaml
-- service-admin.yaml
-- service-metrics.yaml
-
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
-images:
-- name: ghcr.io/kedacore/http-add-on-operator
-  newName: ghcr.io/kedacore/http-add-on-operator
-  newTag: main
+resources:
+- config_map.yaml
+- deployment.yaml
+- role.yaml
+- role_binding.yaml
+- service.yaml
+- service_account.yaml
+labels:
+- includeSelectors: true
+  includeTemplates: true
+  pairs:
+    app.kubernetes.io/instance: operator
diff --git a/config/operator/operator.yaml b/config/operator/operator.yaml
deleted file mode 100644
index 995107e..0000000
--- a/config/operator/operator.yaml
+++ /dev/null
@@ -1,93 +0,0 @@
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: keda-http-add-on-operator
-  namespace: keda
-  labels:
-    control-plane: controller-manager
-    app: keda-http-add-on
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/component: operator
-    app.kubernetes.io/part-of: keda-http-add-on
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      control-plane: controller-manager
-      app: keda-http-add-on
-  template:
-    metadata:
-      labels:
-        control-plane: controller-manager
-        app: keda-http-add-on
-      name: keda-http-add-on-operator
-    spec:
-      serviceAccountName: keda-http-add-on
-      containers:
-        - name: kube-rbac-proxy
-          image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0
-          args:
-          - --secure-listen-address=0.0.0.0:8443
-          - --upstream=http://127.0.0.1:8080/
-          - --logtostderr=true
-          - --v=10
-          ports:
-          - containerPort: 8443
-            name: https
-          securityContext:
-            runAsNonRoot: true
-          resources:
-            requests:
-              cpu: 10m
-              memory: 100Mi
-            limits:
-              cpu: 100m
-              memory: 100Mi
-        - name: operator
-          image: ghcr.io/kedacore/http-add-on-operator:latest
-          command:
-            - /operator
-          args:
-            - --metrics-addr=127.0.0.1:8080
-            - --enable-leader-election
-            - --admin-port=9090
-          imagePullPolicy: IfNotPresent
-          resources:
-            requests:
-              cpu: 100m
-              memory: 100Mi
-            limits:
-              cpu: 1000m
-              memory: 1000Mi
-          ports:
-          - containerPort: 9090
-            name: admin-http
-          env:
-          - name: KEDAHTTP_INTERCEPTOR_SERVICE
-            value: keda-http-add-on-interceptor-admin
-          - name: KEDAHTTP_OPERATOR_EXTERNAL_SCALER_SERVICE
-            value: keda-http-add-on-external-scaler
-          - name: KEDAHTTP_OPERATOR_EXTERNAL_SCALER_PORT
-            value: "9090"
-          - name: KEDAHTTP_INTERCEPTOR_ADMIN_PORT
-            value: "9090"
-          - name: KEDAHTTP_INTERCEPTOR_PROXY_PORT
-            value: "8080"
-          - name: KEDA_HTTP_OPERATOR_NAMESPACE
-            value: "keda"
-          - name: KEDA_HTTP_OPERATOR_WATCH_NAMESPACE
-            value: ""
-          securityContext:
-            runAsNonRoot: true
-            capabilities:
-              drop:
-              - ALL
-            allowPrivilegeEscalation: false
-            readOnlyRootFilesystem: true
-            seccompProfile:
-              type: RuntimeDefault
-      terminationGracePeriodSeconds: 10
-      nodeSelector:
-        kubernetes.io/os: linux
diff --git a/config/rbac/role.yaml b/config/operator/role.yaml
similarity index 95%
rename from config/rbac/role.yaml
rename to config/operator/role.yaml
index 6ece40b..6491582 100644
--- a/config/rbac/role.yaml
+++ b/config/operator/role.yaml
@@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
   creationTimestamp: null
-  name: keda-http-add-on
+  name: operator
 rules:
 - apiGroups:
   - http.keda.sh
@@ -48,7 +48,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
 metadata:
   creationTimestamp: null
-  name: keda-http-add-on
+  name: operator
   namespace: keda
 rules:
 - apiGroups:
diff --git a/config/rbac/role_binding.yaml b/config/operator/role_binding.yaml
similarity index 62%
rename from config/rbac/role_binding.yaml
rename to config/operator/role_binding.yaml
index b43fea4..e8bf02c 100644
--- a/config/rbac/role_binding.yaml
+++ b/config/operator/role_binding.yaml
@@ -1,26 +1,24 @@
+---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
-  name: keda-http-add-on
+  name: operator
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
-  name: keda-http-add-on
+  name: operator
 subjects:
 - kind: ServiceAccount
-  name: keda-http-add-on
-  namespace: keda
+  name: operator
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
 metadata:
-  name: keda-http-add-on
-  namespace: keda
+  name: operator
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
-  name: keda-http-add-on
+  name: operator
 subjects:
 - kind: ServiceAccount
-  name: keda-http-add-on
-  namespace: keda
+  name: operator
diff --git a/config/operator/service-admin.yaml b/config/operator/service-admin.yaml
deleted file mode 100644
index 232453f..0000000
--- a/config/operator/service-admin.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    control-plane: operator-admin
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-operator-admin
-  namespace: keda
-spec:
-  ports:
-  - name: admin-http
-    port: 9090
-    targetPort: admin-http
-  selector:
-    control-plane: controller-manager
-    app: keda-http-add-on
diff --git a/config/operator/service-metrics.yaml b/config/operator/service-metrics.yaml
deleted file mode 100644
index 15cea5b..0000000
--- a/config/operator/service-metrics.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  labels:
-    control-plane: controller-manager
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-controller-manager-metrics-service
-  namespace: keda
-spec:
-  ports:
-  - name: https
-    port: 8443
-    targetPort: https
-  selector:
-    control-plane: controller-manager
-    app: keda-http-add-on
diff --git a/config/operator/service.yaml b/config/operator/service.yaml
new file mode 100644
index 0000000..ccbca9e
--- /dev/null
+++ b/config/operator/service.yaml
@@ -0,0 +1,12 @@
+# TODO(pedrotorres): remove after implementing new routing table
+apiVersion: v1
+kind: Service
+metadata:
+  name: operator-admin
+spec:
+  type: ClusterIP
+  ports:
+  - name: admin
+    protocol: TCP
+    port: 9090
+    targetPort: admin
diff --git a/config/operator/service_account.yaml b/config/operator/service_account.yaml
new file mode 100644
index 0000000..5b9d505
--- /dev/null
+++ b/config/operator/service_account.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: operator
diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml
deleted file mode 100644
index de028b8..0000000
--- a/config/rbac/kustomization.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-commonLabels:
-  app.kubernetes.io/name: keda-http-add-on
-  app.kubernetes.io/part-of: keda-http-add-on
-
-resources:
-- role.yaml
-- role_binding.yaml
diff --git a/config/scaler/deployment.yaml b/config/scaler/deployment.yaml
new file mode 100644
index 0000000..abacafc
--- /dev/null
+++ b/config/scaler/deployment.yaml
@@ -0,0 +1,66 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: scaler
+spec:
+  template:
+    spec:
+      affinity:
+        nodeAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+            nodeSelectorTerms:
+            - matchExpressions:
+              - key: kubernetes.io/os
+                operator: In
+                values:
+                - linux
+              - key: kubernetes.io/arch
+                operator: In
+                values:
+                - amd64
+                - arm64
+      containers:
+      - name: scaler
+        image: ghcr.io/kedacore/http-add-on-scaler
+        env:
+        # TODO(pedrotorres): remove after implementing new routing table
+        - name: KEDA_HTTP_SCALER_TARGET_ADMIN_DEPLOYMENT
+          value: "keda-http-add-on-interceptor"
+        - name: KEDA_HTTP_SCALER_PORT
+          value: "9090"
+        - name: KEDA_HTTP_HEALTH_PORT
+          value: "9091"
+        - name: KEDA_HTTP_SCALER_TARGET_ADMIN_NAMESPACE
+          value: "keda"
+          # TODO(pedrotorres): remove after implementing new routing table
+        - name: KEDA_HTTP_SCALER_TARGET_ADMIN_SERVICE
+          value: "keda-http-add-on-interceptor-admin"
+          # TODO(pedrotorres): remove after implementing new routing table
+        - name: KEDA_HTTP_SCALER_TARGET_ADMIN_PORT
+          value: "9090"
+        - name: KEDA_HTTP_SCALER_TARGET_PENDING_REQUESTS_INTERCEPTOR
+          value: "200"
+        ports:
+        - name: grpc
+          containerPort: 9090
+        - name: health
+          containerPort: 9091
+        # TODO(pedrotorres): set better default values avoiding overcommitment
+        resources:
+          requests:
+            cpu: 100m
+            memory: 100Mi
+          limits:
+            cpu: 1000m
+            memory: 1000Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          capabilities:
+            drop:
+            - ALL
+          seccompProfile:
+            type: RuntimeDefault
+      serviceAccountName: scaler
+      terminationGracePeriodSeconds: 10
diff --git a/config/scaler/kustomization.yaml b/config/scaler/kustomization.yaml
index fbd3167..a5c736a 100644
--- a/config/scaler/kustomization.yaml
+++ b/config/scaler/kustomization.yaml
@@ -1,13 +1,8 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
 resources:
-- scaler.yaml
-- service.yaml
+- deployment.yaml
 - role.yaml
 - role_binding.yaml
+- service.yaml
 - service_account.yaml
-
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-images:
-- name: ghcr.io/kedacore/http-add-on-scaler
-  newName: ghcr.io/kedacore/http-add-on-scaler
-  newTag: main
diff --git a/config/scaler/role.yaml b/config/scaler/role.yaml
index 54d3338..5d136da 100644
--- a/config/scaler/role.yaml
+++ b/config/scaler/role.yaml
@@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
   creationTimestamp: null
-  name: keda-http-add-on-scaler
+  name: scaler
 rules:
 - apiGroups:
   - ""
@@ -26,7 +26,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
 metadata:
   creationTimestamp: null
-  name: keda-http-add-on-scaler
+  name: scaler
   namespace: keda
 rules:
 - apiGroups:
diff --git a/config/scaler/role_binding.yaml b/config/scaler/role_binding.yaml
index 7af45d2..d184e77 100644
--- a/config/scaler/role_binding.yaml
+++ b/config/scaler/role_binding.yaml
@@ -1,26 +1,25 @@
+---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
-  name: keda-http-add-on-scaler
+  name: scaler
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
-  name: keda-http-add-on-scaler
+  name: scaler
 subjects:
 - kind: ServiceAccount
-  name: keda-http-add-on-scaler
-  namespace: keda
+  name: scaler
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
 metadata:
-  name: keda-http-add-on-scaler
+  name: scaler
   namespace: keda
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
-  name: keda-http-add-on-scaler
+  name: scaler
 subjects:
 - kind: ServiceAccount
-  name: keda-http-add-on-scaler
-  namespace: keda
+  name: scaler
diff --git a/config/scaler/scaler.yaml b/config/scaler/scaler.yaml
deleted file mode 100644
index b96f81e..0000000
--- a/config/scaler/scaler.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: keda-http-add-on-scaler
-  namespace: keda
-  labels:
-    control-plane: external-scaler
-    app: keda-http-add-on
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/component: scaler
-    app.kubernetes.io/part-of: keda-http-add-on
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      control-plane: external-scaler
-      app: keda-http-add-on
-  template:
-    metadata:
-      labels:
-        control-plane: external-scaler
-        app: keda-http-add-on
-      name: keda-http-add-on-scaler
-    spec:
-      securityContext:
-        runAsNonRoot: true
-      serviceAccountName: keda-http-add-on-scaler
-      containers:
-        - name: external-scaler
-          image: ghcr.io/kedacore/http-add-on-scaler:latest
-          command:
-            - /scaler
-          imagePullPolicy: IfNotPresent
-          resources:
-            requests:
-              cpu: 100m
-              memory: 100Mi
-            limits:
-              cpu: 1000m
-              memory: 1000Mi
-          ports:
-          - containerPort: 9091
-            name: scaler-health
-          - containerPort: 9090
-            name: scaler-grpc
-          env:
-          - name: KEDA_HTTP_SCALER_TARGET_ADMIN_DEPLOYMENT
-            value: "keda-http-add-on-interceptor"
-          - name: KEDA_HTTP_SCALER_PORT
-            value: "9090"
-          - name: KEDA_HTTP_HEALTH_PORT
-            value: "9091"
-          - name: KEDA_HTTP_SCALER_TARGET_ADMIN_NAMESPACE
-            value: "keda"
-          - name: KEDA_HTTP_SCALER_TARGET_ADMIN_SERVICE
-            value: "keda-http-add-on-interceptor-admin"
-          - name: KEDA_HTTP_SCALER_TARGET_ADMIN_PORT
-            value: "9090"
-          - name: KEDA_HTTP_SCALER_TARGET_PENDING_REQUESTS_INTERCEPTOR
-            value: "200"
-          securityContext:
-            runAsNonRoot: true
-            capabilities:
-              drop:
-              - ALL
-            allowPrivilegeEscalation: false
-            readOnlyRootFilesystem: true
-            seccompProfile:
-              type: RuntimeDefault
-      terminationGracePeriodSeconds: 10
-      nodeSelector:
-        kubernetes.io/os: linux
diff --git a/config/scaler/service.yaml b/config/scaler/service.yaml
index 324acc1..0e6ad79 100644
--- a/config/scaler/service.yaml
+++ b/config/scaler/service.yaml
@@ -1,21 +1,14 @@
 apiVersion: v1
 kind: Service
 metadata:
-  labels:
-    control-plane: external-scaler
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-external-scaler
-  namespace: keda
+  name: external-scaler
 spec:
   ports:
   - name: grpc
+    protocol: TCP
     port: 9090
-    targetPort: scaler-grpc
+    targetPort: grpc
   - name: health
+    protocol: TCP
     port: 9091
-    targetPort: scaler-health
-  selector:
-    control-plane: external-scaler
-    app: keda-http-add-on
+    targetPort: health
diff --git a/config/scaler/service_account.yaml b/config/scaler/service_account.yaml
index 856d8ca..30b3c85 100644
--- a/config/scaler/service_account.yaml
+++ b/config/scaler/service_account.yaml
@@ -1,9 +1,4 @@
 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  labels:
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on-scaler
-  namespace: keda
+  name: scaler
diff --git a/config/service_account/kustomization.yaml b/config/service_account/kustomization.yaml
deleted file mode 100644
index 4256b2b..0000000
--- a/config/service_account/kustomization.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-resources:
-- service_account.yaml
-
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
diff --git a/config/service_account/service_account.yaml b/config/service_account/service_account.yaml
deleted file mode 100644
index 7d2c683..0000000
--- a/config/service_account/service_account.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  labels:
-    app.kubernetes.io/name: keda-http-add-on
-    app.kubernetes.io/version: latest
-    app.kubernetes.io/part-of: keda-http-add-on
-  name: keda-http-add-on
-  namespace: keda
diff --git a/config/shared/kustomization.yaml b/config/shared/kustomization.yaml
deleted file mode 100644
index 3cf18f3..0000000
--- a/config/shared/kustomization.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-resources:
-- routing-table.yaml
-
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
diff --git a/config/shared/routing-table.yaml b/config/shared/routing-table.yaml
deleted file mode 100644
index 532b6f3..0000000
--- a/config/shared/routing-table.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: keda-http-routing-table
-  namespace: keda
-  labels:
-    "control-plane": "operator"
-    "keda.sh/addon": "http-add-on"
-    "app":           "http-add-on"
-    "name":          "http-add-on-routing-table"
-data:
-  # this is where the operator writes the encoded routing table,
-  # and where the interceptor/scaler reads it
-  routing-table: "{}"
diff --git a/go.mod b/go.mod
index 7786c3c..c84be44 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/go-logr/zapr v1.2.3
 	github.com/kedacore/keda/v2 v2.9.3
 	github.com/kelseyhightower/envconfig v1.4.0
-	github.com/onsi/ginkgo v1.16.5
+	github.com/onsi/ginkgo/v2 v2.8.4
 	github.com/onsi/gomega v1.27.2
 	github.com/pkg/errors v0.9.1
 	github.com/stretchr/testify v1.8.2
@@ -19,6 +19,7 @@ require (
 	k8s.io/api v0.26.2
 	k8s.io/apimachinery v0.26.2
 	k8s.io/client-go v0.26.2
+	k8s.io/code-generator v0.26.2
 	k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5
 	sigs.k8s.io/controller-runtime v0.14.5
 )
@@ -35,50 +36,51 @@ require (
 	github.com/go-openapi/jsonpointer v0.19.6 // indirect
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
 	github.com/go-openapi/swag v0.22.3 // indirect
+	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/gnostic v0.6.9 // indirect
 	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
+	github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect
 	github.com/google/uuid v1.3.0 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/kr/pretty v0.3.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/nxadm/tail v1.4.8 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_golang v1.14.0 // indirect
 	github.com/prometheus/client_model v0.3.0 // indirect
 	github.com/prometheus/common v0.41.0 // indirect
 	github.com/prometheus/procfs v0.9.0 // indirect
-	github.com/rogpeppe/go-internal v1.8.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
-	go.uber.org/multierr v1.9.0 // indirect
-	golang.org/x/net v0.8.0 // indirect
-	golang.org/x/oauth2 v0.6.0 // indirect
-	golang.org/x/sys v0.6.0 // indirect
-	golang.org/x/term v0.6.0 // indirect
-	golang.org/x/text v0.8.0 // indirect
+	go.uber.org/multierr v1.8.0 // indirect
+	golang.org/x/mod v0.8.0 // indirect
+	golang.org/x/net v0.7.0 // indirect
+	golang.org/x/oauth2 v0.5.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
+	golang.org/x/term v0.5.0 // indirect
+	golang.org/x/text v0.7.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
+	golang.org/x/tools v0.6.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
+	google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
-	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/apiextensions-apiserver v0.26.2 // indirect
 	k8s.io/component-base v0.26.2 // indirect
+	k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9 // indirect
 	k8s.io/klog/v2 v2.90.1 // indirect
-	k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d // indirect
-	knative.dev/pkg v0.0.0-20230306160420-310274d8e70e // indirect
+	k8s.io/kube-openapi v0.0.0-20230228151317-19cbebb19cb7 // indirect
+	knative.dev/pkg v0.0.0-20221123154742-05b694ec4d3a // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
 	sigs.k8s.io/yaml v1.3.0 // indirect
diff --git a/go.sum b/go.sum
index a7fb346..0c95181 100644
--- a/go.sum
+++ b/go.sum
@@ -39,11 +39,10 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
 github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
 github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
 github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
@@ -91,14 +90,15 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
+github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso=
+github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -114,9 +114,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -132,19 +131,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
 github.com/onsi/ginkgo/v2 v2.8.4 h1:gf5mIQ8cLFieruNLAdgijHF1PYfLphKm2dxxcUtcqK0=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/ginkgo/v2 v2.8.4/go.mod h1:427dEDQZkDKsBvCjc2A/ZPefhKxsTTrsQegMlayL730=
 github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY=
 github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -160,9 +150,6 @@ github.com/prometheus/common v0.41.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr
 github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
 github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
-github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -191,8 +178,8 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0
 go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
 go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
-go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
+go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
 go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
 go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
 go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
@@ -207,9 +194,9 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -217,17 +204,16 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
-golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
+golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
+golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -237,32 +223,27 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -272,10 +253,11 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -291,8 +273,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
-google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
+google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
+google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@@ -321,17 +303,11 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -350,21 +326,27 @@ k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ=
 k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I=
 k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI=
 k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU=
+k8s.io/code-generator v0.26.2 h1:QMgN5oXUgQe27uMaqpbT0hg6ti+rvgCWaHEDMHVhox8=
+k8s.io/code-generator v0.26.2/go.mod h1:ryaiIKwfxEJEaywEzx3dhWOydpVctKYbqLajJf0O8dI=
 k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI=
 k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs=
+k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9 h1:iu3o/SxaHVI7tKPtkGzD3M9IzrE21j+CUKH98NQJ8Ms=
+k8s.io/gengo v0.0.0-20221011193443-fad74ee6edd9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
 k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d h1:VcFq5n7wCJB2FQMCIHfC+f+jNcGgNMar1uKd6rVlifU=
-k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY=
+k8s.io/kube-openapi v0.0.0-20230228151317-19cbebb19cb7 h1:GGNnJLiG6Dk4o75ZHMD40IVsodq/s4kiR0Va5orFZco=
+k8s.io/kube-openapi v0.0.0-20230228151317-19cbebb19cb7/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY=
 k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk=
 k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-knative.dev/pkg v0.0.0-20230306160420-310274d8e70e h1:A2TA2t6aRhoWzWlL5i7DsyuactTJ0Zy44dis53LnS2c=
-knative.dev/pkg v0.0.0-20230306160420-310274d8e70e/go.mod h1:TlO24rbgdsfqpkwZi1Ddue/LiVbvzankhaXpVdP+Nok=
+knative.dev/pkg v0.0.0-20221123154742-05b694ec4d3a h1:mTDxXL+zRBMz7BcdM3WOgw9FVbmkIN/3cvEj4MeS8zI=
+knative.dev/pkg v0.0.0-20221123154742-05b694ec4d3a/go.mod h1:fckNBPf9bu5/p1RbnOhEauX7r+kfN1zSQupEVtkaYBs=
 sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s=
 sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
 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/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
 sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
 sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt
index c38248e..baf2ec6 100644
--- a/hack/boilerplate.go.txt
+++ b/hack/boilerplate.go.txt
@@ -1,5 +1,5 @@
 /*
-
+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.
diff --git a/hack/tools.go b/hack/tools.go
new file mode 100644
index 0000000..596af0a
--- /dev/null
+++ b/hack/tools.go
@@ -0,0 +1,26 @@
+//go:build tools
+// +build tools
+
+/*
+Copyright 2019 The Kubernetes Authors.
+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.
+*/
+
+// This package imports things required by build scripts, to force `go mod` to see them as dependencies
+package hack
+
+import (
+	_ "k8s.io/code-generator"
+)
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
new file mode 100755
index 0000000..21e5d8e
--- /dev/null
+++ b/hack/update-codegen.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+# Copyright 2017 The Kubernetes Authors.
+# 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.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+CODEGEN_PKG="${CODEGEN_PKG:-$(go list -f '{{ .Dir }}' -m k8s.io/code-generator 2>/dev/null)}"
+SCRIPT_ROOT="$(dirname "${BASH_SOURCE[0]}")/.."
+OUTPUT_BASE="$(mktemp -d)"
+
+GO_PACKAGE='github.com/kedacore/http-add-on'
+GEN_SUFFIX="operator/generated"
+API_SUFFIX="operator/apis"
+
+. "${CODEGEN_PKG}/generate-groups.sh" \
+  'client,informer,lister' \
+  "${GO_PACKAGE}/${GEN_SUFFIX}" \
+  "${GO_PACKAGE}/${API_SUFFIX}" \
+  'http:v1alpha1' \
+  --go-header-file "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
+  --output-base "${OUTPUT_BASE}"
+
+rm -fR "${SCRIPT_ROOT}/${GEN_SUFFIX}"
+mv -nT "${OUTPUT_BASE}/${GO_PACKAGE}/${GEN_SUFFIX}" "${SCRIPT_ROOT}/${GEN_SUFFIX}"
+
+rm -fR "${OUTPUT_BASE}"
diff --git a/hack/verify-codegen.sh b/hack/verify-codegen.sh
new file mode 100755
index 0000000..d39ae95
--- /dev/null
+++ b/hack/verify-codegen.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+# Copyright 2017 The Kubernetes Authors.
+# 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.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
+
+DIFFROOT="${SCRIPT_ROOT}/pkg"
+TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg"
+_tmp="${SCRIPT_ROOT}/_tmp"
+
+cleanup() {
+  rm -rf "${_tmp}"
+}
+trap "cleanup" EXIT SIGINT
+
+cleanup
+
+mkdir -p "${TMP_DIFFROOT}"
+cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}"
+
+make codegen
+echo "diffing ${DIFFROOT} against freshly generated codegen"
+ret=0
+diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$?
+cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}"
+if [[ $ret -eq 0 ]]
+then
+  echo "${DIFFROOT} up to date."
+else
+  echo "${DIFFROOT} is out of date. Please run '${SCRIPT_ROOT}/hack/update-codegen.sh'"
+  exit 1
+fi
diff --git a/hack/verify-manifests.sh b/hack/verify-manifests.sh
old mode 100755
new mode 100644
index b94f347..58b3557
--- a/hack/verify-manifests.sh
+++ b/hack/verify-manifests.sh
@@ -1,5 +1,20 @@
 #!/usr/bin/env bash
 
+# Copyright 2017 The Kubernetes Authors.
+# 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.
+
 set -o errexit
 set -o nounset
 set -o pipefail
@@ -21,7 +36,6 @@ mkdir -p "${TMP_DIFFROOT}"
 cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}"
 
 make manifests
-
 echo "diffing ${DIFFROOT} against freshly generated codegen"
 ret=0
 diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$?
diff --git a/operator/.dockerignore b/operator/.dockerignore
new file mode 100644
index 0000000..0f04682
--- /dev/null
+++ b/operator/.dockerignore
@@ -0,0 +1,4 @@
+# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
+# Ignore build and test binaries.
+bin/
+testbin/
diff --git a/operator/.gitignore b/operator/.gitignore
index d97ffc5..e917e5c 100644
--- a/operator/.gitignore
+++ b/operator/.gitignore
@@ -6,6 +6,8 @@
 *.so
 *.dylib
 bin
+testbin/*
+Dockerfile.cross
 
 # Test binary, build with `go test -c`
 *.test
diff --git a/operator/Dockerfile b/operator/Dockerfile
index c594b7d..23daa07 100644
--- a/operator/Dockerfile
+++ b/operator/Dockerfile
@@ -1,29 +1,36 @@
-# Build the adapter binary
+# Build the manager binary
 FROM --platform=$BUILDPLATFORM ghcr.io/kedacore/build-tools:1.19.5 as builder
-
 ARG VERSION=main
 ARG GIT_COMMIT=HEAD
 
 WORKDIR /workspace
-
+# Copy the Go Modules manifests
 COPY go.mod go.mod
 COPY go.sum go.sum
+# cache deps before building and copying source so that we don't need to re-download as much
+# and so that source changes don't invalidate our downloaded layer
 RUN go mod download
 
-COPY . .
+# Copy the go source
+COPY operator operator
+COPY pkg pkg
+COPY proto proto
+COPY Makefile Makefile
 
 # Build
-# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
+# the ARCH has not a default value to allow the binary be built according to the host where the command
+# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
+# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
+# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
 ARG TARGETOS
 ARG TARGETARCH
-RUN VERSION=${VERSION} GIT_COMMIT=${GIT_COMMIT} TARGET_OS=$TARGETOS ARCH=$TARGETARCH make build-operator
+RUN VERSION=${VERSION} GIT_COMMIT=${GIT_COMMIT} TARGET_OS=${TARGETOS:-linux} ARCH=${TARGETARCH} make build-operator
 
 # Use distroless as minimal base image to package the manager binary
 # Refer to https://github.com/GoogleContainerTools/distroless for more details
 FROM gcr.io/distroless/static:nonroot
 WORKDIR /
 COPY --from=builder /workspace/bin/operator .
-# 65532 is numeric for nonroot
 USER 65532:65532
 
 ENTRYPOINT ["/operator"]
diff --git a/operator/PROJECT b/operator/PROJECT
index 51cf82a..ce20cd1 100644
--- a/operator/PROJECT
+++ b/operator/PROJECT
@@ -1,7 +1,20 @@
 domain: keda.sh
-repo: github.com/kedacore/http-add-on
+layout:
+- go.kubebuilder.io/v3
+multigroup: true
+plugins:
+  manifests.sdk.operatorframework.io/v2: {}
+  scorecard.sdk.operatorframework.io/v2: {}
+projectName: http-addon
+repo: github.com/kedacore/http-add-on/operator
 resources:
-- group: http
+- api:
+    crdVersion: v1
+    namespaced: true
+  controller: true
+  domain: keda.sh
+  group: http
   kind: HTTPScaledObject
+  path: github.com/kedacore/http-add-on/operator/apis/http/v1alpha1
   version: v1alpha1
-version: "2"
+version: "3"
diff --git a/operator/api/v1alpha1/groupversion_info.go b/operator/apis/http/v1alpha1/groupversion_info.go
similarity index 68%
rename from operator/api/v1alpha1/groupversion_info.go
rename to operator/apis/http/v1alpha1/groupversion_info.go
index 8c84326..5d0fd95 100644
--- a/operator/api/v1alpha1/groupversion_info.go
+++ b/operator/apis/http/v1alpha1/groupversion_info.go
@@ -1,5 +1,5 @@
 /*
-
+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.
@@ -25,12 +25,17 @@ import (
 )
 
 var (
-	// GroupVersion is group version used to register these objects
-	GroupVersion = schema.GroupVersion{Group: "http.keda.sh", Version: "v1alpha1"}
+	// SchemeGroupVersion is group version used to register these objects
+	SchemeGroupVersion = schema.GroupVersion{Group: "http.keda.sh", Version: "v1alpha1"}
 
 	// SchemeBuilder is used to add go types to the GroupVersionKind scheme
-	SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+	SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
 
 	// AddToScheme adds the types in this group-version to the given scheme.
 	AddToScheme = SchemeBuilder.AddToScheme
 )
+
+// Resource takes an unqualified resource and returns a Group qualified GroupResource
+func Resource(resource string) schema.GroupResource {
+	return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
diff --git a/operator/api/v1alpha1/httpscaledobject_types.go b/operator/apis/http/v1alpha1/httpscaledobject_types.go
similarity index 77%
rename from operator/api/v1alpha1/httpscaledobject_types.go
rename to operator/apis/http/v1alpha1/httpscaledobject_types.go
index 3e59a7c..5bc5b69 100644
--- a/operator/api/v1alpha1/httpscaledobject_types.go
+++ b/operator/apis/http/v1alpha1/httpscaledobject_types.go
@@ -1,5 +1,5 @@
 /*
-
+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.
@@ -20,25 +20,49 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
+// ScaleTargetRef contains all the details about an HTTP application to scale and route to
+type ScaleTargetRef struct {
+	// The name of the deployment to scale according to HTTP traffic
+	Deployment string `json:"deployment"`
+	// The name of the service to route to
+	Service string `json:"service"`
+	// The port to route to
+	Port int32 `json:"port"`
+}
+
+// ReplicaStruct contains the minimum and maximum amount of replicas to have in the deployment
+type ReplicaStruct struct {
+	// Minimum amount of replicas to have in the deployment (Default 0)
+	Min *int32 `json:"min,omitempty" description:"Minimum amount of replicas to have in the deployment (Default 0)"`
+	// Maximum amount of replicas to have in the deployment (Default 100)
+	Max *int32 `json:"max,omitempty" description:"Maximum amount of replicas to have in the deployment (Default 100)"`
+}
+
+// HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
+type HTTPScaledObjectSpec struct {
+	// The host to route. All requests with this host in the "Host" header will
+	// be routed to the Service and Port specified in the scaleTargetRef
+	Host string `json:"host"`
+	// The name of the deployment to route HTTP requests to (and to autoscale).
+	// Either this or Image must be set
+	ScaleTargetRef *ScaleTargetRef `json:"scaleTargetRef"`
+	// (optional) Replica information
+	// +optional
+	Replicas *ReplicaStruct `json:"replicas,omitempty"`
+	// (optional) Target metric value
+	// +optional
+	TargetPendingRequests *int32 `json:"targetPendingRequests,omitempty" description:"The target metric value for the HPA (Default 100)"`
+	// (optional) Cooldown period value
+	// +optional
+	CooldownPeriod *int32 `json:"scaledownPeriod,omitempty" description:"Cooldown period (seconds) for resources to scale down (Default 300)"`
+}
+
+// +kubebuilder:validation:Enum=Created;Terminated;Error;Pending;Terminating;Unknown;Ready
+
 // HTTPScaledObjectCreationStatus describes the creation status
 // of the scaler's additional resources such as Services, Ingresses and Deployments
-// +kubebuilder:validation:Enum=Created;Error;Pending;Unknown;Terminating;Terminated;Ready
 type HTTPScaledObjectCreationStatus string
 
-// HTTPScaledObjectConditionReason describes the reason why the condition transitioned
-// +kubebuilder:validation:Enum=ErrorCreatingAppScaledObject;AppScaledObjectCreated;TerminatingResources;AppScaledObjectTerminated;AppScaledObjectTerminationError;PendingCreation;HTTPScaledObjectIsReady;
-type HTTPScaledObjectConditionReason string
-
-const (
-	ErrorCreatingAppScaledObject    HTTPScaledObjectConditionReason = "ErrorCreatingAppScaledObject"
-	AppScaledObjectCreated          HTTPScaledObjectConditionReason = "AppScaledObjectCreated"
-	TerminatingResources            HTTPScaledObjectConditionReason = "TerminatingResources"
-	AppScaledObjectTerminated       HTTPScaledObjectConditionReason = "AppScaledObjectTerminated"
-	AppScaledObjectTerminationError HTTPScaledObjectConditionReason = "AppScaledObjectTerminationError"
-	PendingCreation                 HTTPScaledObjectConditionReason = "PendingCreation"
-	HTTPScaledObjectIsReady         HTTPScaledObjectConditionReason = "HTTPScaledObjectIsReady"
-)
-
 const (
 	// Created indicates the resource has been created
 	Created HTTPScaledObjectCreationStatus = "Created"
@@ -57,7 +81,22 @@ const (
 	Ready HTTPScaledObjectCreationStatus = "Ready"
 )
 
-// Condition to store the condition state
+// +kubebuilder:validation:Enum=ErrorCreatingAppScaledObject;AppScaledObjectCreated;TerminatingResources;AppScaledObjectTerminated;AppScaledObjectTerminationError;PendingCreation;HTTPScaledObjectIsReady;
+
+// HTTPScaledObjectConditionReason describes the reason why the condition transitioned
+type HTTPScaledObjectConditionReason string
+
+const (
+	ErrorCreatingAppScaledObject    HTTPScaledObjectConditionReason = "ErrorCreatingAppScaledObject"
+	AppScaledObjectCreated          HTTPScaledObjectConditionReason = "AppScaledObjectCreated"
+	TerminatingResources            HTTPScaledObjectConditionReason = "TerminatingResources"
+	AppScaledObjectTerminated       HTTPScaledObjectConditionReason = "AppScaledObjectTerminated"
+	AppScaledObjectTerminationError HTTPScaledObjectConditionReason = "AppScaledObjectTerminationError"
+	PendingCreation                 HTTPScaledObjectConditionReason = "PendingCreation"
+	HTTPScaledObjectIsReady         HTTPScaledObjectConditionReason = "HTTPScaledObjectIsReady"
+)
+
+// HTTPScaledObjectCondition stores the condition state
 type HTTPScaledObjectCondition struct {
 	// Timestamp of the condition
 	// +optional
@@ -68,74 +107,34 @@ type HTTPScaledObjectCondition struct {
 	// Status of the condition, one of True, False, Unknown.
 	// +required
 	Status metav1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"`
-	// The reason for the condition's last transition.
+	// Reason for the condition's last transition.
 	// +optional
 	Reason HTTPScaledObjectConditionReason `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"`
-	// A human readable message indicating details about the transition.
+	// Message indicating details about the transition.
 	// +optional
 	Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"`
 }
 
-// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
-// Important: Run "make" to regenerate code after modifying this file
-
-type ReplicaStruct struct {
-	// Minimum amount of replicas to have in the deployment (Default 0)
-	Min *int32 `json:"min,omitempty" description:"Minimum amount of replicas to have in the deployment (Default 0)"`
-	// Maximum amount of replicas to have in the deployment (Default 100)
-	Max *int32 `json:"max,omitempty" description:"Maximum amount of replicas to have in the deployment (Default 100)"`
-}
-
-// HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
-type HTTPScaledObjectSpec struct {
-	// The host to route. All requests with this host in the "Host"
-	// header will be routed to the Service and Port specified
-	// in the scaleTargetRef
-	Host string `json:"host"`
-	// The name of the deployment to route HTTP requests to (and to autoscale). Either this
-	// or Image must be set
-	ScaleTargetRef *ScaleTargetRef `json:"scaleTargetRef"`
-	// (optional) Replica information
-	// +optional
-	Replicas *ReplicaStruct `json:"replicas,omitempty"`
-	// (optional) Target metric value
-	// +optional
-	TargetPendingRequests *int32 `json:"targetPendingRequests,omitempty" description:"The target metric value for the HPA (Default 100)"`
-	// (optional) Cooldown period value
-	// +optional
-	CooldownPeriod *int32 `json:"scaledownPeriod,omitempty" description:"Cooldown period (seconds) for resources to scale down (Default 300)"`
-}
-
-// ScaleTargetRef contains all the details about an HTTP application to scale and route to
-type ScaleTargetRef struct {
-	// The name of the deployment to scale according to HTTP traffic
-	Deployment string `json:"deployment"`
-	// The name of the service to route to
-	Service string `json:"service"`
-	// The port to route to
-	Port int32 `json:"port"`
-}
-
 // HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
 type HTTPScaledObjectStatus struct {
-	// List of auditable conditions of the operator
+	// Conditions of the operator
 	Conditions []HTTPScaledObjectCondition `json:"conditions,omitempty" description:"List of auditable conditions of the operator"`
 }
 
-// +kubebuilder:object:root=true
-
-// HTTPScaledObject is the Schema for the scaledobjects API
-// +k8s:openapi-gen=true
-// +kubebuilder:subresource:status
-// +kubebuilder:resource:path=httpscaledobjects,scope=Namespaced,shortName=httpso
-// +kubebuilder:printcolumn:name="ScaleTargetDeploymentName",type="string",JSONPath=".spec.scaleTargetRef.deploymentName"
-// +kubebuilder:printcolumn:name="ScaleTargetServiceName",type="string",JSONPath=".spec.scaleTargetRef"
-// +kubebuilder:printcolumn:name="ScaleTargetPort",type="integer",JSONPath=".spec.scaleTargetRef"
-// +kubebuilder:printcolumn:name="MinReplicas",type="integer",JSONPath=".spec.replicas.min"
-// +kubebuilder:printcolumn:name="MaxReplicas",type="integer",JSONPath=".spec.replicas.max"
-// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
-// +kubebuilder:printcolumn:name="Active",type="string",JSONPath=".status.conditions[?(@.type==\"HTTPScaledObjectIsReady\")].status"
-
+//+genclient
+//+k8s:openapi-gen=true
+//+kubebuilder:object:root=true
+//+kubebuilder:printcolumn:name="ScaleTargetDeploymentName",type="string",JSONPath=".spec.scaleTargetRef.deploymentName"
+//+kubebuilder:printcolumn:name="ScaleTargetServiceName",type="string",JSONPath=".spec.scaleTargetRef"
+//+kubebuilder:printcolumn:name="ScaleTargetPort",type="integer",JSONPath=".spec.scaleTargetRef"
+//+kubebuilder:printcolumn:name="MinReplicas",type="integer",JSONPath=".spec.replicas.min"
+//+kubebuilder:printcolumn:name="MaxReplicas",type="integer",JSONPath=".spec.replicas.max"
+//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
+//+kubebuilder:printcolumn:name="Active",type="string",JSONPath=".status.conditions[?(@.type==\"HTTPScaledObjectIsReady\")].status"
+//+kubebuilder:resource:shortName=httpso
+//+kubebuilder:subresource:status
+
+// HTTPScaledObject is the Schema for the httpscaledobjects API
 type HTTPScaledObject struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -144,7 +143,7 @@ type HTTPScaledObject struct {
 	Status HTTPScaledObjectStatus `json:"status,omitempty"`
 }
 
-// +kubebuilder:object:root=true
+//+kubebuilder:object:root=true
 
 // HTTPScaledObjectList contains a list of HTTPScaledObject
 type HTTPScaledObjectList struct {
diff --git a/operator/api/v1alpha1/zz_generated.deepcopy.go b/operator/apis/http/v1alpha1/zz_generated.deepcopy.go
similarity index 86%
rename from operator/api/v1alpha1/zz_generated.deepcopy.go
rename to operator/apis/http/v1alpha1/zz_generated.deepcopy.go
index e506432..3dcff89 100644
--- a/operator/api/v1alpha1/zz_generated.deepcopy.go
+++ b/operator/apis/http/v1alpha1/zz_generated.deepcopy.go
@@ -2,7 +2,7 @@
 // +build !ignore_autogenerated
 
 /*
-
+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.
@@ -44,8 +44,12 @@ func (in *HTTPScaledObject) DeepCopy() *HTTPScaledObject {
 	return out
 }
 
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
 func (in *HTTPScaledObject) DeepCopyObject() runtime.Object {
-	return in.DeepCopy()
+	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.
@@ -103,7 +107,21 @@ func (in *HTTPScaledObjectSpec) DeepCopyInto(out *HTTPScaledObjectSpec) {
 		*out = new(ScaleTargetRef)
 		**out = **in
 	}
-	out.Replicas = in.Replicas
+	if in.Replicas != nil {
+		in, out := &in.Replicas, &out.Replicas
+		*out = new(ReplicaStruct)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.TargetPendingRequests != nil {
+		in, out := &in.TargetPendingRequests, &out.TargetPendingRequests
+		*out = new(int32)
+		**out = **in
+	}
+	if in.CooldownPeriod != nil {
+		in, out := &in.CooldownPeriod, &out.CooldownPeriod
+		*out = new(int32)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPScaledObjectSpec.
@@ -139,6 +157,16 @@ func (in *HTTPScaledObjectStatus) DeepCopy() *HTTPScaledObjectStatus {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ReplicaStruct) DeepCopyInto(out *ReplicaStruct) {
 	*out = *in
+	if in.Min != nil {
+		in, out := &in.Min, &out.Min
+		*out = new(int32)
+		**out = **in
+	}
+	if in.Max != nil {
+		in, out := &in.Max, &out.Max
+		*out = new(int32)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplicaStruct.
diff --git a/operator/controllers/app.go b/operator/controllers/http/app.go
similarity index 76%
rename from operator/controllers/app.go
rename to operator/controllers/http/app.go
index c706896..ef1a0f1 100644
--- a/operator/controllers/app.go
+++ b/operator/controllers/http/app.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
@@ -10,8 +10,8 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	"github.com/kedacore/http-add-on/operator/api/v1alpha1"
-	"github.com/kedacore/http-add-on/operator/controllers/config"
+	"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/pkg/routing"
 )
 
@@ -23,13 +23,19 @@ func removeApplicationResources(
 	baseConfig config.Base,
 	httpso *v1alpha1.HTTPScaledObject,
 ) error {
-	defer httpso.SaveStatus(context.Background(), logger, cl)
+	defer SaveStatus(context.Background(), logger, cl, httpso)
 	// Set initial statuses
-	httpso.AddCondition(*v1alpha1.CreateCondition(
-		v1alpha1.Terminating,
-		v1.ConditionUnknown,
-		v1alpha1.TerminatingResources,
-	).SetMessage("Received termination signal"))
+	AddCondition(
+		httpso,
+		*SetMessage(
+			CreateCondition(
+				v1alpha1.Terminating,
+				v1.ConditionUnknown,
+				v1alpha1.TerminatingResources,
+			),
+			"Received termination signal",
+		),
+	)
 
 	logger = logger.WithValues(
 		"reconciler.appObjects",
@@ -54,15 +60,21 @@ func removeApplicationResources(
 			logger.Info("App ScaledObject not found, moving on")
 		} else {
 			logger.Error(err, "Deleting scaledobject")
-			httpso.AddCondition(*v1alpha1.CreateCondition(
-				v1alpha1.Error,
-				v1.ConditionFalse,
-				v1alpha1.AppScaledObjectTerminationError,
-			).SetMessage(err.Error()))
+			AddCondition(
+				httpso,
+				*SetMessage(
+					CreateCondition(
+						v1alpha1.Error,
+						v1.ConditionFalse,
+						v1alpha1.AppScaledObjectTerminationError,
+					),
+					err.Error(),
+				),
+			)
 			return err
 		}
 	}
-	httpso.AddCondition(*v1alpha1.CreateCondition(
+	AddCondition(httpso, *CreateCondition(
 		v1alpha1.Terminated,
 		v1.ConditionTrue,
 		v1alpha1.AppScaledObjectTerminated,
@@ -91,7 +103,7 @@ func createOrUpdateApplicationResources(
 	externalScalerConfig config.ExternalScaler,
 	httpso *v1alpha1.HTTPScaledObject,
 ) error {
-	defer httpso.SaveStatus(context.Background(), logger, cl)
+	defer SaveStatus(context.Background(), logger, cl, httpso)
 	logger = logger.WithValues(
 		"reconciler.appObjects",
 		"addObjects",
@@ -102,11 +114,16 @@ func createOrUpdateApplicationResources(
 	)
 
 	// set initial statuses
-	httpso.AddCondition(*v1alpha1.CreateCondition(
-		v1alpha1.Pending,
-		v1.ConditionUnknown,
-		v1alpha1.PendingCreation,
-	).SetMessage("Identified HTTPScaledObject creation signal"))
+	AddCondition(
+		httpso,
+		*SetMessage(
+			CreateCondition(
+				v1alpha1.Pending,
+				v1.ConditionUnknown,
+				v1alpha1.PendingCreation,
+			),
+			"Identified HTTPScaledObject creation signal"),
+	)
 
 	// create the KEDA core ScaledObjects (not the HTTP one) for
 	// the app deployment and the interceptor deployment.
diff --git a/operator/api/v1alpha1/condition_provider.go b/operator/controllers/http/condition_provider.go
similarity index 65%
rename from operator/api/v1alpha1/condition_provider.go
rename to operator/controllers/http/condition_provider.go
index 2d51136..5b1fd92 100644
--- a/operator/api/v1alpha1/condition_provider.go
+++ b/operator/controllers/http/condition_provider.go
@@ -1,4 +1,4 @@
-package v1alpha1
+package http
 
 import (
 	"context"
@@ -7,14 +7,17 @@ import (
 	"github.com/go-logr/logr"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 )
 
 // SaveStatus will trigger an object update to save the current status
 // conditions
-func (httpso *HTTPScaledObject) SaveStatus(
+func SaveStatus(
 	ctx context.Context,
 	logger logr.Logger,
 	cl client.Client,
+	httpso *httpv1alpha1.HTTPScaledObject,
 ) {
 	logger.Info("Updating status on HTTPScaledObject", "resource version", httpso.ResourceVersion)
 
@@ -27,18 +30,18 @@ func (httpso *HTTPScaledObject) SaveStatus(
 }
 
 // AddCondition adds a new condition to the HTTPScaledObject
-func (httpso *HTTPScaledObject) AddCondition(condition HTTPScaledObjectCondition) *HTTPScaledObject {
+func AddCondition(httpso *httpv1alpha1.HTTPScaledObject, condition httpv1alpha1.HTTPScaledObjectCondition) *httpv1alpha1.HTTPScaledObject {
 	httpso.Status.Conditions = append(httpso.Status.Conditions, condition)
 	return httpso
 }
 
 // CreateCondition initializes a new status condition
 func CreateCondition(
-	condType HTTPScaledObjectCreationStatus,
+	condType httpv1alpha1.HTTPScaledObjectCreationStatus,
 	status metav1.ConditionStatus,
-	reason HTTPScaledObjectConditionReason,
-) *HTTPScaledObjectCondition {
-	cond := HTTPScaledObjectCondition{
+	reason httpv1alpha1.HTTPScaledObjectConditionReason,
+) *httpv1alpha1.HTTPScaledObjectCondition {
+	cond := httpv1alpha1.HTTPScaledObjectCondition{
 		Timestamp: time.Now().Format(time.RFC3339),
 		Type:      condType,
 		Status:    status,
@@ -48,7 +51,7 @@ func CreateCondition(
 }
 
 // SetMessage sets the optional reason for the condition
-func (c *HTTPScaledObjectCondition) SetMessage(message string) *HTTPScaledObjectCondition {
+func SetMessage(c *httpv1alpha1.HTTPScaledObjectCondition, message string) *httpv1alpha1.HTTPScaledObjectCondition {
 	c.Message = message
 	return c
 }
diff --git a/operator/controllers/config/app_info.go b/operator/controllers/http/config/app_info.go
similarity index 82%
rename from operator/controllers/config/app_info.go
rename to operator/controllers/http/config/app_info.go
index e13be90..3d4eff3 100644
--- a/operator/controllers/config/app_info.go
+++ b/operator/controllers/http/config/app_info.go
@@ -3,7 +3,7 @@ package config
 import (
 	"fmt"
 
-	"github.com/kedacore/http-add-on/operator/api/v1alpha1"
+	"github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 )
 
 // AppScaledObjectName returns the name of the ScaledObject
diff --git a/operator/controllers/config/config.go b/operator/controllers/http/config/config.go
similarity index 100%
rename from operator/controllers/config/config.go
rename to operator/controllers/http/config/config.go
diff --git a/operator/controllers/config/config_test.go b/operator/controllers/http/config/config_test.go
similarity index 100%
rename from operator/controllers/config/config_test.go
rename to operator/controllers/http/config/config_test.go
diff --git a/operator/controllers/finalizer.go b/operator/controllers/http/finalizer.go
similarity index 95%
rename from operator/controllers/finalizer.go
rename to operator/controllers/http/finalizer.go
index ec5679f..403cc3e 100644
--- a/operator/controllers/finalizer.go
+++ b/operator/controllers/http/finalizer.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
@@ -6,7 +6,7 @@ import (
 	"github.com/go-logr/logr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	httpv1alpha1 "github.com/kedacore/http-add-on/operator/api/v1alpha1"
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 )
 
 const (
diff --git a/operator/controllers/httpscaledobject_controller.go b/operator/controllers/http/httpscaledobject_controller.go
similarity index 75%
rename from operator/controllers/httpscaledobject_controller.go
rename to operator/controllers/http/httpscaledobject_controller.go
index 55f3181..01381d3 100644
--- a/operator/controllers/httpscaledobject_controller.go
+++ b/operator/controllers/http/httpscaledobject_controller.go
@@ -14,31 +14,34 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package controllers
+package http
 
 import (
 	"context"
 	"time"
 
-	"github.com/go-logr/logr"
 	"k8s.io/apimachinery/pkg/api/errors"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"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/api/v1alpha1"
-	"github.com/kedacore/http-add-on/operator/controllers/config"
+	httpv1alpha1 "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/pkg/routing"
 )
 
 // HTTPScaledObjectReconciler reconciles a HTTPScaledObject object
+//
+//revive:disable-next-line:exported
+//goland:noinspection GoNameStartsWithPackageName
 type HTTPScaledObjectReconciler struct {
 	client.Client
-	Log                  logr.Logger
-	Scheme               *runtime.Scheme
+	Scheme *runtime.Scheme
+
 	InterceptorConfig    config.Interceptor
 	ExternalScalerConfig config.ExternalScaler
 	BaseConfig           config.Base
@@ -52,14 +55,12 @@ type HTTPScaledObjectReconciler struct {
 
 // Reconcile reconciles a newly created, deleted, or otherwise changed
 // HTTPScaledObject
-func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
-	logger := rec.Log.WithValues("HTTPScaledObject.Namespace", req.Namespace, "HTTPScaledObject.Name", req.Name)
+func (r *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	logger := log.FromContext(ctx, "httpscaledobject", req.NamespacedName)
 	logger.Info("Reconciliation start")
 
-	_ = rec.Log.WithValues("httpscaledobject", req.NamespacedName)
 	httpso := &httpv1alpha1.HTTPScaledObject{}
-
-	if err := rec.Client.Get(ctx, req.NamespacedName, httpso); err != nil {
+	if err := r.Client.Get(ctx, req.NamespacedName, httpso); err != nil {
 		if errors.IsNotFound(err) {
 			// If the HTTPScaledObject wasn't found, it might have
 			// been deleted between the reconcile and the get.
@@ -85,9 +86,9 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
 		removeErr := removeApplicationResources(
 			ctx,
 			logger,
-			rec.Client,
-			rec.RoutingTable,
-			rec.BaseConfig,
+			r.Client,
+			r.RoutingTable,
+			r.BaseConfig,
 			httpso,
 		)
 		if removeErr != nil {
@@ -99,11 +100,11 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
 			}, removeErr
 		}
 		// after we've deleted app objects, we can finalize
-		return ctrl.Result{}, finalizeScaledObject(ctx, logger, rec.Client, httpso)
+		return ctrl.Result{}, finalizeScaledObject(ctx, logger, r.Client, httpso)
 	}
 
 	// ensure finalizer is set on this resource
-	if err := ensureFinalizer(ctx, logger, rec.Client, httpso); err != nil {
+	if err := ensureFinalizer(ctx, logger, r.Client, httpso); err != nil {
 		return ctrl.Result{}, err
 	}
 
@@ -120,10 +121,10 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
 	if err := createOrUpdateApplicationResources(
 		ctx,
 		logger,
-		rec.Client,
-		rec.RoutingTable,
-		rec.BaseConfig,
-		rec.ExternalScalerConfig,
+		r.Client,
+		r.RoutingTable,
+		r.BaseConfig,
+		r.ExternalScalerConfig,
 		httpso,
 	); err != nil {
 		// if we failed to create app resources, remove what we've created and exit
@@ -131,9 +132,9 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
 		if removeErr := removeApplicationResources(
 			ctx,
 			logger,
-			rec.Client,
-			rec.RoutingTable,
-			rec.BaseConfig,
+			r.Client,
+			r.RoutingTable,
+			r.BaseConfig,
 			httpso,
 		); removeErr != nil {
 			logger.Error(removeErr, "Removing previously created resources")
@@ -141,17 +142,32 @@ func (rec *HTTPScaledObjectReconciler) Reconcile(ctx context.Context, req ctrl.R
 
 		return ctrl.Result{}, err
 	}
-	httpso.AddCondition(*httpv1alpha1.CreateCondition(httpv1alpha1.Ready, v1.ConditionTrue, httpv1alpha1.HTTPScaledObjectIsReady).SetMessage("Finished object creation")).
-		SaveStatus(ctx, logger, rec.Client)
+
+	SaveStatus(
+		ctx,
+		logger,
+		r.Client,
+		AddCondition(
+			httpso,
+			*SetMessage(
+				CreateCondition(
+					httpv1alpha1.Ready,
+					v1.ConditionTrue,
+					httpv1alpha1.HTTPScaledObjectIsReady,
+				),
+				"Finished object creation",
+			),
+		),
+	)
 
 	// success reconciling
 	logger.Info("Reconcile success")
 	return ctrl.Result{}, nil
 }
 
-// SetupWithManager starts up reconciliation with the given manager
-func (rec *HTTPScaledObjectReconciler) SetupWithManager(mgr ctrl.Manager) error {
+// 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(predicate.GenerationChangedPredicate{})).
-		Complete(rec)
+		Complete(r)
 }
diff --git a/operator/controllers/ping.go b/operator/controllers/http/ping.go
similarity index 97%
rename from operator/controllers/ping.go
rename to operator/controllers/http/ping.go
index a5487bb..f2c4ce5 100644
--- a/operator/controllers/ping.go
+++ b/operator/controllers/http/ping.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
diff --git a/operator/controllers/routing_table.go b/operator/controllers/http/routing_table.go
similarity index 99%
rename from operator/controllers/routing_table.go
rename to operator/controllers/http/routing_table.go
index 6d3e6a4..e50c168 100644
--- a/operator/controllers/routing_table.go
+++ b/operator/controllers/http/routing_table.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
diff --git a/operator/controllers/scaled_object.go b/operator/controllers/http/scaled_object.go
similarity index 81%
rename from operator/controllers/scaled_object.go
rename to operator/controllers/http/scaled_object.go
index f2fdf20..afa9aed 100644
--- a/operator/controllers/scaled_object.go
+++ b/operator/controllers/http/scaled_object.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
@@ -10,7 +10,7 @@ import (
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	"github.com/kedacore/http-add-on/operator/api/v1alpha1"
+	"github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 	"github.com/kedacore/http-add-on/pkg/k8s"
 )
 
@@ -68,21 +68,34 @@ func createOrUpdateScaledObject(
 				return err
 			}
 		} else {
+			AddCondition(
+				httpso,
+				*SetMessage(
+					CreateCondition(
+						v1alpha1.Error,
+						v1.ConditionFalse,
+						v1alpha1.ErrorCreatingAppScaledObject,
+					),
+					err.Error(),
+				),
+			)
+
 			logger.Error(err, "Creating ScaledObject")
-			httpso.AddCondition(*v1alpha1.CreateCondition(
-				v1alpha1.Error,
-				v1.ConditionFalse,
-				v1alpha1.ErrorCreatingAppScaledObject,
-			).SetMessage(err.Error()))
 			return err
 		}
 	}
 
-	httpso.AddCondition(*v1alpha1.CreateCondition(
-		v1alpha1.Created,
-		v1.ConditionTrue,
-		v1alpha1.AppScaledObjectCreated,
-	).SetMessage("App ScaledObject created"))
+	AddCondition(
+		httpso,
+		*SetMessage(
+			CreateCondition(
+				v1alpha1.Created,
+				v1.ConditionTrue,
+				v1alpha1.AppScaledObjectCreated,
+			),
+			"App ScaledObject created",
+		),
+	)
 
 	return nil
 }
diff --git a/operator/generated/clientset/versioned/clientset.go b/operator/generated/clientset/versioned/clientset.go
new file mode 100644
index 0000000..ef03bea
--- /dev/null
+++ b/operator/generated/clientset/versioned/clientset.go
@@ -0,0 +1,120 @@
+/*
+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 versioned
+
+import (
+	"fmt"
+	"net/http"
+
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/typed/http/v1alpha1"
+	discovery "k8s.io/client-go/discovery"
+	rest "k8s.io/client-go/rest"
+	flowcontrol "k8s.io/client-go/util/flowcontrol"
+)
+
+type Interface interface {
+	Discovery() discovery.DiscoveryInterface
+	HttpV1alpha1() httpv1alpha1.HttpV1alpha1Interface
+}
+
+// Clientset contains the clients for groups.
+type Clientset struct {
+	*discovery.DiscoveryClient
+	httpV1alpha1 *httpv1alpha1.HttpV1alpha1Client
+}
+
+// HttpV1alpha1 retrieves the HttpV1alpha1Client
+func (c *Clientset) HttpV1alpha1() httpv1alpha1.HttpV1alpha1Interface {
+	return c.httpV1alpha1
+}
+
+// Discovery retrieves the DiscoveryClient
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+	if c == nil {
+		return nil
+	}
+	return c.DiscoveryClient
+}
+
+// NewForConfig creates a new Clientset for the given config.
+// If config's RateLimiter is not set and QPS and Burst are acceptable,
+// NewForConfig will generate a rate-limiter in configShallowCopy.
+// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
+// where httpClient was generated with rest.HTTPClientFor(c).
+func NewForConfig(c *rest.Config) (*Clientset, error) {
+	configShallowCopy := *c
+
+	if configShallowCopy.UserAgent == "" {
+		configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
+	}
+
+	// share the transport between all clients
+	httpClient, err := rest.HTTPClientFor(&configShallowCopy)
+	if err != nil {
+		return nil, err
+	}
+
+	return NewForConfigAndClient(&configShallowCopy, httpClient)
+}
+
+// NewForConfigAndClient creates a new Clientset for the given config and http client.
+// Note the http client provided takes precedence over the configured transport values.
+// If config's RateLimiter is not set and QPS and Burst are acceptable,
+// NewForConfigAndClient will generate a rate-limiter in configShallowCopy.
+func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {
+	configShallowCopy := *c
+	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
+		if configShallowCopy.Burst <= 0 {
+			return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
+		}
+		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
+	}
+
+	var cs Clientset
+	var err error
+	cs.httpV1alpha1, err = httpv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)
+	if err != nil {
+		return nil, err
+	}
+
+	cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)
+	if err != nil {
+		return nil, err
+	}
+	return &cs, nil
+}
+
+// NewForConfigOrDie creates a new Clientset for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *Clientset {
+	cs, err := NewForConfig(c)
+	if err != nil {
+		panic(err)
+	}
+	return cs
+}
+
+// New creates a new Clientset for the given RESTClient.
+func New(c rest.Interface) *Clientset {
+	var cs Clientset
+	cs.httpV1alpha1 = httpv1alpha1.New(c)
+
+	cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
+	return &cs
+}
diff --git a/operator/generated/clientset/versioned/doc.go b/operator/generated/clientset/versioned/doc.go
new file mode 100644
index 0000000..d84fae1
--- /dev/null
+++ b/operator/generated/clientset/versioned/doc.go
@@ -0,0 +1,20 @@
+/*
+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.
+
+// This package has the automatically generated clientset.
+package versioned
diff --git a/operator/generated/clientset/versioned/fake/clientset_generated.go b/operator/generated/clientset/versioned/fake/clientset_generated.go
new file mode 100644
index 0000000..34d850e
--- /dev/null
+++ b/operator/generated/clientset/versioned/fake/clientset_generated.go
@@ -0,0 +1,85 @@
+/*
+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 (
+	clientset "github.com/kedacore/http-add-on/operator/generated/clientset/versioned"
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/typed/http/v1alpha1"
+	fakehttpv1alpha1 "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/typed/http/v1alpha1/fake"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/watch"
+	"k8s.io/client-go/discovery"
+	fakediscovery "k8s.io/client-go/discovery/fake"
+	"k8s.io/client-go/testing"
+)
+
+// NewSimpleClientset returns a clientset that will respond with the provided objects.
+// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
+// without applying any validations and/or defaults. It shouldn't be considered a replacement
+// for a real clientset and is mostly useful in simple unit tests.
+func NewSimpleClientset(objects ...runtime.Object) *Clientset {
+	o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
+	for _, obj := range objects {
+		if err := o.Add(obj); err != nil {
+			panic(err)
+		}
+	}
+
+	cs := &Clientset{tracker: o}
+	cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
+	cs.AddReactor("*", "*", testing.ObjectReaction(o))
+	cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
+		gvr := action.GetResource()
+		ns := action.GetNamespace()
+		watch, err := o.Watch(gvr, ns)
+		if err != nil {
+			return false, nil, err
+		}
+		return true, watch, nil
+	})
+
+	return cs
+}
+
+// Clientset implements clientset.Interface. Meant to be embedded into a
+// struct to get a default implementation. This makes faking out just the method
+// you want to test easier.
+type Clientset struct {
+	testing.Fake
+	discovery *fakediscovery.FakeDiscovery
+	tracker   testing.ObjectTracker
+}
+
+func (c *Clientset) Discovery() discovery.DiscoveryInterface {
+	return c.discovery
+}
+
+func (c *Clientset) Tracker() testing.ObjectTracker {
+	return c.tracker
+}
+
+var (
+	_ clientset.Interface = &Clientset{}
+	_ testing.FakeClient  = &Clientset{}
+)
+
+// HttpV1alpha1 retrieves the HttpV1alpha1Client
+func (c *Clientset) HttpV1alpha1() httpv1alpha1.HttpV1alpha1Interface {
+	return &fakehttpv1alpha1.FakeHttpV1alpha1{Fake: &c.Fake}
+}
diff --git a/operator/generated/clientset/versioned/fake/doc.go b/operator/generated/clientset/versioned/fake/doc.go
new file mode 100644
index 0000000..08d2a1e
--- /dev/null
+++ b/operator/generated/clientset/versioned/fake/doc.go
@@ -0,0 +1,20 @@
+/*
+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.
+
+// This package has the automatically generated fake clientset.
+package fake
diff --git a/operator/generated/clientset/versioned/fake/register.go b/operator/generated/clientset/versioned/fake/register.go
new file mode 100644
index 0000000..9a88cc9
--- /dev/null
+++ b/operator/generated/clientset/versioned/fake/register.go
@@ -0,0 +1,56 @@
+/*
+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 (
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var scheme = runtime.NewScheme()
+var codecs = serializer.NewCodecFactory(scheme)
+
+var localSchemeBuilder = runtime.SchemeBuilder{
+	httpv1alpha1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+//	import (
+//	  "k8s.io/client-go/kubernetes"
+//	  clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+//	  aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+//	)
+//
+//	kclientset, _ := kubernetes.NewForConfig(c)
+//	_ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+	v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
+	utilruntime.Must(AddToScheme(scheme))
+}
diff --git a/operator/generated/clientset/versioned/scheme/doc.go b/operator/generated/clientset/versioned/scheme/doc.go
new file mode 100644
index 0000000..a4d1d33
--- /dev/null
+++ b/operator/generated/clientset/versioned/scheme/doc.go
@@ -0,0 +1,20 @@
+/*
+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.
+
+// This package contains the scheme of the automatically generated clientset.
+package scheme
diff --git a/operator/generated/clientset/versioned/scheme/register.go b/operator/generated/clientset/versioned/scheme/register.go
new file mode 100644
index 0000000..813fdf9
--- /dev/null
+++ b/operator/generated/clientset/versioned/scheme/register.go
@@ -0,0 +1,56 @@
+/*
+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 scheme
+
+import (
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+var Scheme = runtime.NewScheme()
+var Codecs = serializer.NewCodecFactory(Scheme)
+var ParameterCodec = runtime.NewParameterCodec(Scheme)
+var localSchemeBuilder = runtime.SchemeBuilder{
+	httpv1alpha1.AddToScheme,
+}
+
+// AddToScheme adds all types of this clientset into the given scheme. This allows composition
+// of clientsets, like in:
+//
+//	import (
+//	  "k8s.io/client-go/kubernetes"
+//	  clientsetscheme "k8s.io/client-go/kubernetes/scheme"
+//	  aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
+//	)
+//
+//	kclientset, _ := kubernetes.NewForConfig(c)
+//	_ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
+//
+// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
+// correctly.
+var AddToScheme = localSchemeBuilder.AddToScheme
+
+func init() {
+	v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
+	utilruntime.Must(AddToScheme(Scheme))
+}
diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/doc.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/doc.go
new file mode 100644
index 0000000..64e019f
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/doc.go
@@ -0,0 +1,20 @@
+/*
+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.
+
+// This package has the automatically generated typed clients.
+package v1alpha1
diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/doc.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/doc.go
new file mode 100644
index 0000000..7ce8052
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/doc.go
@@ -0,0 +1,20 @@
+/*
+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 has the automatically generated clients.
+package fake
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
new file mode 100644
index 0000000..ec48f0a
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_http_client.go
@@ -0,0 +1,40 @@
+/*
+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 (
+	v1alpha1 "github.com/kedacore/http-add-on/operator/generated/clientset/versioned/typed/http/v1alpha1"
+	rest "k8s.io/client-go/rest"
+	testing "k8s.io/client-go/testing"
+)
+
+type FakeHttpV1alpha1 struct {
+	*testing.Fake
+}
+
+func (c *FakeHttpV1alpha1) HTTPScaledObjects(namespace string) v1alpha1.HTTPScaledObjectInterface {
+	return &FakeHTTPScaledObjects{c, namespace}
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *FakeHttpV1alpha1) RESTClient() rest.Interface {
+	var ret *rest.RESTClient
+	return ret
+}
diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscaledobject.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscaledobject.go
new file mode 100644
index 0000000..f81f070
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/fake/fake_httpscaledobject.go
@@ -0,0 +1,142 @@
+/*
+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"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	types "k8s.io/apimachinery/pkg/types"
+	watch "k8s.io/apimachinery/pkg/watch"
+	testing "k8s.io/client-go/testing"
+)
+
+// FakeHTTPScaledObjects implements HTTPScaledObjectInterface
+type FakeHTTPScaledObjects struct {
+	Fake *FakeHttpV1alpha1
+	ns   string
+}
+
+var httpscaledobjectsResource = schema.GroupVersionResource{Group: "http", Version: "v1alpha1", Resource: "httpscaledobjects"}
+
+var httpscaledobjectsKind = schema.GroupVersionKind{Group: "http", Version: "v1alpha1", Kind: "HTTPScaledObject"}
+
+// Get takes name of the hTTPScaledObject, and returns the corresponding hTTPScaledObject object, and an error if there is any.
+func (c *FakeHTTPScaledObjects) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewGetAction(httpscaledobjectsResource, c.ns, name), &v1alpha1.HTTPScaledObject{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.HTTPScaledObject), err
+}
+
+// List takes label and field selectors, and returns the list of HTTPScaledObjects that match those selectors.
+func (c *FakeHTTPScaledObjects) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScaledObjectList, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewListAction(httpscaledobjectsResource, httpscaledobjectsKind, c.ns, opts), &v1alpha1.HTTPScaledObjectList{})
+
+	if obj == nil {
+		return nil, err
+	}
+
+	label, _, _ := testing.ExtractFromListOptions(opts)
+	if label == nil {
+		label = labels.Everything()
+	}
+	list := &v1alpha1.HTTPScaledObjectList{ListMeta: obj.(*v1alpha1.HTTPScaledObjectList).ListMeta}
+	for _, item := range obj.(*v1alpha1.HTTPScaledObjectList).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 hTTPScaledObjects.
+func (c *FakeHTTPScaledObjects) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
+	return c.Fake.
+		InvokesWatch(testing.NewWatchAction(httpscaledobjectsResource, c.ns, opts))
+
+}
+
+// Create takes the representation of a hTTPScaledObject and creates it.  Returns the server's representation of the hTTPScaledObject, and an error, if there is any.
+func (c *FakeHTTPScaledObjects) Create(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.CreateOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewCreateAction(httpscaledobjectsResource, c.ns, hTTPScaledObject), &v1alpha1.HTTPScaledObject{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.HTTPScaledObject), err
+}
+
+// Update takes the representation of a hTTPScaledObject and updates it. Returns the server's representation of the hTTPScaledObject, and an error, if there is any.
+func (c *FakeHTTPScaledObjects) Update(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateAction(httpscaledobjectsResource, c.ns, hTTPScaledObject), &v1alpha1.HTTPScaledObject{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.HTTPScaledObject), 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 *FakeHTTPScaledObjects) UpdateStatus(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (*v1alpha1.HTTPScaledObject, error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewUpdateSubresourceAction(httpscaledobjectsResource, "status", c.ns, hTTPScaledObject), &v1alpha1.HTTPScaledObject{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.HTTPScaledObject), err
+}
+
+// Delete takes name of the hTTPScaledObject and deletes it. Returns an error if one occurs.
+func (c *FakeHTTPScaledObjects) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	_, err := c.Fake.
+		Invokes(testing.NewDeleteActionWithOptions(httpscaledobjectsResource, c.ns, name, opts), &v1alpha1.HTTPScaledObject{})
+
+	return err
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *FakeHTTPScaledObjects) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
+	action := testing.NewDeleteCollectionAction(httpscaledobjectsResource, c.ns, listOpts)
+
+	_, err := c.Fake.Invokes(action, &v1alpha1.HTTPScaledObjectList{})
+	return err
+}
+
+// Patch applies the patch and returns the patched hTTPScaledObject.
+func (c *FakeHTTPScaledObjects) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScaledObject, err error) {
+	obj, err := c.Fake.
+		Invokes(testing.NewPatchSubresourceAction(httpscaledobjectsResource, c.ns, name, pt, data, subresources...), &v1alpha1.HTTPScaledObject{})
+
+	if obj == nil {
+		return nil, err
+	}
+	return obj.(*v1alpha1.HTTPScaledObject), 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
new file mode 100644
index 0000000..cfff474
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/generated_expansion.go
@@ -0,0 +1,21 @@
+/*
+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
+
+type HTTPScaledObjectExpansion 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
new file mode 100644
index 0000000..6a3766e
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/http_client.go
@@ -0,0 +1,107 @@
+/*
+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 (
+	"net/http"
+
+	v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
+	"github.com/kedacore/http-add-on/operator/generated/clientset/versioned/scheme"
+	rest "k8s.io/client-go/rest"
+)
+
+type HttpV1alpha1Interface interface {
+	RESTClient() rest.Interface
+	HTTPScaledObjectsGetter
+}
+
+// HttpV1alpha1Client is used to interact with features provided by the http group.
+type HttpV1alpha1Client struct {
+	restClient rest.Interface
+}
+
+func (c *HttpV1alpha1Client) HTTPScaledObjects(namespace string) HTTPScaledObjectInterface {
+	return newHTTPScaledObjects(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).
+func NewForConfig(c *rest.Config) (*HttpV1alpha1Client, error) {
+	config := *c
+	if err := setConfigDefaults(&config); err != nil {
+		return nil, err
+	}
+	httpClient, err := rest.HTTPClientFor(&config)
+	if err != nil {
+		return nil, err
+	}
+	return NewForConfigAndClient(&config, httpClient)
+}
+
+// NewForConfigAndClient creates a new HttpV1alpha1Client for the given config and http client.
+// Note the http client provided takes precedence over the configured transport values.
+func NewForConfigAndClient(c *rest.Config, h *http.Client) (*HttpV1alpha1Client, error) {
+	config := *c
+	if err := setConfigDefaults(&config); err != nil {
+		return nil, err
+	}
+	client, err := rest.RESTClientForConfigAndClient(&config, h)
+	if err != nil {
+		return nil, err
+	}
+	return &HttpV1alpha1Client{client}, nil
+}
+
+// NewForConfigOrDie creates a new HttpV1alpha1Client for the given config and
+// panics if there is an error in the config.
+func NewForConfigOrDie(c *rest.Config) *HttpV1alpha1Client {
+	client, err := NewForConfig(c)
+	if err != nil {
+		panic(err)
+	}
+	return client
+}
+
+// New creates a new HttpV1alpha1Client for the given RESTClient.
+func New(c rest.Interface) *HttpV1alpha1Client {
+	return &HttpV1alpha1Client{c}
+}
+
+func setConfigDefaults(config *rest.Config) error {
+	gv := v1alpha1.SchemeGroupVersion
+	config.GroupVersion = &gv
+	config.APIPath = "/apis"
+	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
+
+	if config.UserAgent == "" {
+		config.UserAgent = rest.DefaultKubernetesUserAgent()
+	}
+
+	return nil
+}
+
+// RESTClient returns a RESTClient that is used to communicate
+// with API server by this client implementation.
+func (c *HttpV1alpha1Client) RESTClient() rest.Interface {
+	if c == nil {
+		return nil
+	}
+	return c.restClient
+}
diff --git a/operator/generated/clientset/versioned/typed/http/v1alpha1/httpscaledobject.go b/operator/generated/clientset/versioned/typed/http/v1alpha1/httpscaledobject.go
new file mode 100644
index 0000000..e0b1bfc
--- /dev/null
+++ b/operator/generated/clientset/versioned/typed/http/v1alpha1/httpscaledobject.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"
+)
+
+// HTTPScaledObjectsGetter has a method to return a HTTPScaledObjectInterface.
+// A group's client should implement this interface.
+type HTTPScaledObjectsGetter interface {
+	HTTPScaledObjects(namespace string) HTTPScaledObjectInterface
+}
+
+// HTTPScaledObjectInterface has methods to work with HTTPScaledObject resources.
+type HTTPScaledObjectInterface interface {
+	Create(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.CreateOptions) (*v1alpha1.HTTPScaledObject, error)
+	Update(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (*v1alpha1.HTTPScaledObject, error)
+	UpdateStatus(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (*v1alpha1.HTTPScaledObject, 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.HTTPScaledObject, error)
+	List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.HTTPScaledObjectList, 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.HTTPScaledObject, err error)
+	HTTPScaledObjectExpansion
+}
+
+// hTTPScaledObjects implements HTTPScaledObjectInterface
+type hTTPScaledObjects struct {
+	client rest.Interface
+	ns     string
+}
+
+// newHTTPScaledObjects returns a HTTPScaledObjects
+func newHTTPScaledObjects(c *HttpV1alpha1Client, namespace string) *hTTPScaledObjects {
+	return &hTTPScaledObjects{
+		client: c.RESTClient(),
+		ns:     namespace,
+	}
+}
+
+// Get takes name of the hTTPScaledObject, and returns the corresponding hTTPScaledObject object, and an error if there is any.
+func (c *hTTPScaledObjects) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	result = &v1alpha1.HTTPScaledObject{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		Name(name).
+		VersionedParams(&options, scheme.ParameterCodec).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// List takes label and field selectors, and returns the list of HTTPScaledObjects that match those selectors.
+func (c *hTTPScaledObjects) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.HTTPScaledObjectList, err error) {
+	var timeout time.Duration
+	if opts.TimeoutSeconds != nil {
+		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
+	}
+	result = &v1alpha1.HTTPScaledObjectList{}
+	err = c.client.Get().
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Watch returns a watch.Interface that watches the requested hTTPScaledObjects.
+func (c *hTTPScaledObjects) 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("httpscaledobjects").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Watch(ctx)
+}
+
+// Create takes the representation of a hTTPScaledObject and creates it.  Returns the server's representation of the hTTPScaledObject, and an error, if there is any.
+func (c *hTTPScaledObjects) Create(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.CreateOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	result = &v1alpha1.HTTPScaledObject{}
+	err = c.client.Post().
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(hTTPScaledObject).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Update takes the representation of a hTTPScaledObject and updates it. Returns the server's representation of the hTTPScaledObject, and an error, if there is any.
+func (c *hTTPScaledObjects) Update(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	result = &v1alpha1.HTTPScaledObject{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		Name(hTTPScaledObject.Name).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(hTTPScaledObject).
+		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 *hTTPScaledObjects) UpdateStatus(ctx context.Context, hTTPScaledObject *v1alpha1.HTTPScaledObject, opts v1.UpdateOptions) (result *v1alpha1.HTTPScaledObject, err error) {
+	result = &v1alpha1.HTTPScaledObject{}
+	err = c.client.Put().
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		Name(hTTPScaledObject.Name).
+		SubResource("status").
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(hTTPScaledObject).
+		Do(ctx).
+		Into(result)
+	return
+}
+
+// Delete takes name of the hTTPScaledObject and deletes it. Returns an error if one occurs.
+func (c *hTTPScaledObjects) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
+	return c.client.Delete().
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		Name(name).
+		Body(&opts).
+		Do(ctx).
+		Error()
+}
+
+// DeleteCollection deletes a collection of objects.
+func (c *hTTPScaledObjects) 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("httpscaledobjects").
+		VersionedParams(&listOpts, scheme.ParameterCodec).
+		Timeout(timeout).
+		Body(&opts).
+		Do(ctx).
+		Error()
+}
+
+// Patch applies the patch and returns the patched hTTPScaledObject.
+func (c *hTTPScaledObjects) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.HTTPScaledObject, err error) {
+	result = &v1alpha1.HTTPScaledObject{}
+	err = c.client.Patch(pt).
+		Namespace(c.ns).
+		Resource("httpscaledobjects").
+		Name(name).
+		SubResource(subresources...).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Body(data).
+		Do(ctx).
+		Into(result)
+	return
+}
diff --git a/operator/generated/informers/externalversions/factory.go b/operator/generated/informers/externalversions/factory.go
new file mode 100644
index 0000000..fbaa328
--- /dev/null
+++ b/operator/generated/informers/externalversions/factory.go
@@ -0,0 +1,251 @@
+/*
+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 externalversions
+
+import (
+	reflect "reflect"
+	sync "sync"
+	time "time"
+
+	versioned "github.com/kedacore/http-add-on/operator/generated/clientset/versioned"
+	http "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/http"
+	internalinterfaces "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/internalinterfaces"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// SharedInformerOption defines the functional option type for SharedInformerFactory.
+type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
+
+type sharedInformerFactory struct {
+	client           versioned.Interface
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+	lock             sync.Mutex
+	defaultResync    time.Duration
+	customResync     map[reflect.Type]time.Duration
+
+	informers map[reflect.Type]cache.SharedIndexInformer
+	// startedInformers is used for tracking which informers have been started.
+	// This allows Start() to be called multiple times safely.
+	startedInformers map[reflect.Type]bool
+	// wg tracks how many goroutines were started.
+	wg sync.WaitGroup
+	// shuttingDown is true when Shutdown has been called. It may still be running
+	// because it needs to wait for goroutines.
+	shuttingDown bool
+}
+
+// WithCustomResyncConfig sets a custom resync period for the specified informer types.
+func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
+	return func(factory *sharedInformerFactory) *sharedInformerFactory {
+		for k, v := range resyncConfig {
+			factory.customResync[reflect.TypeOf(k)] = v
+		}
+		return factory
+	}
+}
+
+// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
+func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
+	return func(factory *sharedInformerFactory) *sharedInformerFactory {
+		factory.tweakListOptions = tweakListOptions
+		return factory
+	}
+}
+
+// WithNamespace limits the SharedInformerFactory to the specified namespace.
+func WithNamespace(namespace string) SharedInformerOption {
+	return func(factory *sharedInformerFactory) *sharedInformerFactory {
+		factory.namespace = namespace
+		return factory
+	}
+}
+
+// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
+func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
+	return NewSharedInformerFactoryWithOptions(client, defaultResync)
+}
+
+// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
+// Listers obtained via this SharedInformerFactory will be subject to the same filters
+// as specified here.
+// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
+func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
+	return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
+}
+
+// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
+func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
+	factory := &sharedInformerFactory{
+		client:           client,
+		namespace:        v1.NamespaceAll,
+		defaultResync:    defaultResync,
+		informers:        make(map[reflect.Type]cache.SharedIndexInformer),
+		startedInformers: make(map[reflect.Type]bool),
+		customResync:     make(map[reflect.Type]time.Duration),
+	}
+
+	// Apply all options
+	for _, opt := range options {
+		factory = opt(factory)
+	}
+
+	return factory
+}
+
+func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
+	f.lock.Lock()
+	defer f.lock.Unlock()
+
+	if f.shuttingDown {
+		return
+	}
+
+	for informerType, informer := range f.informers {
+		if !f.startedInformers[informerType] {
+			f.wg.Add(1)
+			// We need a new variable in each loop iteration,
+			// otherwise the goroutine would use the loop variable
+			// and that keeps changing.
+			informer := informer
+			go func() {
+				defer f.wg.Done()
+				informer.Run(stopCh)
+			}()
+			f.startedInformers[informerType] = true
+		}
+	}
+}
+
+func (f *sharedInformerFactory) Shutdown() {
+	f.lock.Lock()
+	f.shuttingDown = true
+	f.lock.Unlock()
+
+	// Will return immediately if there is nothing to wait for.
+	f.wg.Wait()
+}
+
+func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
+	informers := func() map[reflect.Type]cache.SharedIndexInformer {
+		f.lock.Lock()
+		defer f.lock.Unlock()
+
+		informers := map[reflect.Type]cache.SharedIndexInformer{}
+		for informerType, informer := range f.informers {
+			if f.startedInformers[informerType] {
+				informers[informerType] = informer
+			}
+		}
+		return informers
+	}()
+
+	res := map[reflect.Type]bool{}
+	for informType, informer := range informers {
+		res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
+	}
+	return res
+}
+
+// InternalInformerFor returns the SharedIndexInformer for obj using an internal
+// client.
+func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
+	f.lock.Lock()
+	defer f.lock.Unlock()
+
+	informerType := reflect.TypeOf(obj)
+	informer, exists := f.informers[informerType]
+	if exists {
+		return informer
+	}
+
+	resyncPeriod, exists := f.customResync[informerType]
+	if !exists {
+		resyncPeriod = f.defaultResync
+	}
+
+	informer = newFunc(f.client, resyncPeriod)
+	f.informers[informerType] = informer
+
+	return informer
+}
+
+// SharedInformerFactory provides shared informers for resources in all known
+// API group versions.
+//
+// It is typically used like this:
+//
+//	ctx, cancel := context.Background()
+//	defer cancel()
+//	factory := NewSharedInformerFactory(client, resyncPeriod)
+//	defer factory.WaitForStop()    // Returns immediately if nothing was started.
+//	genericInformer := factory.ForResource(resource)
+//	typedInformer := factory.SomeAPIGroup().V1().SomeType()
+//	factory.Start(ctx.Done())          // Start processing these informers.
+//	synced := factory.WaitForCacheSync(ctx.Done())
+//	for v, ok := range synced {
+//	    if !ok {
+//	        fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v)
+//	        return
+//	    }
+//	}
+//
+//	// Creating informers can also be created after Start, but then
+//	// Start must be called again:
+//	anotherGenericInformer := factory.ForResource(resource)
+//	factory.Start(ctx.Done())
+type SharedInformerFactory interface {
+	internalinterfaces.SharedInformerFactory
+
+	// Start initializes all requested informers. They are handled in goroutines
+	// which run until the stop channel gets closed.
+	Start(stopCh <-chan struct{})
+
+	// Shutdown marks a factory as shutting down. At that point no new
+	// informers can be started anymore and Start will return without
+	// doing anything.
+	//
+	// In addition, Shutdown blocks until all goroutines have terminated. For that
+	// to happen, the close channel(s) that they were started with must be closed,
+	// either before Shutdown gets called or while it is waiting.
+	//
+	// Shutdown may be called multiple times, even concurrently. All such calls will
+	// block until all goroutines have terminated.
+	Shutdown()
+
+	// WaitForCacheSync blocks until all started informers' caches were synced
+	// or the stop channel gets closed.
+	WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
+
+	// ForResource gives generic access to a shared informer of the matching type.
+	ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
+
+	// InternalInformerFor returns the SharedIndexInformer for obj using an internal
+	// client.
+	InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer
+
+	Http() http.Interface
+}
+
+func (f *sharedInformerFactory) Http() http.Interface {
+	return http.New(f, f.namespace, f.tweakListOptions)
+}
diff --git a/operator/generated/informers/externalversions/generic.go b/operator/generated/informers/externalversions/generic.go
new file mode 100644
index 0000000..b190b7f
--- /dev/null
+++ b/operator/generated/informers/externalversions/generic.go
@@ -0,0 +1,62 @@
+/*
+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 externalversions
+
+import (
+	"fmt"
+
+	v1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
+	schema "k8s.io/apimachinery/pkg/runtime/schema"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
+// sharedInformers based on type
+type GenericInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() cache.GenericLister
+}
+
+type genericInformer struct {
+	informer cache.SharedIndexInformer
+	resource schema.GroupResource
+}
+
+// Informer returns the SharedIndexInformer.
+func (f *genericInformer) Informer() cache.SharedIndexInformer {
+	return f.informer
+}
+
+// Lister returns the GenericLister.
+func (f *genericInformer) Lister() cache.GenericLister {
+	return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
+}
+
+// ForResource gives generic access to a shared informer of the matching type
+// TODO extend this to unknown resources with a client pool
+func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
+	switch resource {
+	// Group=http, Version=v1alpha1
+	case v1alpha1.SchemeGroupVersion.WithResource("httpscaledobjects"):
+		return &genericInformer{resource: resource.GroupResource(), informer: f.Http().V1alpha1().HTTPScaledObjects().Informer()}, nil
+
+	}
+
+	return nil, fmt.Errorf("no informer found for %v", resource)
+}
diff --git a/operator/generated/informers/externalversions/http/interface.go b/operator/generated/informers/externalversions/http/interface.go
new file mode 100644
index 0000000..4e077b0
--- /dev/null
+++ b/operator/generated/informers/externalversions/http/interface.go
@@ -0,0 +1,46 @@
+/*
+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 http
+
+import (
+	v1alpha1 "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/http/v1alpha1"
+	internalinterfaces "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to each of this group's versions.
+type Interface interface {
+	// V1alpha1 provides access to shared informers for resources in V1alpha1.
+	V1alpha1() v1alpha1.Interface
+}
+
+type group struct {
+	factory          internalinterfaces.SharedInformerFactory
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+	return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// V1alpha1 returns a new v1alpha1.Interface.
+func (g *group) V1alpha1() v1alpha1.Interface {
+	return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
+}
diff --git a/operator/generated/informers/externalversions/http/v1alpha1/httpscaledobject.go b/operator/generated/informers/externalversions/http/v1alpha1/httpscaledobject.go
new file mode 100644
index 0000000..ce59035
--- /dev/null
+++ b/operator/generated/informers/externalversions/http/v1alpha1/httpscaledobject.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"
+)
+
+// HTTPScaledObjectInformer provides access to a shared informer and lister for
+// HTTPScaledObjects.
+type HTTPScaledObjectInformer interface {
+	Informer() cache.SharedIndexInformer
+	Lister() v1alpha1.HTTPScaledObjectLister
+}
+
+type hTTPScaledObjectInformer struct {
+	factory          internalinterfaces.SharedInformerFactory
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+	namespace        string
+}
+
+// NewHTTPScaledObjectInformer constructs a new informer for HTTPScaledObject 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 NewHTTPScaledObjectInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
+	return NewFilteredHTTPScaledObjectInformer(client, namespace, resyncPeriod, indexers, nil)
+}
+
+// NewFilteredHTTPScaledObjectInformer constructs a new informer for HTTPScaledObject 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 NewFilteredHTTPScaledObjectInformer(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().HTTPScaledObjects(namespace).List(context.TODO(), options)
+			},
+			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
+				if tweakListOptions != nil {
+					tweakListOptions(&options)
+				}
+				return client.HttpV1alpha1().HTTPScaledObjects(namespace).Watch(context.TODO(), options)
+			},
+		},
+		&httpv1alpha1.HTTPScaledObject{},
+		resyncPeriod,
+		indexers,
+	)
+}
+
+func (f *hTTPScaledObjectInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
+	return NewFilteredHTTPScaledObjectInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
+}
+
+func (f *hTTPScaledObjectInformer) Informer() cache.SharedIndexInformer {
+	return f.factory.InformerFor(&httpv1alpha1.HTTPScaledObject{}, f.defaultInformer)
+}
+
+func (f *hTTPScaledObjectInformer) Lister() v1alpha1.HTTPScaledObjectLister {
+	return v1alpha1.NewHTTPScaledObjectLister(f.Informer().GetIndexer())
+}
diff --git a/operator/generated/informers/externalversions/http/v1alpha1/interface.go b/operator/generated/informers/externalversions/http/v1alpha1/interface.go
new file mode 100644
index 0000000..1f0fb94
--- /dev/null
+++ b/operator/generated/informers/externalversions/http/v1alpha1/interface.go
@@ -0,0 +1,45 @@
+/*
+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 (
+	internalinterfaces "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/internalinterfaces"
+)
+
+// Interface provides access to all the informers in this group version.
+type Interface interface {
+	// HTTPScaledObjects returns a HTTPScaledObjectInformer.
+	HTTPScaledObjects() HTTPScaledObjectInformer
+}
+
+type version struct {
+	factory          internalinterfaces.SharedInformerFactory
+	namespace        string
+	tweakListOptions internalinterfaces.TweakListOptionsFunc
+}
+
+// New returns a new Interface.
+func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
+	return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
+}
+
+// HTTPScaledObjects returns a HTTPScaledObjectInformer.
+func (v *version) HTTPScaledObjects() HTTPScaledObjectInformer {
+	return &hTTPScaledObjectInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
+}
diff --git a/operator/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/operator/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
new file mode 100644
index 0000000..5a074ba
--- /dev/null
+++ b/operator/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
@@ -0,0 +1,40 @@
+/*
+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 internalinterfaces
+
+import (
+	time "time"
+
+	versioned "github.com/kedacore/http-add-on/operator/generated/clientset/versioned"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
+type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
+
+// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
+type SharedInformerFactory interface {
+	Start(stopCh <-chan struct{})
+	InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
+}
+
+// TweakListOptionsFunc is a function that transforms a v1.ListOptions.
+type TweakListOptionsFunc func(*v1.ListOptions)
diff --git a/operator/generated/listers/http/v1alpha1/expansion_generated.go b/operator/generated/listers/http/v1alpha1/expansion_generated.go
new file mode 100644
index 0000000..85dc9c1
--- /dev/null
+++ b/operator/generated/listers/http/v1alpha1/expansion_generated.go
@@ -0,0 +1,27 @@
+/*
+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
+
+// HTTPScaledObjectListerExpansion allows custom methods to be added to
+// HTTPScaledObjectLister.
+type HTTPScaledObjectListerExpansion interface{}
+
+// HTTPScaledObjectNamespaceListerExpansion allows custom methods to be added to
+// HTTPScaledObjectNamespaceLister.
+type HTTPScaledObjectNamespaceListerExpansion interface{}
diff --git a/operator/generated/listers/http/v1alpha1/httpscaledobject.go b/operator/generated/listers/http/v1alpha1/httpscaledobject.go
new file mode 100644
index 0000000..66d3e53
--- /dev/null
+++ b/operator/generated/listers/http/v1alpha1/httpscaledobject.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"
+)
+
+// HTTPScaledObjectLister helps list HTTPScaledObjects.
+// All objects returned here must be treated as read-only.
+type HTTPScaledObjectLister interface {
+	// List lists all HTTPScaledObjects in the indexer.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error)
+	// HTTPScaledObjects returns an object that can list and get HTTPScaledObjects.
+	HTTPScaledObjects(namespace string) HTTPScaledObjectNamespaceLister
+	HTTPScaledObjectListerExpansion
+}
+
+// hTTPScaledObjectLister implements the HTTPScaledObjectLister interface.
+type hTTPScaledObjectLister struct {
+	indexer cache.Indexer
+}
+
+// NewHTTPScaledObjectLister returns a new HTTPScaledObjectLister.
+func NewHTTPScaledObjectLister(indexer cache.Indexer) HTTPScaledObjectLister {
+	return &hTTPScaledObjectLister{indexer: indexer}
+}
+
+// List lists all HTTPScaledObjects in the indexer.
+func (s *hTTPScaledObjectLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error) {
+	err = cache.ListAll(s.indexer, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.HTTPScaledObject))
+	})
+	return ret, err
+}
+
+// HTTPScaledObjects returns an object that can list and get HTTPScaledObjects.
+func (s *hTTPScaledObjectLister) HTTPScaledObjects(namespace string) HTTPScaledObjectNamespaceLister {
+	return hTTPScaledObjectNamespaceLister{indexer: s.indexer, namespace: namespace}
+}
+
+// HTTPScaledObjectNamespaceLister helps list and get HTTPScaledObjects.
+// All objects returned here must be treated as read-only.
+type HTTPScaledObjectNamespaceLister interface {
+	// List lists all HTTPScaledObjects in the indexer for a given namespace.
+	// Objects returned here must be treated as read-only.
+	List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error)
+	// Get retrieves the HTTPScaledObject from the indexer for a given namespace and name.
+	// Objects returned here must be treated as read-only.
+	Get(name string) (*v1alpha1.HTTPScaledObject, error)
+	HTTPScaledObjectNamespaceListerExpansion
+}
+
+// hTTPScaledObjectNamespaceLister implements the HTTPScaledObjectNamespaceLister
+// interface.
+type hTTPScaledObjectNamespaceLister struct {
+	indexer   cache.Indexer
+	namespace string
+}
+
+// List lists all HTTPScaledObjects in the indexer for a given namespace.
+func (s hTTPScaledObjectNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.HTTPScaledObject, err error) {
+	err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
+		ret = append(ret, m.(*v1alpha1.HTTPScaledObject))
+	})
+	return ret, err
+}
+
+// Get retrieves the HTTPScaledObject from the indexer for a given namespace and name.
+func (s hTTPScaledObjectNamespaceLister) Get(name string) (*v1alpha1.HTTPScaledObject, error) {
+	obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
+	if err != nil {
+		return nil, err
+	}
+	if !exists {
+		return nil, errors.NewNotFound(v1alpha1.Resource("httpscaledobject"), name)
+	}
+	return obj.(*v1alpha1.HTTPScaledObject), nil
+}
diff --git a/operator/main.go b/operator/main.go
index 6e659c7..25afb84 100644
--- a/operator/main.go
+++ b/operator/main.go
@@ -1,5 +1,5 @@
 /*
-
+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.
@@ -29,15 +29,17 @@ import (
 	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/plugin/pkg/client/auth/gcp"
+	_ "k8s.io/client-go/plugin/pkg/client/auth"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/healthz"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
-	httpv1alpha1 "github.com/kedacore/http-add-on/operator/api/v1alpha1"
-	"github.com/kedacore/http-add-on/operator/controllers"
-	"github.com/kedacore/http-add-on/operator/controllers/config"
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
+	httpcontrollers "github.com/kedacore/http-add-on/operator/controllers/http"
+	"github.com/kedacore/http-add-on/operator/controllers/http/config"
 	"github.com/kedacore/http-add-on/pkg/build"
 	kedahttp "github.com/kedacore/http-add-on/pkg/http"
 	"github.com/kedacore/http-add-on/pkg/k8s"
@@ -51,10 +53,10 @@ var (
 )
 
 func init() {
-	_ = clientgoscheme.AddToScheme(scheme)
+	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
 
-	_ = httpv1alpha1.AddToScheme(scheme)
-	_ = kedav1alpha1.AddToScheme(scheme)
+	utilruntime.Must(httpv1alpha1.AddToScheme(scheme))
+	utilruntime.Must(kedav1alpha1.AddToScheme(scheme))
 	// +kubebuilder:scaffold:scheme
 }
 
@@ -63,23 +65,30 @@ func init() {
 // +kubebuilder:rbac:groups=coordination.k8s.io,namespace=keda,resources=leases,verbs=get;list;watch;create;update;patch;delete
 
 func main() {
-	ctx := ctrl.SetupSignalHandler()
-	ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
-
 	var metricsAddr string
 	var enableLeaderElection bool
+	var probeAddr string
 	var adminPort int
-	flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
-	flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
+	flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
+	flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
+	flag.BoolVar(&enableLeaderElection, "leader-elect", false,
 		"Enable leader election for controller manager. "+
 			"Enabling this will ensure there is only one active controller manager.")
+	// TODO(pedrotorres): remove after implementing new routing table
 	flag.IntVar(
 		&adminPort,
 		"admin-port",
 		9090,
 		"The port on which to run the admin server. This is the port on which RPCs will be accepted to get the routing table",
 	)
+	opts := zap.Options{
+		Development: true,
+	}
+	opts.BindFlags(flag.CommandLine)
 	flag.Parse()
+
+	ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+
 	interceptorCfg, err := config.NewInterceptorFromEnv()
 	if err != nil {
 		setupLog.Error(err, "unable to get interceptor configuration")
@@ -98,31 +107,18 @@ func main() {
 		)
 		os.Exit(1)
 	}
-	if err := ensureConfigMap(
-		ctx,
-		setupLog,
-		baseConfig.CurrentNamespace,
-		routing.ConfigMapRoutingTableName,
-	); err != nil {
-		setupLog.Error(
-			err,
-			"unable to find routing table ConfigMap",
-			"namespace",
-			baseConfig.CurrentNamespace,
-			"name",
-			routing.ConfigMapRoutingTableName,
-		)
-		os.Exit(1)
-	}
 
 	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
-		Scheme:             scheme,
-		MetricsBindAddress: metricsAddr,
-		Port:               9443,
-		LeaderElection:     enableLeaderElection,
-		LeaderElectionID:   "f8508ff1.keda.sh",
-		// will be empty to indicate all namespaces
+		Scheme:                 scheme,
+		MetricsBindAddress:     metricsAddr,
+		Port:                   9443,
+		HealthProbeBindAddress: probeAddr,
+		LeaderElection:         enableLeaderElection,
+		LeaderElectionID:       "f8508ff1.keda.sh",
+		// TODO(pedrotorres): uncomment after implementing new routing table
+		// LeaderElectionReleaseOnCancel: true,
 		Namespace: baseConfig.WatchNamespace,
+
 		// TODO(pedrotorres): remove this when we stop relying on ConfigMaps for the routing table
 		// workaround for using the same K8s client for both the routing table and the HTTPScaledObject
 		// this was already broken if the operator was running only for a single namespace
@@ -135,12 +131,11 @@ func main() {
 		os.Exit(1)
 	}
 
-	cl := mgr.GetClient()
 	routingTable := routing.NewTable()
-	if err := (&controllers.HTTPScaledObjectReconciler{
-		Client:               cl,
-		Log:                  ctrl.Log.WithName("controllers").WithName("HTTPScaledObject"),
-		Scheme:               mgr.GetScheme(),
+	if err = (&httpcontrollers.HTTPScaledObjectReconciler{
+		Client: mgr.GetClient(),
+		Scheme: mgr.GetScheme(),
+
 		InterceptorConfig:    *interceptorCfg,
 		ExternalScalerConfig: *externalScalerCfg,
 		BaseConfig:           *baseConfig,
@@ -149,11 +144,45 @@ func main() {
 		setupLog.Error(err, "unable to create controller", "controller", "HTTPScaledObject")
 		os.Exit(1)
 	}
-	// +kubebuilder:scaffold:builder
+	//+kubebuilder:scaffold:builder
 
-	errGrp, ctx := errgroup.WithContext(
+	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+		setupLog.Error(err, "unable to set up health check")
+		os.Exit(1)
+	}
+	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+		setupLog.Error(err, "unable to set up ready check")
+		os.Exit(1)
+	}
+
+	// TODO(pedrotorres): uncomment after implementing new routing table
+	// setupLog.Info("starting manager")
+	// if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
+	// 	setupLog.Error(err, "problem running manager")
+	// 	os.Exit(1)
+	// }
+
+	// TODO(pedrotorres): remove everything beyond this line after implementing
+	// new routing table
+	ctx := ctrl.SetupSignalHandler()
+	if err := ensureConfigMap(
 		ctx,
-	)
+		setupLog,
+		baseConfig.CurrentNamespace,
+		routing.ConfigMapRoutingTableName,
+	); err != nil {
+		setupLog.Error(
+			err,
+			"unable to find routing table ConfigMap",
+			"namespace",
+			baseConfig.CurrentNamespace,
+			"name",
+			routing.ConfigMapRoutingTableName,
+		)
+		os.Exit(1)
+	}
+
+	errGrp, ctx := errgroup.WithContext(ctx)
 	ctx, done := context.WithCancel(ctx)
 
 	// start the control loop
diff --git a/operator/scripts/download-controller-gen.sh b/operator/scripts/download-controller-gen.sh
deleted file mode 100644
index 08f9ad7..0000000
--- a/operator/scripts/download-controller-gen.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-CONTROLLER_GEN_TMP_DIR=$(mktemp -d)
-cd $CONTROLLER_GEN_TMP_DIR
-
-go mod init tmp
-echo "Downloading controller gen"
-go install sigs.k8s.io/controller-tools/cmd/controller-gen
-echo "Removing temp dir"
-rm -rf $CONTROLLER_GEN_TMP_DIR
-
-echo "Controller gen installed at $(which controller-gen)"
diff --git a/operator/scripts/set-kustomize-image.sh b/operator/scripts/set-kustomize-image.sh
deleted file mode 100644
index 3acf97a..0000000
--- a/operator/scripts/set-kustomize-image.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-cd config/manager
-kustomize edit set image controller=$1
diff --git a/pkg/routing/config_map.go b/pkg/routing/config_map.go
index 6f3ce40..bb26608 100644
--- a/pkg/routing/config_map.go
+++ b/pkg/routing/config_map.go
@@ -15,7 +15,7 @@ import (
 
 const (
 	// the name of the ConfigMap that stores the routing table
-	ConfigMapRoutingTableName = "keda-http-routing-table"
+	ConfigMapRoutingTableName = "keda-http-add-on-routing-table"
 	// the key in the ConfigMap data that stores the JSON routing table
 	configMapRoutingTableKey = "routing-table"
 )


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 98d6de3..a9839cf 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -26,9 +26,6 @@ jobs:
     - name: Check go version
       run: go version
 
-    - name: Manifests
-      run: make verify-manifests
-
     - name: Set Go paths
       id: go-paths
       run: |
@@ -47,6 +44,15 @@ jobs:
         path: ${{ steps.go-paths.outputs.build_cache }}
         key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.sum') }}
 
+    - name: Download Go Modules
+      run: go mod download
+
+    - name: Codegen
+      run: make verify-codegen
+
+    - name: Manifests
+      run: make verify-manifests
+
     - name: Build
       run: ARCH=${{ matrix.name }} make build
 
diff --git a/interceptor/main_test.go b/interceptor/main_test.go
index ac9bb39..f86531c 100644
--- a/interceptor/main_test.go
+++ b/interceptor/main_test.go
@@ -127,7 +127,7 @@ func TestRunProxyServerCountMiddleware(t *testing.T) {
 	case hostAndCount := <-q.ResizedCh:
 		r.Equal(host, hostAndCount.Host)
 		r.Equal(-1, hostAndCount.Count)
-	case <-time.After(500 * time.Millisecond):
+	case <-time.After(2 * time.Second):
 		r.Fail("timeout waiting for -1 queue resize")
 	}
 
diff --git a/operator/controllers/config/app_info_test.go b/operator/controllers/http/config/app_info_test.go
similarity index 88%
rename from operator/controllers/config/app_info_test.go
rename to operator/controllers/http/config/app_info_test.go
index 1527148..7e7da55 100644
--- a/operator/controllers/config/app_info_test.go
+++ b/operator/controllers/http/config/app_info_test.go
@@ -6,7 +6,7 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"github.com/kedacore/http-add-on/operator/api/v1alpha1"
+	"github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 )
 
 func TestAppScaledObjectName(t *testing.T) {
diff --git a/operator/controllers/ping_test.go b/operator/controllers/http/ping_test.go
similarity index 98%
rename from operator/controllers/ping_test.go
rename to operator/controllers/http/ping_test.go
index 101281b..2bd4feb 100644
--- a/operator/controllers/ping_test.go
+++ b/operator/controllers/http/ping_test.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
diff --git a/operator/controllers/routing_table_test.go b/operator/controllers/http/routing_table_test.go
similarity index 98%
rename from operator/controllers/routing_table_test.go
rename to operator/controllers/http/routing_table_test.go
index ce0ad54..07d6bf1 100644
--- a/operator/controllers/routing_table_test.go
+++ b/operator/controllers/http/routing_table_test.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
diff --git a/operator/controllers/scaled_object_test.go b/operator/controllers/http/scaled_object_test.go
similarity index 95%
rename from operator/controllers/scaled_object_test.go
rename to operator/controllers/http/scaled_object_test.go
index 401c4c4..25d583b 100644
--- a/operator/controllers/scaled_object_test.go
+++ b/operator/controllers/http/scaled_object_test.go
@@ -1,4 +1,4 @@
-package controllers
+package http
 
 import (
 	"context"
@@ -10,8 +10,8 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	"github.com/kedacore/http-add-on/operator/api/v1alpha1"
-	"github.com/kedacore/http-add-on/operator/controllers/config"
+	"github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
+	"github.com/kedacore/http-add-on/operator/controllers/http/config"
 )
 
 func TestCreateOrUpdateScaledObject(t *testing.T) {
diff --git a/operator/controllers/suite_test.go b/operator/controllers/http/suite_test.go
similarity index 67%
rename from operator/controllers/suite_test.go
rename to operator/controllers/http/suite_test.go
index f9af2c5..3470689 100644
--- a/operator/controllers/suite_test.go
+++ b/operator/controllers/http/suite_test.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 // */
 
-package controllers
+package http
 
 import (
 	"context"
@@ -21,32 +21,21 @@ import (
 
 	"github.com/go-logr/logr"
 	kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	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"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
-	httpv1alpha1 "github.com/kedacore/http-add-on/operator/api/v1alpha1"
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 	// +kubebuilder:scaffold:imports
 )
 
-var (
-	scheme = runtime.NewScheme()
-)
-
-func init() {
-	_ = clientgoscheme.AddToScheme(scheme)
-
-	_ = httpv1alpha1.AddToScheme(scheme)
-	_ = kedav1alpha1.AddToScheme(scheme)
-	// +kubebuilder:scaffold:scheme
-}
-
 // These tests use Ginkgo (BDD-style Go testing framework). Refer to
 // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
 
@@ -60,6 +49,40 @@ func TestAPIs(t *testing.T) {
 	RunSpecs(t, "Controller Suite")
 }
 
+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,
+	// }
+
+	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)
+	Expect(err).NotTo(HaveOccurred())
+
+	err = kedav1alpha1.AddToScheme(clientgoscheme.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())
+})
+
+var _ = AfterSuite(func() {
+	By("tearing down the test environment")
+	// err := testEnv.Stop()
+	// Expect(err).NotTo(HaveOccurred())
+})
+
 type commonTestInfra struct {
 	ns      string
 	appName string
@@ -70,8 +93,13 @@ type commonTestInfra struct {
 }
 
 func newCommonTestInfra(namespace, appName string) *commonTestInfra {
+	localScheme := runtime.NewScheme()
+	utilruntime.Must(clientgoscheme.AddToScheme(localScheme))
+	utilruntime.Must(httpv1alpha1.AddToScheme(localScheme))
+	utilruntime.Must(kedav1alpha1.AddToScheme(localScheme))
+
 	ctx := context.Background()
-	cl := fake.NewClientBuilder().WithScheme(scheme).Build()
+	cl := fake.NewClientBuilder().WithScheme(localScheme).Build()
 	logger := logr.Discard()
 
 	httpso := httpv1alpha1.HTTPScaledObject{
@@ -97,40 +125,3 @@ func newCommonTestInfra(namespace, appName string) *commonTestInfra {
 		httpso:  httpso,
 	}
 }
-
-var _ = BeforeSuite(func(done Done) {
-	// The commented code in this function connects to a test Kubernetes cluster.
-	// We don't currently have tests that exercise functionality that needs a cluster,
-	// so we're keeping it commented.
-	logf.SetLogger(zap.New(func(opts *zap.Options) {
-		opts.DestWriter = GinkgoWriter
-	}))
-
-	// By("bootstrapping test environment")
-	// useExistingCluster := true
-	// testEnv = &envtest.Environment{
-	// 	CRDDirectoryPaths:  []string{filepath.Join("..", "config", "crd", "bases")},
-	// 	UseExistingCluster: &useExistingCluster,
-	// }
-
-	// cfg, err = testEnv.Start()
-	// Expect(err).ToNot(HaveOccurred())
-	// Expect(cfg).ToNot(BeNil())
-
-	err := httpv1alpha1.AddToScheme(clientgoscheme.Scheme)
-	Expect(err).NotTo(HaveOccurred())
-
-	// +kubebuilder:scaffold:scheme
-
-	// k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
-	// Expect(err).ToNot(HaveOccurred())
-	// Expect(k8sClient).ToNot(BeNil())
-
-	close(done)
-}, 60)
-
-var _ = AfterSuite(func() {
-	By("tearing down the test environment")
-	// err := testEnv.Stop()
-	// Expect(err).ToNot(HaveOccurred())
-})
diff --git a/operator/main_test.go b/operator/main_test.go
index 33aeab7..a42734c 100644
--- a/operator/main_test.go
+++ b/operator/main_test.go
@@ -14,7 +14,7 @@ import (
 	"golang.org/x/sync/errgroup"
 	"k8s.io/apimachinery/pkg/util/rand"
 
-	"github.com/kedacore/http-add-on/operator/controllers/config"
+	"github.com/kedacore/http-add-on/operator/controllers/http/config"
 	"github.com/kedacore/http-add-on/pkg/routing"
 	"github.com/kedacore/http-add-on/pkg/test"
 )
diff --git a/pkg/routing/suite_test.go b/pkg/routing/suite_test.go
index 94d53b1..03b125d 100644
--- a/pkg/routing/suite_test.go
+++ b/pkg/routing/suite_test.go
@@ -3,11 +3,52 @@ package routing
 import (
 	"testing"
 
-	. "github.com/onsi/ginkgo"
+	kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+	"k8s.io/client-go/kubernetes/scheme"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+	httpv1alpha1 "github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
 )
 
 func TestRouting(t *testing.T) {
 	RegisterFailHandler(Fail)
+
 	RunSpecs(t, "Routing Suite")
 }
+
+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,
+	// }
+
+	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(scheme.Scheme)
+	Expect(err).NotTo(HaveOccurred())
+
+	err = kedav1alpha1.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())
+})
+
+var _ = AfterSuite(func() {
+	By("tearing down the test environment")
+	// err := testEnv.Stop()
+	// Expect(err).NotTo(HaveOccurred())
+})
diff --git a/pkg/routing/table_test.go b/pkg/routing/table_test.go
index 1269d77..4e202d0 100644
--- a/pkg/routing/table_test.go
+++ b/pkg/routing/table_test.go
@@ -7,7 +7,7 @@ import (
 	"strconv"
 	"testing"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 	"github.com/stretchr/testify/require"
 )