Repository: kedacore/http-add-on
Pull request #564 (2023-01-05 21:18:04)

feat: use make for scripting unifying the KEDA tools
Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>

As we use make for keda, let's standardize the tooling across repositories.
This PR also moves the e2e test from only main branch to every PR (and also every push to main) due to now it's possible having the manifests close to the code.
Any information related with mage has been updated and any dependency has been replaced by other tools

### Checklist

- [x] Commits are signed with Developer Certificate of Origin (DCO)

Fixes #


tomkerkhove: Nice job!

Patch:
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index a7300a6..cfdb0a3 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,13 +1,6 @@
 ARG GO_VERSION=1.18
 FROM mcr.microsoft.com/vscode/devcontainers/go:0-${GO_VERSION}
 
-# install mage
-RUN git clone https://github.com/magefile/mage && \
-    cd mage && \
-    go run bootstrap.go && \
-    cd .. && \
-    rm -rf mage
-
 # install helm
 RUN curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
 
diff --git a/.dockerignore b/.dockerignore
index e413e8a..ab088f7 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -13,4 +13,4 @@
 /README.md
 /RELEASE_PROCESS.md
 CONTRIBUTING.md
-Makefile
+Dockerfile
diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml
index aade603..e3d00ef 100644
--- a/.github/workflows/build_release.yml
+++ b/.github/workflows/build_release.yml
@@ -141,23 +141,3 @@ jobs:
           file: scaler/Dockerfile
           context: .
           push: true
-
-  e2e_tests:
-    runs-on: ubuntu-20.04
-    steps:
-      - uses: actions/checkout@v3
-
-      - name: Helm install
-        uses: Azure/setup-helm@v3
-
-      - name: Create k8s v1.23 Kind Cluster
-        uses: helm/kind-action@main
-        with:
-          node_image: kindest/node:v1.23.0@sha256:49824ab1727c04e56a21a5d8372a402fcd32ea51ac96a2706a12af38934f81ac
-          cluster_name: http-add-on-smoke-tests-cluster
-
-      - name: Run e2e test
-        run: |
-          ./tests/e2e-test.sh
-        env:
-          TAG: ${{ steps.get_version.outputs.VERSION }}
diff --git a/.github/workflows/images.yaml b/.github/workflows/images.yaml
index 34acc47..7d4c7bc 100644
--- a/.github/workflows/images.yaml
+++ b/.github/workflows/images.yaml
@@ -7,34 +7,37 @@ on:
 
 jobs:
   build_scaler:
-    
-    runs-on: ubuntu-20.04
-
+    runs-on: ubuntu-latest
+    container: ghcr.io/kedacore/build-tools:1.18.8
     steps:
       - uses: actions/checkout@v3
+      - name: Register workspace path
+        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
       - name: Build The Scaler
         run: |
           COMMIT=$(git rev-parse --short HEAD)
-          docker build --build-arg VERSION=${COMMIT} -t scaler -f scaler/Dockerfile .
+          VERSION=${COMMIT} make docker-build-scaler
   
   build_operator:
-    
-    runs-on: ubuntu-20.04
-
+    runs-on: ubuntu-latest
+    container: ghcr.io/kedacore/build-tools:1.18.8
     steps:
       - uses: actions/checkout@v3
+      - name: Register workspace path
+        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
       - name: Build The Operator
         run: |
           COMMIT=$(git rev-parse --short=7 HEAD)
-          docker build --build-arg VERSION=${COMMIT} -t operator -f operator/Dockerfile .
+          VERSION=${COMMIT} make docker-build-operator
 
   build_interceptor:
-    
-    runs-on: ubuntu-20.04
-
+    runs-on: ubuntu-latest
+    container: ghcr.io/kedacore/build-tools:1.18.8
     steps:
       - uses: actions/checkout@v3
+      - name: Register workspace path
+        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
       - name: Build The Interceptor
         run: |
           COMMIT=$(git rev-parse --short=7 HEAD)
-          docker build --build-arg VERSION=${COMMIT} -t interceptor -f interceptor/Dockerfile .
+          VERSION=${COMMIT} make docker-build-interceptor
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 88c04a2..b0d630f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -43,57 +43,44 @@ Please find it at [docs/developing.md](./docs/developing.md).
 ### Pre-requisite:
 
 - A running Kubernetes cluster with KEDA installed.
-- [Mage](https://magefile.org/)
+- [Make](https://www.gnu.org/software/make/)
 - [Helm](https://helm.sh/)
 - [k9s](https://github.com/derailed/k9s) (_optional_)
-- Set the required environment variables explained [here](https://github.com/kedacore/http-add-on/blob/main/docs/developing.md#required-environment-variables).
 
 ### Building:
 
 - Fork & clone the repo:
-  ```console
+  ```bash
   $ git clone https://github.com/<your-username>/http-add-on.git
   ```
 - Change into the repo directory:
-  ```console
+  ```bash
   $ cd http-add-on
   ```
-- Use Mage to build with:
-   ```console
-   $ mage build       # build local binaries
-   $ mage dockerBuild # build docker images of the components
+- Use Make to build with:
+   ```bash
+   $ make build         # build local binaries
+   $ make docker-build  # build docker images of the components
    ```
- If the environment variables are not setup , the docker build will fail so remember to export the right variable values.
 
 ### Deploying:
 
 Custom HTTP Add-on as an image
 
 - Make your changes in the code
-- Build and publish images with your changes, remember  to 	set your environment variables for images as per the registry of your choice and run
-  ```console
-  $ mage dockerBuild
-  ```
- If you want to deploy with docker or any other registry of your choice then use right address in setting the images.
+- Build and publish images with your changes, remembering to update the information for registry of your choice:
 
- There are local clusters with local registries provided, in such cases  make sure to use and push your images to its local registry. In the case of MicroK8s, the address is `localhost:32000` and the helm install command would look like the following.
+```bash
+IMAGE_REGISTRY=docker.io IMAGE_REPO=johndoe make docker-publish
+```
 
-```console
-$ helm repo add kedacore https://kedacore.github.io/charts
-$ helm repo update
-$ helm pull kedacore/keda-add-ons-http --untar --untardir ./charts
-$ helm upgrade kedahttp ./charts/keda-add-ons-http \
-  --install \
-  --namespace ${NAMESPACE} \
-  --create-namespace \
-  --set image=localhost:32000/keda-http-operator \
-  --set images.scaler=localhost:32000/keda-http-scaler \
-  --set images.interceptor=localhost:32000/keda-http-interceptor
+> Note: If you need to build images to other architecture from your machine, you can use multi-arch building with `IMAGE_REGISTRY=docker.io IMAGE_REPO=johndo make publish-multiarch`.
+
+There are local clusters with local registries provided, in such cases make sure to use and push your images to its local registry. In the case of MicroK8s, the address is `localhost:32000` and the  command would look like the following.
+
+```bash
+IMAGE_REGISTRY=localhost:32000 IMAGE_REPO=johndo make deploy
 ```
- If you want to install the latest build of the HTTP Add-on, set version to `canary`:
- ```console
- $ helm install http-add-on kedacore/keda-add-ons-http --create-namespace --namespace ${NAMESPACE} --set images.tag=canary
- ```
 ### Load testing with k9s:
 
 K9s integrates Hey, a CLI tool to benchmark HTTP endpoints similar to AB bench. This preliminary feature currently supports benchmarking port-forwards and services. You can use this feature in load testing as follows:
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ac0606a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,105 @@
+##################################################
+# Variables                                      #
+##################################################
+SHELL           = /bin/bash
+
+IMAGE_REGISTRY 	?= ghcr.io
+IMAGE_REPO     	?= kedacore
+VERSION 		?= main
+
+IMAGE_OPERATOR 		?= ${IMAGE_REGISTRY}/${IMAGE_REPO}/http-add-on-operator:$(VERSION)
+IMAGE_INTERCEPTOR	?= ${IMAGE_REGISTRY}/${IMAGE_REPO}/http-add-on-interceptor:$(VERSION)
+IMAGE_SCALER		?= ${IMAGE_REGISTRY}/${IMAGE_REPO}/http-add-on-scaler:$(VERSION)
+
+ARCH       ?=amd64
+CGO        ?=0
+TARGET_OS  ?=linux
+
+BUILD_PLATFORMS ?= linux/amd64,linux/arm64
+OUTPUT_TYPE     ?= registry
+
+GO_BUILD_VARS= GO111MODULE=on CGO_ENABLED=$(CGO) GOOS=$(TARGET_OS) GOARCH=$(ARCH)
+GO_LDFLAGS="-X github.com/kedacore/http-add-on/pkg/build.version=${VERSION} -X github.com/kedacore/http-add-on/pkg/build.gitCommit=${GIT_COMMIT}"
+
+GIT_COMMIT  ?= $(shell git rev-list -1 HEAD)
+
+# Build targets
+
+build-operator:
+	${GO_BUILD_VARS} go build -ldflags $(GO_LDFLAGS) -a -o bin/operator ./operator
+
+build-interceptor:
+	${GO_BUILD_VARS} go build -ldflags $(GO_LDFLAGS) -a -o bin/interceptor ./interceptor
+
+build-scaler:
+	${GO_BUILD_VARS} go build -ldflags $(GO_LDFLAGS) -a -o bin/scaler ./scaler
+
+build: build-operator build-interceptor build-scaler
+
+# Test targets
+test:
+	go test ./...
+
+e2e-test:
+	./tests/e2e-test.sh
+
+# Docker targets
+docker-build-operator: 
+	DOCKER_BUILDKIT=1 docker build . -t ${IMAGE_OPERATOR} -f operator/Dockerfile --build-arg VERSION=${VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT}
+
+docker-build-interceptor: 
+	DOCKER_BUILDKIT=1 docker build . -t ${IMAGE_INTERCEPTOR} -f interceptor/Dockerfile --build-arg VERSION=${VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT}
+
+docker-build-scaler: 
+	DOCKER_BUILDKIT=1 docker build . -t ${IMAGE_SCALER} -f scaler/Dockerfile --build-arg VERSION=${VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT}
+
+docker-build: docker-build-operator docker-build-interceptor docker-build-scaler
+
+docker-publish: docker-build ## Push images on to Container Registry (default: ghcr.io).
+	docker push $(IMAGE_OPERATOR)
+	docker push $(IMAGE_INTERCEPTOR)
+	docker push $(IMAGE_SCALER)
+
+publish-operator-multiarch:
+	docker buildx build --output=type=${OUTPUT_TYPE} --platform=${BUILD_PLATFORMS} . -t ${IMAGE_OPERATOR} -f operator/Dockerfile --build-arg VERSION=${VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT}
+
+publish-interceptor-multiarch:
+	docker buildx build --output=type=${OUTPUT_TYPE} --platform=${BUILD_PLATFORMS} . -t ${IMAGE_INTERCEPTOR} -f interceptor/Dockerfile --build-arg VERSION=${VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT}
+
+publish-scaler-multiarch:
+	docker buildx build --output=type=${OUTPUT_TYPE} --platform=${BUILD_PLATFORMS} . -t ${IMAGE_SCALER} -f scaler/Dockerfile --build-arg VERSION=${VERSION} --build-arg GIT_COMMIT=${GIT_COMMIT}
+
+publish-multiarch: publish-operator-multiarch publish-interceptor-multiarch publish-scaler-multiarch
+
+# Development
+
+manifests: controller-gen ## Generate ClusterRole and CustomResourceDefinition objects for core componenets.
+	$(CONTROLLER_GEN) crd:crdVersions=v1 rbac:roleName=keda-http-add-on paths="./..." output:crd:artifacts:config=config/crd/bases
+
+verify-manifests: 
+	./hack/verify-manifests.sh
+
+CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
+controller-gen: ## Download controller-gen locally if necessary.
+	GOBIN=$(shell pwd)/bin go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.10.0
+
+KUSTOMIZE = $(shell pwd)/bin/kustomize
+kustomize: ## Download kustomize locally if necessary.
+	GOBIN=$(shell pwd)/bin go install sigs.k8s.io/kustomize/kustomize/v4@v4.5.7
+
+deploy: manifests kustomize ## Deploy to the K8s cluster specified in ~/.kube/config.
+	cd config/interceptor && \
+	$(KUSTOMIZE) edit set image ghcr.io/kedacore/http-add-on-interceptor=${IMAGE_INTERCEPTOR}
+
+	cd config/scaler && \
+	$(KUSTOMIZE) edit set image ghcr.io/kedacore/http-add-on-scaler=${IMAGE_SCALER}
+
+	cd config/operator && \
+	$(KUSTOMIZE) edit set image ghcr.io/kedacore/http-add-on-operator=${IMAGE_OPERATOR}
+
+	@sed -i".out" -e 's@version:[ ].*@version: $(VERSION)@g' config/default/kustomize-config/metadataLabelTransformer.yaml
+	rm -rf config/default/kustomize-config/metadataLabelTransformer.yaml.out
+	$(KUSTOMIZE) build config/default | kubectl apply -f -
+
+undeploy:
+	$(KUSTOMIZE) build config/default | kubectl delete -f -
\ No newline at end of file
diff --git a/config/crd/bases/http.keda.sh_httpscaledobjects.yaml b/config/crd/bases/http.keda.sh_httpscaledobjects.yaml
new file mode 100644
index 0000000..ac80645
--- /dev/null
+++ b/config/crd/bases/http.keda.sh_httpscaledobjects.yaml
@@ -0,0 +1,162 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.10.0
+  creationTimestamp: null
+  name: httpscaledobjects.http.keda.sh
+spec:
+  group: http.keda.sh
+  names:
+    kind: HTTPScaledObject
+    listKind: HTTPScaledObjectList
+    plural: httpscaledobjects
+    shortNames:
+    - httpso
+    singular: httpscaledobject
+  scope: Namespaced
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .spec.scaleTargetRef.deploymentName
+      name: ScaleTargetDeploymentName
+      type: string
+    - jsonPath: .spec.scaleTargetRef
+      name: ScaleTargetServiceName
+      type: string
+    - jsonPath: .spec.scaleTargetRef
+      name: ScaleTargetPort
+      type: integer
+    - jsonPath: .spec.replicas.min
+      name: MinReplicas
+      type: integer
+    - jsonPath: .spec.replicas.max
+      name: MaxReplicas
+      type: integer
+    - jsonPath: .metadata.creationTimestamp
+      name: Age
+      type: date
+    - jsonPath: .status.conditions[?(@.type=="HTTPScaledObjectIsReady")].status
+      name: Active
+      type: string
+    name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: HTTPScaledObjectSpec defines the desired state of HTTPScaledObject
+            properties:
+              host:
+                description: 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
+                type: string
+              replicas:
+                description: (optional) Replica information
+                properties:
+                  max:
+                    description: Maximum amount of replicas to have in the deployment
+                      (Default 100)
+                    format: int32
+                    type: integer
+                  min:
+                    description: Minimum amount of replicas to have in the deployment
+                      (Default 0)
+                    format: int32
+                    type: integer
+                type: object
+              scaleTargetRef:
+                description: The name of the deployment to route HTTP requests to
+                  (and to autoscale). Either this or Image must be set
+                properties:
+                  deployment:
+                    description: The name of the deployment to scale according to
+                      HTTP traffic
+                    type: string
+                  port:
+                    description: The port to route to
+                    format: int32
+                    type: integer
+                  service:
+                    description: The name of the service to route to
+                    type: string
+                required:
+                - deployment
+                - port
+                - service
+                type: object
+              scaledownPeriod:
+                description: (optional) Cooldown period value
+                format: int32
+                type: integer
+              targetPendingRequests:
+                description: (optional) Target metric value
+                format: int32
+                type: integer
+            required:
+            - host
+            - scaleTargetRef
+            type: object
+          status:
+            description: HTTPScaledObjectStatus defines the observed state of HTTPScaledObject
+            properties:
+              conditions:
+                description: List of auditable conditions of the operator
+                items:
+                  description: Condition to store the condition state
+                  properties:
+                    message:
+                      description: A human readable message indicating details about
+                        the transition.
+                      type: string
+                    reason:
+                      description: The reason for the condition's last transition.
+                      enum:
+                      - ErrorCreatingAppScaledObject
+                      - AppScaledObjectCreated
+                      - TerminatingResources
+                      - AppScaledObjectTerminated
+                      - AppScaledObjectTerminationError
+                      - PendingCreation
+                      - HTTPScaledObjectIsReady
+                      type: string
+                    status:
+                      description: Status of the condition, one of True, False, Unknown.
+                      type: string
+                    timestamp:
+                      description: Timestamp of the condition
+                      type: string
+                    type:
+                      description: Type of condition
+                      enum:
+                      - Created
+                      - Error
+                      - Pending
+                      - Unknown
+                      - Terminating
+                      - Terminated
+                      - Ready
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
new file mode 100644
index 0000000..de97b00
--- /dev/null
+++ b/config/crd/kustomization.yaml
@@ -0,0 +1,7 @@
+# This kustomization.yaml is not intended to be run by itself,
+# since it depends on service name and namespace that are out of this kustomize package.
+# It should be run by config/default
+resources:
+- bases/http.keda.sh_httpscaledobjects.yaml
+
+# +kubebuilder:scaffold:crdkustomizeresource
\ No newline at end of file
diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml
new file mode 100644
index 0000000..8bc94d6
--- /dev/null
+++ b/config/default/kustomization.yaml
@@ -0,0 +1,33 @@
+# 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
+
diff --git a/config/default/kustomize-config/metadataLabelTransformer.yaml b/config/default/kustomize-config/metadataLabelTransformer.yaml
new file mode 100644
index 0000000..cf55ef9
--- /dev/null
+++ b/config/default/kustomize-config/metadataLabelTransformer.yaml
@@ -0,0 +1,10 @@
+apiVersion: builtin
+kind: LabelTransformer
+metadata:
+  name: metadataLabelTransformer
+labels:
+  app.kubernetes.io/version: e2e-test
+  app.kubernetes.io/part-of: keda-http-add-on
+fieldSpecs:
+- path: metadata/labels
+  create: true
diff --git a/config/interceptor/interceptor.yaml b/config/interceptor/interceptor.yaml
new file mode 100644
index 0000000..367cc3d
--- /dev/null
+++ b/config/interceptor/interceptor.yaml
@@ -0,0 +1,88 @@
+---
+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
+      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
new file mode 100644
index 0000000..1aa1786
--- /dev/null
+++ b/config/interceptor/kustomization.yaml
@@ -0,0 +1,11 @@
+resources:
+- interceptor.yaml
+- service-admin.yaml
+- service-proxy.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
diff --git a/config/interceptor/service-admin.yaml b/config/interceptor/service-admin.yaml
new file mode 100644
index 0000000..f38629e
--- /dev/null
+++ b/config/interceptor/service-admin.yaml
@@ -0,0 +1,18 @@
+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
\ No newline at end of file
diff --git a/config/interceptor/service-proxy.yaml b/config/interceptor/service-proxy.yaml
new file mode 100644
index 0000000..1e6bc6b
--- /dev/null
+++ b/config/interceptor/service-proxy.yaml
@@ -0,0 +1,18 @@
+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/operator/kustomization.yaml b/config/operator/kustomization.yaml
new file mode 100644
index 0000000..62de905
--- /dev/null
+++ b/config/operator/kustomization.yaml
@@ -0,0 +1,11 @@
+resources:
+- 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
diff --git a/config/operator/operator.yaml b/config/operator/operator.yaml
new file mode 100644
index 0000000..4129891
--- /dev/null
+++ b/config/operator/operator.yaml
@@ -0,0 +1,88 @@
+---
+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.5.0
+          args:
+          - --secure-listen-address=0.0.0.0:8443
+          - --upstream=http://127.0.0.1:8080/
+          - --logtostderr=true
+          - --v=10
+          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/operator/service-admin.yaml b/config/operator/service-admin.yaml
new file mode 100644
index 0000000..e7b6b33
--- /dev/null
+++ b/config/operator/service-admin.yaml
@@ -0,0 +1,18 @@
+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
\ No newline at end of file
diff --git a/config/operator/service-metrics.yaml b/config/operator/service-metrics.yaml
new file mode 100644
index 0000000..15cea5b
--- /dev/null
+++ b/config/operator/service-metrics.yaml
@@ -0,0 +1,18 @@
+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/rbac/kustomization.yaml b/config/rbac/kustomization.yaml
new file mode 100644
index 0000000..de028b8
--- /dev/null
+++ b/config/rbac/kustomization.yaml
@@ -0,0 +1,9 @@
+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/rbac/role.yaml b/config/rbac/role.yaml
new file mode 100644
index 0000000..c18b68a
--- /dev/null
+++ b/config/rbac/role.yaml
@@ -0,0 +1,97 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  creationTimestamp: null
+  name: keda-http-add-on
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  - endpoint
+  - endpoints
+  - pods
+  - services
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - watch
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - watch
+- apiGroups:
+  - coordination.k8s.io
+  resources:
+  - leases
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - update
+- apiGroups:
+  - http.keda.sh
+  resources:
+  - httpscaledobjects
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - http.keda.sh
+  resources:
+  - httpscaledobjects/status
+  verbs:
+  - get
+  - patch
+  - update
+- apiGroups:
+  - keda.sh
+  resources:
+  - scaledobjects
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - networking
+  resources:
+  - ingresses
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  creationTimestamp: null
+  name: keda-http-add-on
+  namespace: keda
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - '*'
diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml
new file mode 100644
index 0000000..b43fea4
--- /dev/null
+++ b/config/rbac/role_binding.yaml
@@ -0,0 +1,26 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: keda-http-add-on
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: keda-http-add-on
+subjects:
+- kind: ServiceAccount
+  name: keda-http-add-on
+  namespace: keda
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: keda-http-add-on
+  namespace: keda
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: keda-http-add-on
+subjects:
+- kind: ServiceAccount
+  name: keda-http-add-on
+  namespace: keda
diff --git a/config/scaler/kustomization.yaml b/config/scaler/kustomization.yaml
new file mode 100644
index 0000000..77ef9e5
--- /dev/null
+++ b/config/scaler/kustomization.yaml
@@ -0,0 +1,10 @@
+resources:
+- scaler.yaml
+- service.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/scaler.yaml b/config/scaler/scaler.yaml
new file mode 100644
index 0000000..be362a1
--- /dev/null
+++ b/config/scaler/scaler.yaml
@@ -0,0 +1,74 @@
+---
+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
+      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
new file mode 100644
index 0000000..690363d
--- /dev/null
+++ b/config/scaler/service.yaml
@@ -0,0 +1,21 @@
+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
+spec:
+  ports:
+  - name: grpc
+    port: 9090
+    targetPort: scaler-grpc
+  - name: health
+    port: 9091
+    targetPort: scaler-health
+  selector:
+    control-plane: external-scaler
+    app: keda-http-add-on
\ No newline at end of file
diff --git a/config/service_account/kustomization.yaml b/config/service_account/kustomization.yaml
new file mode 100644
index 0000000..4256b2b
--- /dev/null
+++ b/config/service_account/kustomization.yaml
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 0000000..7d2c683
--- /dev/null
+++ b/config/service_account/service_account.yaml
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000..3cf18f3
--- /dev/null
+++ b/config/shared/kustomization.yaml
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 0000000..532b6f3
--- /dev/null
+++ b/config/shared/routing-table.yaml
@@ -0,0 +1,14 @@
+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/docs/developing.md b/docs/developing.md
index d5e1840..4e08fa6 100644
--- a/docs/developing.md
+++ b/docs/developing.md
@@ -34,45 +34,29 @@ the service, you can host your own with a series of amazing tools like:
 
 Follow the [install instructions](./install.md) to check out how to install and get this add-on up and running.
 
-## Build scripts
-
-This project uses [Mage](https://magefile.org) as opposed to Make because it's way faster to build and push images, as well as to run tests and other common tasks. Please install [version v1.11.0](https://github.com/magefile/mage/releases/tag/v1.11.0) or above to have access to the task runner.
-
 ### In the Root Directory
 
-The Magefile located in the root directory has targets useful for the whole project. There is another magefile [in the operator directory](../operator/magefile.go), which has targets more specific to the operator module.
-
-The most useful and common commands from the root directory are listed below. Please see the "In the Operator Directory" section for the operator-specific targets. Whther you're in the root or the operator directory, you can always run the following general helper commands:
-
-- `mage -l`: shows a list of all available commands
-- `mage -h <command>`: shows command-specific details
-- `mage -h`: shows the general help
+The Makefile located in the root directory has targets useful for the whole project.
 
-> All commands are case insensitive, so `buildAll` and `buildall` are the same.
+> All commands are case sensitive.
 
-- `mage build`: Builds all the binaries for local testing.
-- `mage test`: Tests the entire codebase
-- `mage dockerBuild`: Builds all docker images
-  - Please see the below "Environment Variables" section for more information on this command
-- `mage dockerPush`: Pushes all docker images, without building them first
-  - Please see the below "Environment Variables" section for more information on this command
-
-### In the Operator Directory
-
-- `mage Manifests`: Builds all the manifest files for Kubernetes, it's important to build after every change
-  to a Kustomize annotation.
-- `mage All`: Generates the operator.
+- `make build`: Builds all the binaries for local testing
+- `make test`: Run all unit tests
+- `make e2e-test`: Run all e2e tests
+- `make docker-build`: Builds all docker images
+- `make docker-publish`: Build and push all Docker images
+- `make publish-multiarch`: Build and push all Docker images for `linux/arm64` and `linux/amd64`
+- `make manifests`: Generate all the manifest files for Kubernetes, it's important to build after every change
+- `make deploy`: Deploys the HTTP Add-on to the cluster selected in `~/.kube/config` using `config` folder manifests
 
 ### Required Environment Variables
 
-Some of the above commands require several environment variables to be set. You should set them once in your environment to ensure that you can run these targets. We recommend using [direnv](https://direnv.net) to set these environment variables once, so that you don't need to remember to do it.
-
-- `KEDAHTTP_SCALER_IMAGE`: the fully qualified name of the [scaler](../scaler) image. This is used to build, push, and install the scaler into a Kubernetes cluster (required)
-- `KEDAHTTP_INTERCEPTOR_IMAGE`: the fully qualified name of the [interceptor](../interceptor) image. This is used to build, push, and install the interceptor into a Kubernetes cluster (required)
-- `KEDAHTTP_OPERATOR_IMAGE`: the fully qualified name of the [operator](../operator) image. This is used to build, push, and install the operator into a Kubernetes cluster (required)
-- `KEDAHTTP_NAMESPACE`: the Kubernetes namespace to which to install the add-on and other required components (optional, defaults to `kedahttp`)
+Some of the above commands support changes in the default values: 
 
->Suffix any `*_IMAGE` variable with `<keda-git-sha>` and the build system will automatically replace it with `sha-$(git rev-parse --short HEAD)`
+- `IMAGE_REGISTRY`: Image registry to be used for docker images
+- `IMAGE_REPO`: Repository to be used for docker images
+- `VERSION`: Tag to be used for docker images
+- `BUILD_PLATFORMS`: Built platform targeted for multi-arch docker images
 
 ## Debugging and Observing Components
 
diff --git a/go.mod b/go.mod
index e8dc456..fd89738 100644
--- a/go.mod
+++ b/go.mod
@@ -3,11 +3,11 @@ module github.com/kedacore/http-add-on
 go 1.18
 
 require (
+	github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe
 	github.com/go-logr/logr v1.2.3
 	github.com/go-logr/zapr v1.2.3
 	github.com/golang/protobuf v1.5.2
 	github.com/kelseyhightower/envconfig v1.4.0
-	github.com/magefile/mage v1.13.0
 	github.com/onsi/ginkgo v1.16.5
 	github.com/onsi/gomega v1.20.2
 	github.com/pkg/errors v0.9.1
@@ -28,6 +28,7 @@ require (
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
+	github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/emicklei/go-restful v2.9.5+incompatible // indirect
 	github.com/evanphx/json-patch v4.12.0+incompatible // indirect
diff --git a/go.sum b/go.sum
index 5d3407e..7e2bdc8 100644
--- a/go.sum
+++ b/go.sum
@@ -99,6 +99,10 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
 github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
 github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
 github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
+github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
+github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe h1:69JI97HlzP+PH5Mi1thcGlDoBr6PS2Oe+l3mNmAkbs4=
+github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
@@ -325,8 +329,6 @@ 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=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
-github.com/magefile/mage v1.13.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
diff --git a/hack/verify-manifests.sh b/hack/verify-manifests.sh
new file mode 100755
index 0000000..b94f347
--- /dev/null
+++ b/hack/verify-manifests.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
+
+DIFFROOT="${SCRIPT_ROOT}/config"
+TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/config"
+_tmp="${SCRIPT_ROOT}/_tmp"
+
+cleanup() {
+  rm -rf "${_tmp}"
+}
+trap "cleanup" EXIT SIGINT
+
+cleanup
+
+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=$?
+cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}"
+if [[ $ret -eq 0 ]]
+then
+  echo "${DIFFROOT} up to date."
+else
+  echo "${DIFFROOT} is out of date. Please run 'make manifests'"
+  exit 1
+fi
diff --git a/interceptor/Dockerfile b/interceptor/Dockerfile
index 3ad89f8..e9d5132 100644
--- a/interceptor/Dockerfile
+++ b/interceptor/Dockerfile
@@ -1,13 +1,10 @@
-# adapted from Athens
-# https://github.com/gomods/athens/blob/main/cmd/proxy/Dockerfile
-ARG GOLANG_VERSION=1.18.3
-ARG GOARCH=amd64
-ARG GOOS=linux
-ARG VERSION="unset"
+# Build the adapter binary
+FROM --platform=$BUILDPLATFORM ghcr.io/kedacore/build-tools:1.18.8 as builder
 
-FROM golang:${GOLANG_VERSION}-alpine AS builder
+ARG VERSION=main
+ARG GIT_COMMIT=HEAD
 
-WORKDIR $GOPATH/src/github.com/kedacore/http-add-on
+WORKDIR /workspace
 
 COPY go.mod go.mod
 COPY go.sum go.sum
@@ -15,17 +12,18 @@ RUN go mod download
 
 COPY . .
 
-ENV GO111MODULE=on
-ENV CGO_ENABLED=0
-ENV GOOS=${GOOS}
-ENV GOARCH=${GOARCH}
-ENV GOPROXY="https://proxy.golang.org"
-RUN go build -ldflags "-X github.com/kedacore/http-add-on/pkg/build.version=${VERSION}" -a -o /bin/interceptor ./interceptor
+# Build
+# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
+ARG TARGETOS
+ARG TARGETARCH
+RUN VERSION=${VERSION} GIT_COMMIT=${GIT_COMMIT} TARGET_OS=$TARGETOS ARCH=$TARGETARCH make build-interceptor
+
 
 FROM gcr.io/distroless/static:nonroot
 WORKDIR /
-COPY --from=builder /bin/interceptor .
-USER nonroot:nonroot
+COPY --from=builder /workspace/bin/interceptor .
+# 65532 is numeric for nonroot
+USER 65532:65532
 EXPOSE 8080
 
 ENTRYPOINT ["/interceptor"]
diff --git a/interceptor/main.go b/interceptor/main.go
index 14eac2e..d1bca23 100644
--- a/interceptor/main.go
+++ b/interceptor/main.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/go-logr/logr"
 	"github.com/kedacore/http-add-on/interceptor/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"
 	pkglog "github.com/kedacore/http-add-on/pkg/log"
@@ -176,6 +177,7 @@ func main() {
 		lggr.Error(err, "proxy server failed")
 		return err
 	})
+	build.PrintComponentInfo(lggr, "Interceptor")
 
 	// errGrp.Wait() should hang forever for healthy admin and proxy servers.
 	// if it returns an error, log and exit immediately.
diff --git a/magefile.go b/magefile.go
deleted file mode 100644
index d451410..0000000
--- a/magefile.go
+++ /dev/null
@@ -1,462 +0,0 @@
-//go:build mage
-// +build mage
-
-package main
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/kedacore/http-add-on/pkg/build"
-	"github.com/kedacore/http-add-on/pkg/env"
-	"github.com/magefile/mage/mg"
-	"github.com/magefile/mage/sh"
-)
-
-// Note for Mac M1 users building Docker images:
-// If you want to build images for Linux (like, for example, an
-// AKS/GKE/EKS, DOKS cluster), you need to use docker's buildx driver
-// to do so. This would be the command to build the interceptor for
-// 64 Bit Amd and ARM platforms on Linux, for example:
-//
-//	docker buildx build --platform linux/amd64,linux/arm64 --push -t testingkeda.azurecr.io/interceptor:testing -f interceptor/Dockerfile .
-//
-// See
-// https://blog.jaimyn.dev/how-to-build-multi-architecture-docker-images-on-an-m1-mac/
-// for more details.
-
-// Global consts
-const (
-	DEFAULT_NAMESPACE      string = "kedahttp"
-	DEFAULT_CHART_LOCATION        = "kedacore/http-add-on"
-	DEFAULT_HTTP_SO_YAML          = "./examples/v0.3.0/httpscaledobject.yaml"
-
-	ACR_REGISTRY_NAME         = "KEDAHTTP_ACR_REGISTRY"
-	SCALER_IMAGE_ENV_VAR      = "KEDAHTTP_SCALER_IMAGE"
-	INTERCEPTOR_IMAGE_ENV_VAR = "KEDAHTTP_INTERCEPTOR_IMAGE"
-	OPERATOR_IMAGE_ENV_VAR    = "KEDAHTTP_OPERATOR_IMAGE"
-	NAMESPACE_ENV_VAR         = "KEDAHTTP_NAMESPACE"
-	CHART_LOCATION_ENV_VAR    = "KEDAHTTP_CHART_LOCATION"
-	HTTP_SO_YAML_ENV_VAR      = "KEDAHTTP_HTTP_SO_YAML"
-)
-
-type Scaler mg.Namespace
-
-// Generate Go build of the scaler binary
-func (Scaler) Build(ctx context.Context) error {
-	fmt.Println("Running scaler binary build")
-	buildFunc, err := build.GoBuild()
-	if err != nil {
-		return err
-	}
-	out, err := buildFunc("bin/scaler", "./scaler")
-	if err != nil {
-		return err
-	}
-	fmt.Println("Finished building scaler")
-	fmt.Println("Command Output: ", out)
-
-	return nil
-}
-
-// Run tests on the Scaler
-func (Scaler) Test(ctx context.Context) error {
-	fmt.Println("Running scaler tests")
-	testOutput, err := sh.Output("go", "test", "-v", "./scaler/...")
-	fmt.Println(testOutput)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (Scaler) DockerBuild(ctx context.Context) error {
-	img, err := build.GetImageName(SCALER_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerBuild(img, "scaler/Dockerfile", ".")
-}
-
-// Build the scaler docker image using ACR tasks.
-//
-// This command reads the value of the following environment variables:
-//
-// - KEDAHTTP_ACR_REGISTRY - for the value of the --registry flag
-// - KEDAHTTP_SCALER_IMAGE -- for the value of the --image flag
-//
-// it returns an error if either of the env vars are not set or they are and
-// the build fails.
-func (Scaler) DockerBuildACR(ctx context.Context) error {
-	registry, err := env.Get(ACR_REGISTRY_NAME)
-	if err != nil {
-		return err
-	}
-	image, err := build.GetImageName(SCALER_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerBuildACR(registry, image, "scaler/Dockerfile", ".")
-}
-
-func (Scaler) DockerPush(ctx context.Context) error {
-	image, err := build.GetImageName(SCALER_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerPush(image)
-}
-
-type Operator mg.Namespace
-
-// Generate Go build of the operator binary
-func (Operator) Build(ctx context.Context) error {
-	fmt.Println("Running operator binary build")
-	buildFunc, err := build.GoBuild()
-	if err != nil {
-		return err
-	}
-	out, err := buildFunc("bin/operator", "./operator")
-	if err != nil {
-		return err
-	}
-	fmt.Println("Finished building operator")
-	fmt.Println("Command Output: ", out)
-
-	return nil
-}
-
-// Run operator tests
-func (Operator) Test(ctx context.Context) error {
-	fmt.Println("Running operator tests")
-	testOutput, err := sh.Output("go", "test", "-v", "./operator/...")
-	if err != nil {
-		return err
-	}
-	fmt.Println(testOutput)
-
-	return nil
-}
-
-func (Operator) DockerBuild(ctx context.Context) error {
-	img, err := build.GetImageName(OPERATOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerBuild(img, "operator/Dockerfile", ".")
-}
-
-// Build the operator docker image using ACR tasks.
-//
-// This command reads the value of the following environment variables:
-//
-// - KEDAHTTP_ACR_REGISTRY - for the value of the --registry flag
-// - KEDAHTTP_INTERCEPTOR_IMAGE -- for the value of the --image flag
-//
-// it returns an error if either of the env vars are not set or they are and
-// the build fails.
-func (Operator) DockerBuildACR(ctx context.Context) error {
-	registry, err := env.Get(ACR_REGISTRY_NAME)
-	if err != nil {
-		return err
-	}
-	image, err := build.GetImageName(OPERATOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerBuildACR(registry, image, "operator/Dockerfile", ".")
-}
-
-func (Operator) DockerPush(ctx context.Context) error {
-	image, err := build.GetImageName(OPERATOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerPush(image)
-}
-
-type Interceptor mg.Namespace
-
-// Generate Go build of the interceptor binary
-func (Interceptor) Build(ctx context.Context) error {
-	fmt.Println("Running interceptor binary build")
-	buildFunc, err := build.GoBuild()
-	if err != nil {
-		return err
-	}
-	out, err := buildFunc("bin/interceptor", "./interceptor")
-	if err != nil {
-		return err
-	}
-	fmt.Println("Finished building interceptor")
-	fmt.Println("Command Output: ", out)
-
-	return nil
-}
-
-// Run interceptor tests
-func (Interceptor) Test(ctx context.Context) error {
-	fmt.Println("Running interceptor tests")
-	testOutput, err := sh.Output("go", "test", "-v", "./interceptor/...")
-	if err != nil {
-		return err
-	}
-	fmt.Println(testOutput)
-
-	return nil
-}
-
-// DockerBuild builds the interceptor docker image. It looks for the
-// KEDAHTTP_INTERCEPTOR_IMAGE environment variable and builds the image with
-// that as the name
-func (Interceptor) DockerBuild(ctx context.Context) error {
-	image, err := build.GetImageName(INTERCEPTOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerBuild(image, "interceptor/Dockerfile", ".")
-}
-
-// Build the interceptor docker image using ACR tasks.
-//
-// This command reads the value of the following environment variables:
-//
-// - KEDAHTTP_ACR_REGISTRY - for the value of the --registry flag
-// - KEDAHTTP_INTERCEPTOR_IMAGE -- for the value of the --image flag
-//
-// it returns an error if either of the env vars are not set or they are and
-// the build fails.
-func (Interceptor) DockerBuildACR(ctx context.Context) error {
-	registry, err := env.Get(ACR_REGISTRY_NAME)
-	if err != nil {
-		return err
-	}
-	image, err := build.GetImageName(INTERCEPTOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerBuildACR(registry, image, "interceptor/Dockerfile", ".")
-}
-
-func (Interceptor) DockerPush(ctx context.Context) error {
-	image, err := build.GetImageName(INTERCEPTOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	return build.DockerPush(image)
-}
-
-// Build all binaries
-func Build() {
-	fmt.Println("Building all binaries")
-	scaler := Scaler{}
-	interceptor := Interceptor{}
-	operator := Operator{}
-	mg.Deps(scaler.Build, operator.Build, interceptor.Build)
-}
-
-// Run tests on all the components in this project
-func Test() error {
-	return sh.RunV("go", "test", "-timeout=20s", "./...")
-}
-
-// --- Docker --- //
-
-// DockerBuild builds the operator, scaler and interceptor images in parallel
-func DockerBuild(ctx context.Context) error {
-	scaler, operator, interceptor := Scaler{}, Interceptor{}, Operator{}
-	mg.Deps(scaler.DockerBuild, operator.DockerBuild, interceptor.DockerBuild)
-	return nil
-}
-
-// DockerBuildACR builds the operator, scaler and interceptor images in parallel,
-// all using ACR tasks
-func DockerBuildACR(ctx context.Context) error {
-	scaler, operator, interceptor := Scaler{}, Interceptor{}, Operator{}
-	mg.Deps(scaler.DockerBuildACR, operator.DockerBuildACR, interceptor.DockerBuildACR)
-	return nil
-}
-
-// Pushes a given image name to a given repository
-func DockerPush(ctx context.Context) error {
-	scaler, operator, interceptor := Scaler{}, Interceptor{}, Operator{}
-	mg.Deps(scaler.DockerPush, operator.DockerPush, interceptor.DockerPush)
-	return nil
-}
-
-// --- Helm --- //
-
-// Upgrades or installs the Add-on onto the current cluster.
-// Issuing "mage helmupgradeoperator kedahttp kedacore" will download
-// "kedacore/http-add-on-operator:{SHA}" image and install along with the
-// interceptor and scaler images on the same SHA
-func UpgradeOperator(ctx context.Context) error {
-	namespace := env.GetOr(NAMESPACE_ENV_VAR, DEFAULT_NAMESPACE)
-
-	operatorImg, err := build.GetImageName(OPERATOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	scalerImg, err := build.GetImageName(SCALER_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-	interceptorImg, err := build.GetImageName(INTERCEPTOR_IMAGE_ENV_VAR)
-	if err != nil {
-		return err
-	}
-
-	chartLocation := env.GetOr(CHART_LOCATION_ENV_VAR, DEFAULT_CHART_LOCATION)
-
-	if err := sh.RunV(
-		"helm",
-		"upgrade",
-		"http-add-on",
-		chartLocation,
-		"--install",
-		"--namespace",
-		namespace,
-		"--create-namespace",
-		"--set",
-		fmt.Sprintf("images.operator=%s", operatorImg),
-		"--set",
-		fmt.Sprintf("images.scaler=%s", scalerImg),
-		"--set",
-		fmt.Sprintf("images.interceptor=%s", interceptorImg),
-	); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Deletes the operator release
-func DeleteOperator(ctx context.Context) error {
-	namespace, err := env.Get(NAMESPACE_ENV_VAR)
-	if err != nil {
-		namespace = DEFAULT_NAMESPACE
-	}
-	if err := sh.RunV("helm", "delete", "http-add-on", "-n", namespace); err != nil {
-		return err
-	}
-	return nil
-}
-
-// Installs or upgrades KEDA in the given namespace
-func InstallKeda(ctx context.Context) error {
-	namespace, err := env.Get(NAMESPACE_ENV_VAR)
-	if err != nil {
-		namespace = DEFAULT_NAMESPACE
-	}
-	if err := sh.RunV(
-		"helm",
-		"upgrade",
-		"keda",
-		"kedacore/keda",
-		"--install",
-		"--namespace",
-		namespace,
-		"--create-namespace",
-		"--set",
-		fmt.Sprintf("watchNamespace=%s", namespace),
-	); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Deletes the installed release of KEDA in the given namespaces
-func DeleteKeda(ctx context.Context) error {
-	namespace, err := env.Get(NAMESPACE_ENV_VAR)
-	if err != nil {
-		namespace = DEFAULT_NAMESPACE
-	}
-	if err := sh.RunV("helm", "delete", "keda", "-n", namespace); err != nil {
-		return err
-	}
-	return nil
-}
-
-func InstallXKCD(ctx context.Context) error {
-	namespace, err := env.Get(NAMESPACE_ENV_VAR)
-	if err != nil {
-		namespace = DEFAULT_NAMESPACE
-	}
-	if err := sh.RunV(
-		"helm",
-		"upgrade",
-		"xkcd",
-		"./examples/xkcd",
-		"--install",
-		"--namespace",
-		namespace,
-		"--create-namespace",
-	); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// --- Operator tasks --- //
-
-// Generates the operator
-func (Operator) Generate() error {
-	if err := sh.RunV("mage", "-d", "operator", "all"); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Rebuilds all manifests for the operator
-func (Operator) BuildManifests() error {
-	if err := sh.RunV("mage", "-d", "operator", "manifests"); err != nil {
-		return err
-	}
-	return nil
-}
-
-// --- Misc --- //
-
-// Generates protofiles for external scaler
-func (Scaler) GenerateProto() error {
-	if err := sh.RunV(
-		"protoc",
-		"--go_out",
-		".",
-		"--go_opt",
-		"paths=source_relative",
-		"--go-grpc_out",
-		".",
-		"--go-grpc_opt",
-		"paths=source_relative",
-		"proto/scaler.proto",
-	); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Create a new example HTTPScaledObject
-func NewHTTPSO(ctx context.Context, namespace string) error {
-	httpSODefinition := env.GetOr(HTTP_SO_YAML_ENV_VAR, DEFAULT_HTTP_SO_YAML)
-	return sh.RunV("kubectl", "create", "-f", httpSODefinition, "-n", namespace)
-}
-
-func ShowHTTPSO(ctx context.Context, namespace string) error {
-	return sh.RunV("kubectl", "get", "httpscaledobject", "-n", namespace)
-}
-
-// Delete the example HTTPScaledObject created from NewHTTPSO
-func DeleteHTTPSO(ctx context.Context, namespace string) error {
-	httpSODefinition := env.GetOr(HTTP_SO_YAML_ENV_VAR, DEFAULT_HTTP_SO_YAML)
-	return sh.RunV("kubectl", "delete", "-f", httpSODefinition, "-n", namespace)
-}
-
-func TestE2E(ctx context.Context) error {
-	return sh.RunV("go", "test", "-v", "./e2e...")
-}
diff --git a/operator/Dockerfile b/operator/Dockerfile
index f954430..f9cf93d 100644
--- a/operator/Dockerfile
+++ b/operator/Dockerfile
@@ -1,13 +1,10 @@
-# adapted from Athens 
-# https://github.com/gomods/athens/blob/main/cmd/proxy/Dockerfile
-ARG GOLANG_VERSION=1.18.3
-ARG GOARCH=amd64
-ARG GOOS=linux
-ARG VERSION="unset"
+# Build the adapter binary
+FROM --platform=$BUILDPLATFORM ghcr.io/kedacore/build-tools:1.18.8 as builder
 
-FROM golang:${GOLANG_VERSION}-alpine AS builder
+ARG VERSION=main
+ARG GIT_COMMIT=HEAD
 
-WORKDIR $GOPATH/src/github.com/kedacore/http-add-on
+WORKDIR /workspace
 
 COPY go.mod go.mod
 COPY go.sum go.sum
@@ -15,20 +12,18 @@ RUN go mod download
 
 COPY . .
 
-ENV GO111MODULE=on
-ENV CGO_ENABLED=0
-ENV GOOS=${GOOS}
-ENV GOARCH=${GOARCH}
-ENV GOPROXY="https://proxy.golang.org"
-
 # Build
-RUN go build -ldflags "-X github.com/kedacore/http-add-on/pkg/build.version=${VERSION}" -a -o /bin/operator ./operator
+# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
+ARG TARGETOS
+ARG TARGETARCH
+RUN VERSION=${VERSION} GIT_COMMIT=${GIT_COMMIT} TARGET_OS=$TARGETOS 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 /bin/operator .
-USER nonroot:nonroot
+COPY --from=builder /workspace/bin/operator .
+# 65532 is numeric for nonroot
+USER 65532:65532
 
 ENTRYPOINT ["/operator"]
diff --git a/operator/Makefile b/operator/Makefile
deleted file mode 100644
index 2cab8ec..0000000
--- a/operator/Makefile
+++ /dev/null
@@ -1,82 +0,0 @@
-
-# Image URL to use all building/pushing image targets
-IMG ?= "keda-http-addon-controller:latest"
-# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
-CRD_OPTIONS ?= "crd:trivialVersions=true"
-
-# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
-ifeq (,$(shell go env GOBIN))
-GOBIN=$(shell go env GOPATH)/bin
-else
-GOBIN=$(shell go env GOBIN)
-endif
-
-all: manager
-
-# Run tests
-test: generate fmt vet manifests
-	go test ./... -coverprofile cover.out
-
-# Build manager binary
-manager: generate fmt vet
-	go build -o bin/manager main.go
-
-# Run against the configured Kubernetes cluster in ~/.kube/config
-run: generate fmt vet manifests
-	go run ./main.go
-
-# Install CRDs into a cluster
-install: manifests
-	kustomize build config/crd | kubectl apply -f -
-
-# Uninstall CRDs from a cluster
-uninstall: manifests
-	kustomize build config/crd | kubectl delete -f -
-
-# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
-deploy: manifests set-image
-	kustomize build config/default | kubectl apply -f -
-
-set-image:
-	cd config/manager && kustomize edit set image controller=${IMG} && cd ..
-
-# Generate manifests e.g. CRD, RBAC etc.
-manifests: controller-gen
-	$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=keda-http-addon-manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
-
-# Run go fmt against code
-fmt:
-	go fmt ./...
-
-# Run go vet against code
-vet:
-	go vet ./...
-
-# Generate code
-generate: controller-gen
-	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
-
-# Build the docker image
-docker-build: test
-	docker build . -t ${IMG}
-
-# Push the docker image
-docker-push:
-	docker push ${IMG}
-
-# find or download controller-gen
-# download controller-gen if necessary
-controller-gen:
-ifeq (, $(shell which controller-gen))
-	@{ \
-	set -e ;\
-	CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
-	cd $$CONTROLLER_GEN_TMP_DIR ;\
-	go mod init tmp ;\
-	go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1 ;\
-	rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
-	}
-CONTROLLER_GEN=$(GOBIN)/controller-gen
-else
-CONTROLLER_GEN=$(shell which controller-gen)
-endif
diff --git a/operator/api/v1alpha1/httpscaledobject_types.go b/operator/api/v1alpha1/httpscaledobject_types.go
index de74652..4d8a0ae 100644
--- a/operator/api/v1alpha1/httpscaledobject_types.go
+++ b/operator/api/v1alpha1/httpscaledobject_types.go
@@ -99,9 +99,11 @@ type HTTPScaledObjectSpec struct {
 	//+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
-	CooldownPeriod int32 `json:"scaledownPeriod,omniempty" description:"Cooldown period (seconds) for resources to scale down(Default x)"`
+	//+optional
+	CooldownPeriod int32 `json:"scaledownPeriod,omitempty" description:"Cooldown period (seconds) for resources to scale down(Default x)"`
 }
 
 // ScaleTargetRef contains all the details about an HTTP application to scale and route to
diff --git a/operator/controllers/app.go b/operator/controllers/app.go
index b4a1e32..0fead35 100644
--- a/operator/controllers/app.go
+++ b/operator/controllers/app.go
@@ -14,6 +14,8 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+// +kubebuilder:rbac:groups="",namespace=keda,resources=configmaps,verbs="*"
+
 func removeApplicationResources(
 	ctx context.Context,
 	logger logr.Logger,
diff --git a/operator/magefile.go b/operator/magefile.go
deleted file mode 100644
index e9d4883..0000000
--- a/operator/magefile.go
+++ /dev/null
@@ -1,121 +0,0 @@
-//+build mage
-
-package main
-
-import (
-	"context"
-	"fmt"
-	"github.com/magefile/mage/mg"
-	"github.com/magefile/mage/sh"
-	"os"
-)
-
-const (
-	IMAGE_NAME    string = "keda-http-addon-controller:latest"
-	CRD_OPTIONS   string = "crd:trivialVersions=true"
-	RBAC_ROLENAME string = "keda-http-manager-role"
-)
-
-var CONTROLLER_GEN_PATH string = ""
-
-func controllerGen(ctx context.Context) error {
-	binaryName := "controller-gen"
-	genPath, genErr := sh.Output("which", binaryName)
-	if genErr != nil {
-		sh.RunV("./scripts/download-controller-gen.sh")
-		genPath, genErr = sh.Output("which", binaryName)
-		if genErr != nil {
-			return genErr
-		}
-	}
-	CONTROLLER_GEN_PATH = genPath
-	fmt.Printf("Controller gen path set to %q\n", CONTROLLER_GEN_PATH)
-	return nil
-}
-
-// Sets the image name for all manifests
-func SetImage(ctx context.Context, image string) error {
-	if image == "" {
-		image = IMAGE_NAME
-	}
-	return sh.RunV("./scripts/set-kustomize-image.sh", image)
-}
-
-// Creates and copies the chart to the chart directory
-func charts(ctx context.Context) error {
-	mg.SerialDeps(Manifests, mg.F(SetImage, ""))
-	chart, err := sh.Output(
-		"kustomize",
-		"build",
-		"config/default",
-	)
-	if err != nil {
-		return err
-	}
-
-	file, fErr := os.Create("../charts/keda-http-operator/templates/keda-http-operator.yml")
-	if fErr != nil {
-		return fErr
-	}
-	defer file.Close()
-
-	if _, wErr := file.WriteString(chart); wErr != nil {
-		return wErr
-	}
-
-	return nil
-}
-
-// Generates and formats the manager
-func Manager(ctx context.Context) {
-	mg.SerialDeps(Generate, Fmt, Vet)
-}
-
-// Runs go tests
-func Test(ctx context.Context) {
-	mg.SerialDeps(Generate, Fmt, Vet, Manifests)
-}
-
-// Runs the manager
-func Run(ctx context.Context) error {
-	mg.SerialDeps(Generate, Fmt, Vet, Manifests)
-	return sh.RunV("go", "run", "main.go")
-}
-
-// Runs go fmt in this directory
-func Fmt() error {
-	return sh.RunV("go", "fmt", "./...")
-}
-
-// Runs go vet in this directory
-func Vet() error {
-	return sh.RunV("go", "vet", "./...")
-}
-
-// Builds all the manifest files
-func Manifests() error {
-	mg.SerialDeps(controllerGen)
-	return sh.RunV(
-		CONTROLLER_GEN_PATH,
-		CRD_OPTIONS,
-		fmt.Sprintf("rbac:roleName=%q", RBAC_ROLENAME),
-		"webhook",
-		fmt.Sprintf("paths=%q", "./..."),
-		"output:crd:artifacts:config=config/crd/bases",
-	)
-}
-
-// Generates the controller
-func Generate() error {
-	mg.SerialDeps(controllerGen)
-	return sh.RunV(
-		CONTROLLER_GEN_PATH,
-		fmt.Sprintf("object:headerFile=%q", "hack/boilerplate.go.txt"),
-		fmt.Sprintf("paths=%q", "./..."),
-	)
-}
-
-// Creates the manager
-func All(ctx context.Context) {
-	mg.Deps(Manager)
-}
diff --git a/operator/main.go b/operator/main.go
index c8e34e7..6fbbaf2 100644
--- a/operator/main.go
+++ b/operator/main.go
@@ -35,6 +35,7 @@ import (
 	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"
+	"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"
 	"github.com/kedacore/http-add-on/pkg/routing"
@@ -162,7 +163,7 @@ func main() {
 			externalScalerCfg,
 		)
 	})
-
+	build.PrintComponentInfo(setupLog, "Operator")
 	setupLog.Error(errGrp.Wait(), "running the operator")
 }
 
diff --git a/pkg/build/build.go b/pkg/build/build.go
deleted file mode 100644
index 9253864..0000000
--- a/pkg/build/build.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package build
-
-import (
-	"fmt"
-
-	"github.com/magefile/mage/sh"
-)
-
-type Cmd = func(args ...string) (string, error)
-
-// GoBuild runs the go build command with the -ldflags flag and
-// the git sha of the current build
-func GoBuild() (Cmd, error) {
-	version, err := getGitSHA()
-	if err != nil {
-		return nil, err
-	}
-	return sh.OutCmd("go", "build", "-ldflags", fmt.Sprintf(`"-X github.com/kedacore/http-add-on/pkg/build.version=%s"`, version), "-o"), nil
-}
diff --git a/pkg/build/docker.go b/pkg/build/docker.go
deleted file mode 100644
index 071aa18..0000000
--- a/pkg/build/docker.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package build
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/kedacore/http-add-on/pkg/env"
-	"github.com/magefile/mage/sh"
-)
-
-const (
-	gitShaSuffix = "<keda-git-sha>"
-)
-
-func getGitSHA() (string, error) {
-	return sh.Output("git", "rev-parse", "--short", "HEAD")
-}
-
-// DockerBuild calls the following and returns the resulting error, if any:
-//
-//	docker build -t <image> -f <dockerfileLocation> <context>
-func DockerBuild(image, dockerfileLocation, context string) error {
-	return sh.RunV(
-		"docker",
-		"build",
-		"-t",
-		image,
-		"-f",
-		dockerfileLocation,
-		context,
-	)
-}
-
-func DockerBuildACR(registry, image, dockerfileLocation, context string) error {
-	return sh.RunV(
-		"az",
-		"acr",
-		"build",
-		"--image",
-		image,
-		"--registry",
-		registry,
-		"--file",
-		dockerfileLocation,
-		context,
-	)
-}
-
-// DockerPush calls the following and returns the resulting error, if any:
-//
-//	docker push <image>
-func DockerPush(image string) error {
-	return sh.RunV("docker", "push", image)
-}
-
-func GetImageName(envName string) (string, error) {
-	img, err := env.Get(envName)
-	if err != nil {
-		return "", err
-	}
-	if strings.HasSuffix(img, gitShaSuffix) {
-		sha, err := getGitSHA()
-		if err != nil {
-			return "", err
-		}
-		fmt.Println("using sha ", sha)
-		trimmed := strings.TrimSuffix(img, gitShaSuffix)
-		img = fmt.Sprintf("%ssha-%s", trimmed, sha)
-	}
-	return img, nil
-}
diff --git a/pkg/build/version.go b/pkg/build/version.go
index 8253dd8..0ca153d 100644
--- a/pkg/build/version.go
+++ b/pkg/build/version.go
@@ -1,8 +1,30 @@
 package build
 
-var version string
+import (
+	"fmt"
+	"runtime"
+
+	"github.com/go-logr/logr"
+)
+
+var (
+	version   = "main"
+	gitCommit string
+)
 
 // Version returns the current git SHA of commit the binary was built from
 func Version() string {
 	return version
 }
+
+// GitCommit stores the current commit hash
+func GitCommit() string {
+	return gitCommit
+}
+
+func PrintComponentInfo(logger logr.Logger, component string) {
+	logger.Info(fmt.Sprintf("%s Version: %s", component, Version()))
+	logger.Info(fmt.Sprintf("%s Commit: %s", component, GitCommit()))
+	logger.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
+	logger.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH))
+}
diff --git a/scaler/Dockerfile b/scaler/Dockerfile
index c4f5ea9..429cc85 100644
--- a/scaler/Dockerfile
+++ b/scaler/Dockerfile
@@ -1,14 +1,10 @@
-# taken from Athens
-# https://github.com/gomods/athens/blob/main/cmd/proxy/Dockerfile
-ARG GOLANG_VERSION=1.18.3
+# Build the adapter binary
+FROM --platform=$BUILDPLATFORM ghcr.io/kedacore/build-tools:1.18.8 as builder
 
-ARG GOARCH=amd64
-ARG GOOS=linux
-ARG VERSION="unset"
+ARG VERSION=main
+ARG GIT_COMMIT=HEAD
 
-FROM golang:${GOLANG_VERSION}-alpine AS builder
-
-WORKDIR $GOPATH/src/github.com/kedacore/http-add-on
+WORKDIR /workspace
 
 COPY go.mod go.mod
 COPY go.sum go.sum
@@ -16,17 +12,17 @@ RUN go mod download
 
 COPY . .
 
-ENV GO111MODULE=on
-ENV CGO_ENABLED=0
-ENV GOOS=${GOOS}
-ENV GOARCH=${GOARCH}
-ENV GOPROXY="https://proxy.golang.org"
-RUN go build -ldflags "-X github.com/kedacore/http-add-on/pkg/build.version=${VERSION}" -a -o /bin/scaler ./scaler
+# Build
+# https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
+ARG TARGETOS
+ARG TARGETARCH
+RUN VERSION=${VERSION} GIT_COMMIT=${GIT_COMMIT} TARGET_OS=$TARGETOS ARCH=$TARGETARCH make build-scaler
 
 FROM gcr.io/distroless/static:nonroot
 WORKDIR /
-COPY --from=builder /bin/scaler .
-USER nonroot:nonroot
+COPY --from=builder /workspace/bin/scaler .
+# 65532 is numeric for nonroot
+USER 65532:65532
 EXPOSE 8080
 
 ENTRYPOINT ["/scaler"]
diff --git a/scaler/main.go b/scaler/main.go
index 8a3a1dd..3ed8353 100644
--- a/scaler/main.go
+++ b/scaler/main.go
@@ -15,6 +15,7 @@ import (
 	"time"
 
 	"github.com/go-logr/logr"
+	"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"
 	pkglog "github.com/kedacore/http-add-on/pkg/log"
@@ -143,6 +144,7 @@ func main() {
 			table,
 		)
 	})
+	build.PrintComponentInfo(lggr, "Scaler")
 	lggr.Error(grp.Wait(), "one or more of the servers failed")
 }
 


Test patch:
diff --git a/.github/workflows/pr-e2e-tests.yaml b/.github/workflows/pr-e2e-tests.yaml
new file mode 100644
index 0000000..2b389dd
--- /dev/null
+++ b/.github/workflows/pr-e2e-tests.yaml
@@ -0,0 +1,58 @@
+name: e2e tests
+on:
+  push:
+    branches: [main]
+  pull_request:
+    branches: [main]
+
+jobs:
+  e2e_tests:
+    runs-on: ubuntu-latest
+    name: Execute e2e test on k8s-${{ matrix.kubernetesVersion }}
+    strategy:
+      fail-fast: false
+      matrix:
+        kubernetesVersion: [v1.26, v1.25, v1.24, v1.23]
+        include:
+        - kubernetesVersion: v1.26
+          kindImage: kindest/node:v1.26.0@sha256:691e24bd2417609db7e589e1a479b902d2e209892a10ce375fab60a8407c7352
+        - kubernetesVersion: v1.25
+          kindImage: kindest/node:v1.25.0@sha256:428aaa17ec82ccde0131cb2d1ca6547d13cf5fdabcc0bbecf749baa935387cbf
+        - kubernetesVersion: v1.24
+          kindImage: kindest/node:v1.24.4@sha256:adfaebada924a26c2c9308edd53c6e33b3d4e453782c0063dc0028bdebaddf98
+        - kubernetesVersion: v1.23
+          kindImage: kindest/node:v1.23.10@sha256:f047448af6a656fae7bc909e2fab360c18c487ef3edc93f06d78cdfd864b2d12
+
+    steps:
+      - name: Install prerequisites
+        run: |
+          sudo apt update
+          sudo apt install curl make ca-certificates gcc libc-dev -y
+        env:
+          DEBIAN_FRONTEND: noninteractive
+
+      - uses: actions/checkout@v3
+
+      - name: Helm install
+        uses: Azure/setup-helm@v3
+
+      - name: Create k8s ${{ matrix.kubernetesVersion }} Kind Cluster
+        uses: helm/kind-action@v1.4.0
+        with:
+          node_image: ${{ matrix.kindImage }}
+          cluster_name: e2e-tests-cluster
+
+      - name: Generate images and push to the cluster
+        run: |
+          make docker-build
+          kind load docker-image ghcr.io/kedacore/http-add-on-operator:${VERSION} --name e2e-tests-cluster
+          kind load docker-image ghcr.io/kedacore/http-add-on-interceptor:${VERSION} --name e2e-tests-cluster
+          kind load docker-image ghcr.io/kedacore/http-add-on-scaler:${VERSION} --name e2e-tests-cluster
+        env:
+          VERSION: e2e-test
+
+      - name: Run e2e test
+        run: |
+          make e2e-test
+        env:
+          VERSION: e2e-test
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index ffe0ba4..d1c9aef 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -1,4 +1,4 @@
-name: Run tests
+name: Validate
 on:
   push:
     branches: [main]
@@ -6,15 +6,49 @@ on:
     branches: [main]
 
 jobs:
-  run_tests:
-
-    runs-on: ubuntu-20.04
-
+  validate:
+    name: validate - ${{ matrix.name }}
+    runs-on: ${{ matrix.runner }}
+    container: ghcr.io/kedacore/build-tools:1.18.8
+    strategy:
+      matrix:
+        include:
+        - runner: ARM64
+          name: arm64
+        - runner: ubuntu-latest
+          name: amd64
     steps:
     - uses: actions/checkout@v3
-    - name: Install Go
-      uses: actions/setup-go@v3
+
+    - name: Register workspace path
+      run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
+
+    - name: Check go version
+      run: go version
+
+    - name: Manifests
+      run: make verify-manifests
+
+    - name: Set Go paths
+      id: go-paths
+      run: |
+        echo ::set-output name=mod_cache::$(go env GOMODCACHE)
+        echo ::set-output name=build_cache::$(go env GOCACHE)
+
+    - name: Go modules cache
+      uses: actions/cache@v3.2.2
+      with:
+        path: ${{ steps.go-paths.outputs.mod_cache }}
+        key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
+
+    - name: Go build cache
+      uses: actions/cache@v3.2.2
       with:
-        go-version: 1.17.2
+        path: ${{ steps.go-paths.outputs.build_cache }}
+        key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.sum') }}
+
+    - name: Build
+      run: ARCH=${{ matrix.name }} make build
+
     - name: Test
-      run: go test ./...
+      run: ARCH=${{ matrix.name }} make test
diff --git a/e2e/helm.go b/e2e/helm.go
index 1755ff5..64ef722 100644
--- a/e2e/helm.go
+++ b/e2e/helm.go
@@ -3,34 +3,34 @@ package e2e
 import (
 	"fmt"
 
-	"github.com/magefile/mage/sh"
+	"github.com/codeskyblue/go-sh"
 )
 
 func helmDelete(namespace, chartName string) error {
-	return sh.RunV(
+	return sh.Command(
 		"helm",
 		"delete",
 		"-n",
 		namespace,
 		chartName,
-	)
+	).Run()
 }
 
 func helmRepoAdd(name, url string) error {
-	return sh.RunV(
+	return sh.Command(
 		"helm",
 		"repo",
 		"add",
 		name,
 		url,
-	)
+	).Run()
 }
 func helmRepoUpdate() error {
-	return sh.RunV(
+	return sh.Command(
 		"helm",
 		"repo",
 		"update",
-	)
+	).Run()
 }
 
 func emptyHelmVars() map[string]string {
@@ -57,8 +57,8 @@ func helmInstall(
 			v,
 		))
 	}
-	return sh.RunV(
+	return sh.Command(
 		"helm",
-		helmArgs...,
-	)
+		helmArgs,
+	).Run()
 }
diff --git a/e2e/k8s.go b/e2e/k8s.go
index e940bd2..ebdc9a6 100644
--- a/e2e/k8s.go
+++ b/e2e/k8s.go
@@ -4,8 +4,8 @@ import (
 	"context"
 	"strconv"
 
+	"github.com/codeskyblue/go-sh"
 	"github.com/kedacore/http-add-on/pkg/k8s"
-	"github.com/magefile/mage/sh"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -31,7 +31,7 @@ func getClient() (
 }
 
 func deleteNS(ns string) error {
-	return sh.RunV("kubectl", "delete", "namespace", ns)
+	return sh.Command("kubectl", "delete", "namespace", ns).Run()
 }
 
 func getPortStrings(svc *corev1.Service) []string {
diff --git a/tests/e2e-test.sh b/tests/e2e-test.sh
index 2634184..d129654 100755
--- a/tests/e2e-test.sh
+++ b/tests/e2e-test.sh
@@ -1,20 +1,48 @@
 #!/bin/bash
 
-TAG=${TAG:-canary}
+function clear_resources(){
+  # Clear e2e resources
+  kubectl delete ns app
+
+  # Remove Http-add-on
+  make undeploy
+
+  # Remove KEDA
+  helm uninstall keda --namespace keda --wait
+}
+
+function print_logs {   
+  echo ">>> KEDA Operator log <<<"
+  kubectl logs -n keda -l app=keda-operator
+  printf "##############################################\n"
+  printf "##############################################\n"
+
+  echo ">>> HTTP Add-on Operator log <<<"
+  kubectl logs -n keda -l control-plane=controller-manager -c operator
+  printf "##############################################\n"
+  printf "##############################################\n"
+
+  echo ">>> HTTP Add-on Interceptor log <<<"
+  kubectl logs -n keda -l control-plane=interceptor
+  printf "##############################################\n"
+  printf "##############################################\n"
+
+    echo ">>> HTTP Add-on Scaler log <<<"
+  kubectl logs -n keda -l control-plane=external-scaler
+  printf "##############################################\n"
+  printf "##############################################\n"
+}
 
 # Install KEDA
 helm repo add kedacore https://kedacore.github.io/charts
 helm repo update
 helm upgrade --install keda kedacore/keda --namespace keda --create-namespace --wait
 
-# Show Kubernetes resources in keda namespace
-kubectl get all --namespace keda
-
 # Install Http-add-on
-helm upgrade --install http-add-on kedacore/keda-add-ons-http --set images.tag=$TAG --namespace keda --create-namespace --wait
+make deploy
 
-# Show Kubernetes resources in http-add-on namespace
-kubectl get all --namespace http-add-on
+# Show Kubernetes resources in keda namespace
+kubectl get all --namespace keda
 
 # Create resources
 kubectl create ns app
@@ -30,13 +58,16 @@ until [ "$n" -ge "$max" ]
 do
   ready=$(kubectl get so xkcd-app -n app -o jsonpath="{.status.conditions[0].status}")
   echo "ready: $ready"
-  if [ $ready == "True" ]; then
+  if [ "$ready" == "True" ]; then
     break
   fi
   n=$((n+1))
   sleep 15
 done
 if [ $n -eq $max ]; then
+  kubectl get all --namespace keda
+  print_logs
+  clear_resources
   echo "The ScaledObject is not working correctly"
   exit 1
 fi
@@ -75,7 +106,7 @@ spec:
       - name: curl-client
         image: curlimages/curl
         imagePullPolicy: Always
-        command: ["curl", "-H", "Host: myhost.com", "keda-add-ons-http-interceptor-proxy.keda:8080"]
+        command: ["curl", "-H", "Host: myhost.com", "keda-http-add-on-interceptor-proxy.keda:8080"]
       restartPolicy: Never
   activeDeadlineSeconds: 600
   backoffLimit: 5
@@ -94,11 +125,15 @@ do
   n=$((n+1))
   sleep 15
 done
+
 if [ $n -eq $max ]; then
+  print_logs
+  clear_resources
   echo "Current replica count is not 1"
   exit 1
 fi
 
-echo "The workflow is scaled to one"
+clear_resources
 
+echo "The workflow is scaled to one"
 echo "SUCCESS"