You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ka...@apache.org on 2020/01/23 06:50:14 UTC

[airflow-on-k8s-operator] branch master updated: Move to kubebuilder v2 generated project (#5)

This is an automated email from the ASF dual-hosted git repository.

kaxilnaik pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-on-k8s-operator.git


The following commit(s) were added to refs/heads/master by this push:
     new 4152ae7  Move to kubebuilder v2 generated project (#5)
4152ae7 is described below

commit 4152ae7e4653db6278284cb61a93dc38c3e2f600
Author: barney-s <64...@users.noreply.github.com>
AuthorDate: Wed Jan 22 22:50:06 2020 -0800

    Move to kubebuilder v2 generated project (#5)
---
 Dockerfile                                         |   36 +-
 Makefile                                           |   39 +-
 PROJECT                                            |   10 +
 .../airflow => api}/v1alpha1/airflowbase_types.go  |   18 +-
 .../v1alpha1/airflowcluster_types.go               |   18 +-
 .../doc.go => api/v1alpha1/groupversion_info.go    |   23 +-
 .../v1alpha1/zz_generated.deepcopy.go              |    0
 cmd/manager/main.go                                |   81 -
 config/certmanager/certificate.yaml                |   25 +
 config/certmanager/kustomization.yaml              |    5 +
 config/certmanager/kustomizeconfig.yaml            |   16 +
 .../crd/bases/airflow.apache.org_airflowbases.yaml | 1742 ++++++++++++++++++++
 .../bases/airflow.apache.org_airflowclusters.yaml  | 1621 ++++++++++++++++++
 config/crd/kustomization.yaml                      |   24 +
 config/crd/kustomizeconfig.yaml                    |   17 +
 .../crd/patches/cainjection_in_airflowbases.yaml   |    8 +
 .../patches/cainjection_in_airflowclusters.yaml    |    8 +
 config/crd/patches/webhook_in_airflowbases.yaml    |   17 +
 config/crd/patches/webhook_in_airflowclusters.yaml |   17 +
 config/crds/airflow_v1alpha1_airflowbase.yaml      |  217 ---
 config/crds/airflow_v1alpha1_airflowcluster.yaml   |  373 -----
 config/default/kustomization.yaml                  |   93 +-
 config/default/manager/manager.yaml                |   98 --
 config/default/manager_auth_proxy_patch.yaml       |   25 +
 config/default/manager_image_patch.yaml            |   37 -
 config/default/manager_webhook_patch.yaml          |   23 +
 config/default/rbac/rbac_role.yaml                 |  191 ---
 config/default/rbac/rbac_role_binding.yaml         |   29 -
 config/default/webhookcainjection_patch.yaml       |   15 +
 config/manager/kustomization.yaml                  |    2 +
 config/manager/manager.yaml                        |   39 +
 config/prometheus/kustomization.yaml               |    2 +
 config/prometheus/monitor.yaml                     |   15 +
 config/rbac/airflowbase_editor_role.yaml           |   26 +
 config/rbac/airflowbase_viewer_role.yaml           |   20 +
 config/rbac/airflowcluster_editor_role.yaml        |   26 +
 config/rbac/airflowcluster_viewer_role.yaml        |   20 +
 config/rbac/auth_proxy_role.yaml                   |   13 +
 config/rbac/auth_proxy_role_binding.yaml           |   12 +
 config/rbac/auth_proxy_service.yaml                |   14 +
 config/rbac/kustomization.yaml                     |   11 +
 config/rbac/leader_election_role.yaml              |   32 +
 config/rbac/leader_election_role_binding.yaml      |   12 +
 config/rbac/rbac_role.yaml                         |  191 ---
 config/rbac/rbac_role_binding.yaml                 |   29 -
 config/{crd/bases => rbac}/role.yaml               |   20 +-
 config/rbac/role_binding.yaml                      |   12 +
 config/samples/airflow_v1alpha1_airflowbase.yaml   |   20 +-
 .../samples/airflow_v1alpha1_airflowcluster.yaml   |   20 +-
 config/webhook/kustomization.yaml                  |    6 +
 config/webhook/kustomizeconfig.yaml                |   25 +
 config/{crd/bases => webhook}/manifests.yaml       |    0
 config/webhook/service.yaml                        |   12 +
 .../airflowbase_controller.go                      |   53 +-
 .../airflowcluster_controller.go                   |   87 +-
 .../application/application.go                     |    0
 .../application/application_suite_test.go          |    0
 .../application/application_test.go                |    2 +-
 {pkg/controller => controllers}/application/doc.go |    0
 {pkg/controller => controllers}/common/common.go   |    2 +-
 controllers/suite_test.go                          |   82 +
 docs/userguide.md                                  |    2 +-
 go.mod                                             |    3 +-
 go.sum                                             |   14 +-
 hack/sample/cloudsql-celery/base.yaml              |    2 +-
 hack/sample/cloudsql-celery/cluster.yaml           |    2 +-
 hack/sample/cloudsql-k8s/cluster.yaml              |    2 +-
 hack/sample/cloudsql-local/cluster.yaml            |    2 +-
 hack/sample/mysql-celery-gcs/cluster.yaml          |    2 +-
 hack/sample/mysql-celery/base.yaml                 |    2 +-
 hack/sample/mysql-celery/cluster.yaml              |    2 +-
 hack/sample/mysql-k8s/cluster.yaml                 |    2 +-
 hack/sample/mysql-local/cluster.yaml               |    2 +-
 .../postgres-celery-memorystore/cluster.yaml       |    2 +-
 hack/sample/postgres-celery-redis/cluster.yaml     |    2 +-
 hack/sample/postgres-celery/base.yaml              |    2 +-
 hack/sample/postgres-celery/cluster.yaml           |    2 +-
 hack/sample/postgres-k8s/cluster.yaml              |    2 +-
 hack/sample/postgres-local/cluster.yaml            |    2 +-
 main.go                                            |   90 +
 pkg/apis/addtoscheme_airflow_v1alpha1.go           |   25 -
 pkg/apis/airflow/group.go                          |   17 -
 .../airflow/v1alpha1/airflowbase_types_test.go     |   57 -
 .../airflow/v1alpha1/airflowcluster_types_test.go  |   57 -
 pkg/apis/airflow/v1alpha1/register.go              |   45 -
 pkg/apis/airflow/v1alpha1/v1alpha1_suite_test.go   |   54 -
 pkg/apis/apis.go                                   |   32 -
 pkg/apis/doc.go                                    |    2 -
 pkg/controller/add_airflowbase.go                  |   25 -
 pkg/controller/add_airflowcluster.go               |   25 -
 .../airflowbase_controller_suite_test.go           |   74 -
 .../airflowbase/airflowbase_controller_test.go     |   98 --
 .../airflowcluster_controller_suite_test.go        |   74 -
 .../airflowcluster_controller_test.go              |  133 --
 pkg/controller/controller.go                       |   33 -
 pkg/webhook/webhook.go                             |   36 -
 test/e2e/{ => base}/base_test.go                   |    4 +-
 test/e2e/{ => cluster}/cluster_test.go             |    4 +-
 test/e2e/{ => gcp}/gcp_test.go                     |    4 +-
 99 files changed, 4323 insertions(+), 2246 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 6ccc6b9..74eb9d7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,19 +1,27 @@
 # Build the manager binary
-FROM golang:1.10.3 as builder
+FROM golang:1.13 as builder
 
-# Copy in the go src
-WORKDIR /go/src/github.com/apache/airflow-on-k8s-operator
-COPY pkg/    pkg/
-COPY cmd/    cmd/
-COPY vendor/ vendor/
+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 the go source
+COPY main.go main.go
+COPY api/ api/
+COPY controllers/ controllers/
 
 # Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager github.com/apache/airflow-on-k8s-operator/cmd/manager
+RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
+
+# 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/manager .
+USER nonroot:nonroot
 
-# Copy the controller-manager into a thin image
-FROM ubuntu:latest
-WORKDIR /root/
-COPY --from=builder /go/src/github.com/apache/airflow-on-k8s-operator/manager .
-COPY templates/ templates/
-COPY config/crds/ crds/
-ENTRYPOINT ["./manager"]
+ENTRYPOINT ["/manager"]
diff --git a/Makefile b/Makefile
index b467ff6..bf4d856 100644
--- a/Makefile
+++ b/Makefile
@@ -14,22 +14,23 @@ GOBIN=$(shell go env GOBIN)
 endif
 
 # Image URL to use all building/pushing image targets
+IMG ?= controller:latest
+# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
+CRD_OPTIONS ?= "crd:trivialVersions=true"
 
 all: test manager
 
 # Run tests
 test: generate fmt vet manifests
-	ln -s ../../../templates/ pkg/controller/airflowbase/ || true
-	ln -s ../../../templates/ pkg/controller/airflowcluster/ || true
-	go test ./pkg/... ./cmd/... -coverprofile cover.out
+	go test ./controllers/... -coverprofile cover.out
 
 # Build manager binary
 manager: generate fmt vet
-	go build -o bin/manager github.com/apache/airflow-on-k8s-operator/cmd/manager
+	go build -o bin/manager main.go
 
 # Run against the configured Kubernetes cluster in ~/.kube/config
-run: generate fmt vet
-	go run ./cmd/manager/main.go
+run: generate fmt vet manifests
+	go run ./main.go
 
 # Run against the configured Kubernetes cluster in ~/.kube/config
 debug: generate fmt vet
@@ -37,11 +38,16 @@ debug: generate fmt vet
 
 # Install CRDs into a cluster
 install: manifests
-	kubectl apply -f config/crds
+	kustomize build config/crd | kubectl apply -f -
 	kubectl apply -f hack/appcrd.yaml
 
+# 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: install
+deploy: manifests
+	cd config/manager && kustomize edit set image controller=${IMG}
 	kustomize build config/default | kubectl apply -f -
 
 # Deploy controller in the configured Kubernetes cluster in ~/.kube/config
@@ -51,27 +57,26 @@ undeploy: manifests
 	kubectl delete -f hack/appcrd.yaml || true
 
 # Generate manifests e.g. CRD, RBAC etc.
-# TODO $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/..." output:crd:artifacts:config=config/crd/bases
 manifests: controller-gen
-	$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/..." output:artifacts:config=config/crd/bases
+	$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
 
 # Run go fmt against code
 fmt:
-	go fmt ./pkg/... ./cmd/...
+	go fmt ./...
 
 # Run go vet against code
 vet:
-	go vet ./pkg/... ./cmd/...
+	go vet ./...
 
 # Generate code
 generate: controller-gen
-	$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./pkg/..."
+	$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
 
 # Build the docker image
 docker-build: test
 	docker build . -t ${IMG}
 	@echo "updating kustomize image patch file for manager resource"
-	sed -i'' -e 's@image: .*@image: '"${IMG}"'@' ./config/default/manager_image_patch.yaml
+	cd config/manager && kustomize edit set image controller=${IMG}
 
 # Push the docker image
 docker-push: docker-build
@@ -80,13 +85,13 @@ docker-push: docker-build
 
 e2e-test:
 	kubectl get namespace airflowop-system || kubectl create namespace airflowop-system
-	go test -v -timeout 20m test/e2e/base_test.go --namespace airflowop-system
-	go test -v -timeout 20m test/e2e/cluster_test.go --namespace airflowop-system
+	go test -v -timeout 20m test/e2e/base/base_test.go --namespace airflowop-system
+	go test -v -timeout 20m test/e2e/cluster/cluster_test.go --namespace airflowop-system
 
 e2e-test-gcp:
 	kubectl get namespace airflowop-system || kubectl create namespace airflowop-system
 	kubectl apply -f hack/sample/cloudsql-celery/sqlproxy-secret.yaml -n airflowop-system
-	go test -v -timeout 20m test/e2e/gcp_test.go --namespace airflowop-system
+	go test -v -timeout 20m test/e2e/gcp/gcp_test.go --namespace airflowop-system
 
 
 # find or download controller-gen
diff --git a/PROJECT b/PROJECT
index e69de29..fe8cf22 100644
--- a/PROJECT
+++ b/PROJECT
@@ -0,0 +1,10 @@
+domain: apache.org
+repo: github.com/apache/airflow-on-k8s-operator
+resources:
+- group: airflow
+  kind: AirflowBase
+  version: v1alpha1
+- group: airflow
+  kind: AirflowCluster
+  version: v1alpha1
+version: "2"
diff --git a/pkg/apis/airflow/v1alpha1/airflowbase_types.go b/api/v1alpha1/airflowbase_types.go
similarity index 97%
rename from pkg/apis/airflow/v1alpha1/airflowbase_types.go
rename to api/v1alpha1/airflowbase_types.go
index 600fcdf..a680907 100644
--- a/pkg/apis/airflow/v1alpha1/airflowbase_types.go
+++ b/api/v1alpha1/airflowbase_types.go
@@ -50,15 +50,11 @@ const (
 	DatabaseSQLProxy       = "SQLProxy"
 )
 
-// +genclient
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
-
 // AirflowBase represents the components required for an Airflow scheduler and worker to
 // function. At a minimum they need a SQL service (MySQL or SQLProxy) and Airflow UI.
 // In addition for an installation with minimal external dependencies, NFS and Airflow UI
 // are also added.
-// +k8s:openapi-gen=true
-// +kubebuilder:resource:path=airflowbases
+// +kubebuilder:object:root=true
 type AirflowBase struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -131,8 +127,8 @@ type PostgresSpec struct {
 	Operator bool `json:"operator,omitempty"`
 	// Resources is the resource requests and limits for the pods.
 	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
-	// Options command line options for mysql
-	Options map[string]string
+	// Options command line options for postgres
+	Options map[string]string `json:"options,omitempty"`
 }
 
 func (s *PostgresSpec) validate(fp *field.Path) field.ErrorList {
@@ -178,7 +174,7 @@ type MySQLSpec struct {
 	// Resources is the resource requests and limits for the pods.
 	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
 	// Options command line options for mysql
-	Options map[string]string
+	Options map[string]string `json:"options,omitempty"`
 }
 
 func (s *MySQLSpec) validate(fp *field.Path) field.ErrorList {
@@ -511,8 +507,8 @@ func (b *AirflowBase) Validate() error {
 // OwnerRef returns owner ref object with the component's resource as owner
 func (b *AirflowBase) OwnerRef() *metav1.OwnerReference {
 	return metav1.NewControllerRef(b, schema.GroupVersionKind{
-		Group:   SchemeGroupVersion.Group,
-		Version: SchemeGroupVersion.Version,
+		Group:   GroupVersion.Group,
+		Version: GroupVersion.Version,
 		Kind:    "AirflowBase",
 	})
 }
@@ -546,7 +542,7 @@ func NewAirflowBase(name, namespace string, database string, storage bool) *Airf
 	return &b
 }
 
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +kubebuilder:object:root=true
 
 // AirflowBaseList contains a list of AirflowBase
 type AirflowBaseList struct {
diff --git a/pkg/apis/airflow/v1alpha1/airflowcluster_types.go b/api/v1alpha1/airflowcluster_types.go
similarity index 98%
rename from pkg/apis/airflow/v1alpha1/airflowcluster_types.go
rename to api/v1alpha1/airflowcluster_types.go
index 109ad1b..3232341 100644
--- a/pkg/apis/airflow/v1alpha1/airflowcluster_types.go
+++ b/api/v1alpha1/airflowcluster_types.go
@@ -321,9 +321,12 @@ func (s *DagSpec) validate(fp *field.Path) field.ErrorList {
 
 // SecretEnv secret env
 type SecretEnv struct {
-	Env    string
-	Secret string
-	Field  string
+	// Env - env variable name
+	Env string `json:"env,omitempty"`
+	// Secret - secret name
+	Secret string `json:"secret,omitempty"`
+	// Field - field name
+	Field string `json:"field,omitempty"`
 }
 
 // ClusterConfig is used to capture the config for Airflow
@@ -417,15 +420,11 @@ type AirflowClusterStatus struct {
 	status.ComponentMeta `json:",inline"`
 }
 
-// +genclient
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
-
 // AirflowCluster represents the Airflow Scheduler and workers for a single DAG folder
 // function. At a minimum they need a SQL service (MySQL or SQLProxy) and Airflow UI.
 // In addition for an installation with minimal external dependencies, NFS and Airflow UI
 // are also added.
-// +k8s:openapi-gen=true
-// +kubebuilder:resource:path=airflowclusters
+// +kubebuilder:object:root=true
 type AirflowCluster struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -602,9 +601,8 @@ func NewAirflowCluster(name, namespace, executor, base string, dags *DagSpec) *A
 	return &c
 }
 
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
-
 // AirflowClusterList contains a list of AirflowCluster
+// +kubebuilder:object:root=true
 type AirflowClusterList struct {
 	metav1.TypeMeta `json:",inline"`
 	metav1.ListMeta `json:"metadata,omitempty"`
diff --git a/pkg/apis/airflow/v1alpha1/doc.go b/api/v1alpha1/groupversion_info.go
similarity index 60%
rename from pkg/apis/airflow/v1alpha1/doc.go
rename to api/v1alpha1/groupversion_info.go
index 393cfc7..9ee23e5 100644
--- a/pkg/apis/airflow/v1alpha1/doc.go
+++ b/api/v1alpha1/groupversion_info.go
@@ -14,9 +14,22 @@
 // limitations under the License.
 
 // Package v1alpha1 contains API Schema definitions for the airflow v1alpha1 API group
-// +k8s:openapi-gen=true
-// +k8s:deepcopy-gen=package,register
-// +k8s:conversion-gen=github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow
-// +k8s:defaulter-gen=TypeMeta
-// +groupName=airflow.k8s.io
+// +kubebuilder:object:generate=true
+// +groupName=airflow.apache.org
 package v1alpha1
+
+import (
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"sigs.k8s.io/controller-runtime/pkg/scheme"
+)
+
+var (
+	// GroupVersion is group version used to register these objects
+	GroupVersion = schema.GroupVersion{Group: "airflow.apache.org", Version: "v1alpha1"}
+
+	// SchemeBuilder is used to add go types to the GroupVersionKind scheme
+	SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+
+	// AddToScheme adds the types in this group-version to the given scheme.
+	AddToScheme = SchemeBuilder.AddToScheme
+)
diff --git a/pkg/apis/airflow/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
similarity index 100%
rename from pkg/apis/airflow/v1alpha1/zz_generated.deepcopy.go
rename to api/v1alpha1/zz_generated.deepcopy.go
diff --git a/cmd/manager/main.go b/cmd/manager/main.go
deleted file mode 100644
index 666bb98..0000000
--- a/cmd/manager/main.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"os"
-
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis"
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller"
-	"github.com/apache/airflow-on-k8s-operator/pkg/webhook"
-	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
-	"sigs.k8s.io/controller-runtime/pkg/client/config"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
-	"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
-	"time"
-)
-
-func main() {
-	logf.SetLogger(logf.ZapLogger(false))
-	log := logf.Log.WithName("entrypoint")
-
-	// Get a config to talk to the apiserver
-	log.Info("setting up client for manager")
-	cfg, err := config.GetConfig()
-	if err != nil {
-		log.Error(err, "unable to set up client config")
-		os.Exit(1)
-	}
-
-	// Create a new Cmd to provide shared dependencies and start components
-	log.Info("setting up manager")
-	syncperiod := time.Minute * 2
-	mgr, err := manager.New(cfg, manager.Options{SyncPeriod: &syncperiod})
-	if err != nil {
-		log.Error(err, "unable to set up overall controller manager")
-		os.Exit(1)
-	}
-
-	log.Info("Registering Components.")
-
-	// Setup Scheme for all resources
-	log.Info("setting up scheme")
-	if err := apis.AddToScheme(mgr.GetScheme()); err != nil {
-		log.Error(err, "unable add APIs to scheme")
-		os.Exit(1)
-	}
-
-	// Setup all Controllers
-	log.Info("Setting up controller")
-	if err := controller.AddToManager(mgr); err != nil {
-		log.Error(err, "unable to register controllers to the manager")
-		os.Exit(1)
-	}
-
-	log.Info("setting up webhooks")
-	if err := webhook.AddToManager(mgr); err != nil {
-		log.Error(err, "unable to register webhooks to the manager")
-		os.Exit(1)
-	}
-
-	// Start the Cmd
-	log.Info("Starting the Cmd.")
-	if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
-		log.Error(err, "unable to run the manager")
-		os.Exit(1)
-	}
-}
diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml
new file mode 100644
index 0000000..237c937
--- /dev/null
+++ b/config/certmanager/certificate.yaml
@@ -0,0 +1,25 @@
+# The following manifests contain a self-signed issuer CR and a certificate CR.
+# More document can be found at https://docs.cert-manager.io
+# WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for breaking changes
+apiVersion: cert-manager.io/v1alpha2
+kind: Issuer
+metadata:
+  name: selfsigned-issuer
+  namespace: system
+spec:
+  selfSigned: {}
+---
+apiVersion: cert-manager.io/v1alpha2
+kind: Certificate
+metadata:
+  name: serving-cert  # this name should match the one appeared in kustomizeconfig.yaml
+  namespace: system
+spec:
+  # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
+  dnsNames:
+  - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
+  - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
+  issuerRef:
+    kind: Issuer
+    name: selfsigned-issuer
+  secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize
diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml
new file mode 100644
index 0000000..bebea5a
--- /dev/null
+++ b/config/certmanager/kustomization.yaml
@@ -0,0 +1,5 @@
+resources:
+- certificate.yaml
+
+configurations:
+- kustomizeconfig.yaml
diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml
new file mode 100644
index 0000000..90d7c31
--- /dev/null
+++ b/config/certmanager/kustomizeconfig.yaml
@@ -0,0 +1,16 @@
+# This configuration is for teaching kustomize how to update name ref and var substitution 
+nameReference:
+- kind: Issuer
+  group: cert-manager.io
+  fieldSpecs:
+  - kind: Certificate
+    group: cert-manager.io
+    path: spec/issuerRef/name
+
+varReference:
+- kind: Certificate
+  group: cert-manager.io
+  path: spec/commonName
+- kind: Certificate
+  group: cert-manager.io
+  path: spec/dnsNames
diff --git a/config/crd/bases/airflow.apache.org_airflowbases.yaml b/config/crd/bases/airflow.apache.org_airflowbases.yaml
new file mode 100644
index 0000000..488ac23
--- /dev/null
+++ b/config/crd/bases/airflow.apache.org_airflowbases.yaml
@@ -0,0 +1,1742 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.4
+  creationTimestamp: null
+  name: airflowbases.airflow.apache.org
+spec:
+  group: airflow.apache.org
+  names:
+    kind: AirflowBase
+    listKind: AirflowBaseList
+    plural: airflowbases
+    singular: airflowbase
+  scope: Namespaced
+  validation:
+    openAPIV3Schema:
+      description: AirflowBase represents the components required for an Airflow scheduler
+        and worker to function. At a minimum they need a SQL service (MySQL or SQLProxy)
+        and Airflow UI. In addition for an installation with minimal external dependencies,
+        NFS and Airflow UI are also added.
+      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: AirflowBaseSpec defines the desired state of AirflowBase
+          properties:
+            affinity:
+              description: Define scheduling constraints for pods.
+              properties:
+                nodeAffinity:
+                  description: Describes node affinity scheduling rules for the pod.
+                  properties:
+                    preferredDuringSchedulingIgnoredDuringExecution:
+                      description: The scheduler will prefer to schedule pods to nodes
+                        that satisfy the affinity expressions specified by this field,
+                        but it may choose a node that violates one or more of the
+                        expressions. The node that is most preferred is the one with
+                        the greatest sum of weights, i.e. for each node that meets
+                        all of the scheduling requirements (resource request, requiredDuringScheduling
+                        affinity expressions, etc.), compute a sum by iterating through
+                        the elements of this field and adding "weight" to the sum
+                        if the node matches the corresponding matchExpressions; the
+                        node(s) with the highest sum are the most preferred.
+                      items:
+                        description: An empty preferred scheduling term matches all
+                          objects with implicit weight 0 (i.e. it's a no-op). A null
+                          preferred scheduling term matches no objects (i.e. is also
+                          a no-op).
+                        properties:
+                          preference:
+                            description: A node selector term, associated with the
+                              corresponding weight.
+                            properties:
+                              matchExpressions:
+                                description: A list of node selector requirements
+                                  by node's labels.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchFields:
+                                description: A list of node selector requirements
+                                  by node's fields.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                            type: object
+                          weight:
+                            description: Weight associated with matching the corresponding
+                              nodeSelectorTerm, in the range 1-100.
+                            format: int32
+                            type: integer
+                        required:
+                        - preference
+                        - weight
+                        type: object
+                      type: array
+                    requiredDuringSchedulingIgnoredDuringExecution:
+                      description: If the affinity requirements specified by this
+                        field are not met at scheduling time, the pod will not be
+                        scheduled onto the node. If the affinity requirements specified
+                        by this field cease to be met at some point during pod execution
+                        (e.g. due to an update), the system may or may not try to
+                        eventually evict the pod from its node.
+                      properties:
+                        nodeSelectorTerms:
+                          description: Required. A list of node selector terms. The
+                            terms are ORed.
+                          items:
+                            description: A null or empty node selector term matches
+                              no objects. The requirements of them are ANDed. The
+                              TopologySelectorTerm type implements a subset of the
+                              NodeSelectorTerm.
+                            properties:
+                              matchExpressions:
+                                description: A list of node selector requirements
+                                  by node's labels.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchFields:
+                                description: A list of node selector requirements
+                                  by node's fields.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                            type: object
+                          type: array
+                      required:
+                      - nodeSelectorTerms
+                      type: object
+                  type: object
+                podAffinity:
+                  description: Describes pod affinity scheduling rules (e.g. co-locate
+                    this pod in the same node, zone, etc. as some other pod(s)).
+                  properties:
+                    preferredDuringSchedulingIgnoredDuringExecution:
+                      description: The scheduler will prefer to schedule pods to nodes
+                        that satisfy the affinity expressions specified by this field,
+                        but it may choose a node that violates one or more of the
+                        expressions. The node that is most preferred is the one with
+                        the greatest sum of weights, i.e. for each node that meets
+                        all of the scheduling requirements (resource request, requiredDuringScheduling
+                        affinity expressions, etc.), compute a sum by iterating through
+                        the elements of this field and adding "weight" to the sum
+                        if the node has pods which matches the corresponding podAffinityTerm;
+                        the node(s) with the highest sum are the most preferred.
+                      items:
+                        description: The weights of all of the matched WeightedPodAffinityTerm
+                          fields are added per-node to find the most preferred node(s)
+                        properties:
+                          podAffinityTerm:
+                            description: Required. A pod affinity term, associated
+                              with the corresponding weight.
+                            properties:
+                              labelSelector:
+                                description: A label query over a set of resources,
+                                  in this case pods.
+                                properties:
+                                  matchExpressions:
+                                    description: matchExpressions is a list of label
+                                      selector requirements. The requirements are
+                                      ANDed.
+                                    items:
+                                      description: A label selector requirement is
+                                        a selector that contains values, a key, and
+                                        an operator that relates the key and values.
+                                      properties:
+                                        key:
+                                          description: key is the label key that the
+                                            selector applies to.
+                                          type: string
+                                        operator:
+                                          description: operator represents a key's
+                                            relationship to a set of values. Valid
+                                            operators are In, NotIn, Exists and DoesNotExist.
+                                          type: string
+                                        values:
+                                          description: values is an array of string
+                                            values. If the operator is In or NotIn,
+                                            the values array must be non-empty. If
+                                            the operator is Exists or DoesNotExist,
+                                            the values array must be empty. This array
+                                            is replaced during a strategic merge patch.
+                                          items:
+                                            type: string
+                                          type: array
+                                      required:
+                                      - key
+                                      - operator
+                                      type: object
+                                    type: array
+                                  matchLabels:
+                                    additionalProperties:
+                                      type: string
+                                    description: matchLabels is a map of {key,value}
+                                      pairs. A single {key,value} in the matchLabels
+                                      map is equivalent to an element of matchExpressions,
+                                      whose key field is "key", the operator is "In",
+                                      and the values array contains only "value".
+                                      The requirements are ANDed.
+                                    type: object
+                                type: object
+                              namespaces:
+                                description: namespaces specifies which namespaces
+                                  the labelSelector applies to (matches against);
+                                  null or empty list means "this pod's namespace"
+                                items:
+                                  type: string
+                                type: array
+                              topologyKey:
+                                description: This pod should be co-located (affinity)
+                                  or not co-located (anti-affinity) with the pods
+                                  matching the labelSelector in the specified namespaces,
+                                  where co-located is defined as running on a node
+                                  whose value of the label with key topologyKey matches
+                                  that of any node on which any of the selected pods
+                                  is running. Empty topologyKey is not allowed.
+                                type: string
+                            required:
+                            - topologyKey
+                            type: object
+                          weight:
+                            description: weight associated with matching the corresponding
+                              podAffinityTerm, in the range 1-100.
+                            format: int32
+                            type: integer
+                        required:
+                        - podAffinityTerm
+                        - weight
+                        type: object
+                      type: array
+                    requiredDuringSchedulingIgnoredDuringExecution:
+                      description: If the affinity requirements specified by this
+                        field are not met at scheduling time, the pod will not be
+                        scheduled onto the node. If the affinity requirements specified
+                        by this field cease to be met at some point during pod execution
+                        (e.g. due to a pod label update), the system may or may not
+                        try to eventually evict the pod from its node. When there
+                        are multiple elements, the lists of nodes corresponding to
+                        each podAffinityTerm are intersected, i.e. all terms must
+                        be satisfied.
+                      items:
+                        description: Defines a set of pods (namely those matching
+                          the labelSelector relative to the given namespace(s)) that
+                          this pod should be co-located (affinity) or not co-located
+                          (anti-affinity) with, where co-located is defined as running
+                          on a node whose value of the label with key <topologyKey>
+                          matches that of any node on which a pod of the set of pods
+                          is running
+                        properties:
+                          labelSelector:
+                            description: A label query over a set of resources, in
+                              this case pods.
+                            properties:
+                              matchExpressions:
+                                description: matchExpressions is a list of label selector
+                                  requirements. The requirements are ANDed.
+                                items:
+                                  description: A label selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: key is the label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: operator represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists and DoesNotExist.
+                                      type: string
+                                    values:
+                                      description: values is an array of string values.
+                                        If the operator is In or NotIn, the values
+                                        array must be non-empty. If the operator is
+                                        Exists or DoesNotExist, the values array must
+                                        be empty. This array is replaced during a
+                                        strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchLabels:
+                                additionalProperties:
+                                  type: string
+                                description: matchLabels is a map of {key,value} pairs.
+                                  A single {key,value} in the matchLabels map is equivalent
+                                  to an element of matchExpressions, whose key field
+                                  is "key", the operator is "In", and the values array
+                                  contains only "value". The requirements are ANDed.
+                                type: object
+                            type: object
+                          namespaces:
+                            description: namespaces specifies which namespaces the
+                              labelSelector applies to (matches against); null or
+                              empty list means "this pod's namespace"
+                            items:
+                              type: string
+                            type: array
+                          topologyKey:
+                            description: This pod should be co-located (affinity)
+                              or not co-located (anti-affinity) with the pods matching
+                              the labelSelector in the specified namespaces, where
+                              co-located is defined as running on a node whose value
+                              of the label with key topologyKey matches that of any
+                              node on which any of the selected pods is running. Empty
+                              topologyKey is not allowed.
+                            type: string
+                        required:
+                        - topologyKey
+                        type: object
+                      type: array
+                  type: object
+                podAntiAffinity:
+                  description: Describes pod anti-affinity scheduling rules (e.g.
+                    avoid putting this pod in the same node, zone, etc. as some other
+                    pod(s)).
+                  properties:
+                    preferredDuringSchedulingIgnoredDuringExecution:
+                      description: The scheduler will prefer to schedule pods to nodes
+                        that satisfy the anti-affinity expressions specified by this
+                        field, but it may choose a node that violates one or more
+                        of the expressions. The node that is most preferred is the
+                        one with the greatest sum of weights, i.e. for each node that
+                        meets all of the scheduling requirements (resource request,
+                        requiredDuringScheduling anti-affinity expressions, etc.),
+                        compute a sum by iterating through the elements of this field
+                        and adding "weight" to the sum if the node has pods which
+                        matches the corresponding podAffinityTerm; the node(s) with
+                        the highest sum are the most preferred.
+                      items:
+                        description: The weights of all of the matched WeightedPodAffinityTerm
+                          fields are added per-node to find the most preferred node(s)
+                        properties:
+                          podAffinityTerm:
+                            description: Required. A pod affinity term, associated
+                              with the corresponding weight.
+                            properties:
+                              labelSelector:
+                                description: A label query over a set of resources,
+                                  in this case pods.
+                                properties:
+                                  matchExpressions:
+                                    description: matchExpressions is a list of label
+                                      selector requirements. The requirements are
+                                      ANDed.
+                                    items:
+                                      description: A label selector requirement is
+                                        a selector that contains values, a key, and
+                                        an operator that relates the key and values.
+                                      properties:
+                                        key:
+                                          description: key is the label key that the
+                                            selector applies to.
+                                          type: string
+                                        operator:
+                                          description: operator represents a key's
+                                            relationship to a set of values. Valid
+                                            operators are In, NotIn, Exists and DoesNotExist.
+                                          type: string
+                                        values:
+                                          description: values is an array of string
+                                            values. If the operator is In or NotIn,
+                                            the values array must be non-empty. If
+                                            the operator is Exists or DoesNotExist,
+                                            the values array must be empty. This array
+                                            is replaced during a strategic merge patch.
+                                          items:
+                                            type: string
+                                          type: array
+                                      required:
+                                      - key
+                                      - operator
+                                      type: object
+                                    type: array
+                                  matchLabels:
+                                    additionalProperties:
+                                      type: string
+                                    description: matchLabels is a map of {key,value}
+                                      pairs. A single {key,value} in the matchLabels
+                                      map is equivalent to an element of matchExpressions,
+                                      whose key field is "key", the operator is "In",
+                                      and the values array contains only "value".
+                                      The requirements are ANDed.
+                                    type: object
+                                type: object
+                              namespaces:
+                                description: namespaces specifies which namespaces
+                                  the labelSelector applies to (matches against);
+                                  null or empty list means "this pod's namespace"
+                                items:
+                                  type: string
+                                type: array
+                              topologyKey:
+                                description: This pod should be co-located (affinity)
+                                  or not co-located (anti-affinity) with the pods
+                                  matching the labelSelector in the specified namespaces,
+                                  where co-located is defined as running on a node
+                                  whose value of the label with key topologyKey matches
+                                  that of any node on which any of the selected pods
+                                  is running. Empty topologyKey is not allowed.
+                                type: string
+                            required:
+                            - topologyKey
+                            type: object
+                          weight:
+                            description: weight associated with matching the corresponding
+                              podAffinityTerm, in the range 1-100.
+                            format: int32
+                            type: integer
+                        required:
+                        - podAffinityTerm
+                        - weight
+                        type: object
+                      type: array
+                    requiredDuringSchedulingIgnoredDuringExecution:
+                      description: If the anti-affinity requirements specified by
+                        this field are not met at scheduling time, the pod will not
+                        be scheduled onto the node. If the anti-affinity requirements
+                        specified by this field cease to be met at some point during
+                        pod execution (e.g. due to a pod label update), the system
+                        may or may not try to eventually evict the pod from its node.
+                        When there are multiple elements, the lists of nodes corresponding
+                        to each podAffinityTerm are intersected, i.e. all terms must
+                        be satisfied.
+                      items:
+                        description: Defines a set of pods (namely those matching
+                          the labelSelector relative to the given namespace(s)) that
+                          this pod should be co-located (affinity) or not co-located
+                          (anti-affinity) with, where co-located is defined as running
+                          on a node whose value of the label with key <topologyKey>
+                          matches that of any node on which a pod of the set of pods
+                          is running
+                        properties:
+                          labelSelector:
+                            description: A label query over a set of resources, in
+                              this case pods.
+                            properties:
+                              matchExpressions:
+                                description: matchExpressions is a list of label selector
+                                  requirements. The requirements are ANDed.
+                                items:
+                                  description: A label selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: key is the label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: operator represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists and DoesNotExist.
+                                      type: string
+                                    values:
+                                      description: values is an array of string values.
+                                        If the operator is In or NotIn, the values
+                                        array must be non-empty. If the operator is
+                                        Exists or DoesNotExist, the values array must
+                                        be empty. This array is replaced during a
+                                        strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchLabels:
+                                additionalProperties:
+                                  type: string
+                                description: matchLabels is a map of {key,value} pairs.
+                                  A single {key,value} in the matchLabels map is equivalent
+                                  to an element of matchExpressions, whose key field
+                                  is "key", the operator is "In", and the values array
+                                  contains only "value". The requirements are ANDed.
+                                type: object
+                            type: object
+                          namespaces:
+                            description: namespaces specifies which namespaces the
+                              labelSelector applies to (matches against); null or
+                              empty list means "this pod's namespace"
+                            items:
+                              type: string
+                            type: array
+                          topologyKey:
+                            description: This pod should be co-located (affinity)
+                              or not co-located (anti-affinity) with the pods matching
+                              the labelSelector in the specified namespaces, where
+                              co-located is defined as running on a node whose value
+                              of the label with key topologyKey matches that of any
+                              node on which any of the selected pods is running. Empty
+                              topologyKey is not allowed.
+                            type: string
+                        required:
+                        - topologyKey
+                        type: object
+                      type: array
+                  type: object
+              type: object
+            annotations:
+              additionalProperties:
+                type: string
+              description: Custom annotations to be added to the pods.
+              type: object
+            labels:
+              additionalProperties:
+                type: string
+              description: Custom labels to be added to the pods.
+              type: object
+            mysql:
+              description: Spec for MySQL component.
+              properties:
+                backup:
+                  description: Spec defining the Backup Custom Resource to be handled
+                    by MySQLOperator Ignored when Operator is False
+                  properties:
+                    schedule:
+                      description: Schedule is the cron string used to schedule backup
+                      type: string
+                    storage:
+                      description: Storage has the s3 compatible storage spec
+                      properties:
+                        config:
+                          additionalProperties:
+                            type: string
+                          description: Config is generic string based key-value map
+                            that defines non-secret configuration values for uploading
+                            the backup to storage w.r.t the configured storage provider.
+                          type: object
+                        secretRef:
+                          description: SecretRef is a reference to the Kubernetes
+                            secret containing the configuration for uploading the
+                            backup to authenticated storage.
+                          properties:
+                            name:
+                              description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                TODO: Add other useful fields. apiVersion, kind, uid?'
+                              type: string
+                          type: object
+                        storageprovider:
+                          description: Provider is the storage type used for backup
+                            and restore e.g. s3, oci-s3-compat, aws-s3, gce-s3, etc.
+                          type: string
+                      required:
+                      - storageprovider
+                      type: object
+                  required:
+                  - schedule
+                  - storage
+                  type: object
+                backupVolumeClaimTemplate:
+                  description: BackupVolumeClaimTemplate allows a user to specify
+                    a volume to temporarily store the data for a backup prior to it
+                    being shipped to object storage.
+                  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:
+                      description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
+                      type: object
+                    spec:
+                      description: 'Spec defines the desired characteristics of a
+                        volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the desired access modes
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        dataSource:
+                          description: This field requires the VolumeSnapshotDataSource
+                            alpha feature gate to be enabled and currently VolumeSnapshot
+                            is the only supported data source. If the provisioner
+                            can support VolumeSnapshot data source, it will create
+                            a new volume and data will be restored to the volume at
+                            the same time. If the provisioner does not support VolumeSnapshot
+                            data source, volume will not be created and the failure
+                            will be reported as an event. In the future, we plan to
+                            support more data source types and the behavior of the
+                            provisioner may change.
+                          properties:
+                            apiGroup:
+                              description: APIGroup is the group for the resource
+                                being referenced. If APIGroup is not specified, the
+                                specified Kind must be in the core API group. For
+                                any other third-party types, APIGroup is required.
+                              type: string
+                            kind:
+                              description: Kind is the type of resource being referenced
+                              type: string
+                            name:
+                              description: Name is the name of resource being referenced
+                              type: string
+                          required:
+                          - kind
+                          - name
+                          type: object
+                        resources:
+                          description: 'Resources represents the minimum resources
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                          properties:
+                            limits:
+                              additionalProperties:
+                                type: string
+                              description: 'Limits describes the maximum amount of
+                                compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                            requests:
+                              additionalProperties:
+                                type: string
+                              description: 'Requests describes the minimum amount
+                                of compute resources required. If Requests is omitted
+                                for a container, it defaults to Limits if that is
+                                explicitly specified, otherwise to an implementation-defined
+                                value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                          type: object
+                        selector:
+                          description: A label query over volumes to consider for
+                            binding.
+                          properties:
+                            matchExpressions:
+                              description: matchExpressions is a list of label selector
+                                requirements. The requirements are ANDed.
+                              items:
+                                description: A label selector requirement is a selector
+                                  that contains values, a key, and an operator that
+                                  relates the key and values.
+                                properties:
+                                  key:
+                                    description: key is the label key that the selector
+                                      applies to.
+                                    type: string
+                                  operator:
+                                    description: operator represents a key's relationship
+                                      to a set of values. Valid operators are In,
+                                      NotIn, Exists and DoesNotExist.
+                                    type: string
+                                  values:
+                                    description: values is an array of string values.
+                                      If the operator is In or NotIn, the values array
+                                      must be non-empty. If the operator is Exists
+                                      or DoesNotExist, the values array must be empty.
+                                      This array is replaced during a strategic merge
+                                      patch.
+                                    items:
+                                      type: string
+                                    type: array
+                                required:
+                                - key
+                                - operator
+                                type: object
+                              type: array
+                            matchLabels:
+                              additionalProperties:
+                                type: string
+                              description: matchLabels is a map of {key,value} pairs.
+                                A single {key,value} in the matchLabels map is equivalent
+                                to an element of matchExpressions, whose key field
+                                is "key", the operator is "In", and the values array
+                                contains only "value". The requirements are ANDed.
+                              type: object
+                          type: object
+                        storageClassName:
+                          description: 'Name of the StorageClass required by the claim.
+                            More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                          type: string
+                        volumeMode:
+                          description: volumeMode defines what type of volume is required
+                            by the claim. Value of Filesystem is implied when not
+                            included in claim spec. This is a beta feature.
+                          type: string
+                        volumeName:
+                          description: VolumeName is the binding reference to the
+                            PersistentVolume backing this claim.
+                          type: string
+                      type: object
+                    status:
+                      description: 'Status represents the current information/status
+                        of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the actual access modes
+                            the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        capacity:
+                          additionalProperties:
+                            type: string
+                          description: Represents the actual resources of the underlying
+                            volume.
+                          type: object
+                        conditions:
+                          description: Current Condition of persistent volume claim.
+                            If underlying persistent volume is being resized then
+                            the Condition will be set to 'ResizeStarted'.
+                          items:
+                            description: PersistentVolumeClaimCondition contails details
+                              about state of pvc
+                            properties:
+                              lastProbeTime:
+                                description: Last time we probed the condition.
+                                format: date-time
+                                type: string
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: Human-readable message indicating details
+                                  about last transition.
+                                type: string
+                              reason:
+                                description: Unique, this should be a short, machine
+                                  understandable string that gives the reason for
+                                  condition's last transition. If it reports "ResizeStarted"
+                                  that means the underlying persistent volume is being
+                                  resized.
+                                type: string
+                              status:
+                                type: string
+                              type:
+                                description: PersistentVolumeClaimConditionType is
+                                  a valid value of PersistentVolumeClaimCondition.Type
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                        phase:
+                          description: Phase represents the current phase of PersistentVolumeClaim.
+                          type: string
+                      type: object
+                  type: object
+                image:
+                  description: Image defines the MySQL Docker image name
+                  type: string
+                operator:
+                  description: Flag when True generates MySQLOperator CustomResource
+                    to be handled by MySQL Operator If False, a StatefulSet with 1
+                    replica is created (not for production setups)
+                  type: boolean
+                options:
+                  additionalProperties:
+                    type: string
+                  description: Options command line options for mysql
+                  type: object
+                replicas:
+                  description: Replicas defines the number of running MySQL instances
+                    in a cluster
+                  format: int32
+                  type: integer
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the MySQL Docker image version
+                  type: string
+                volumeClaimTemplate:
+                  description: VolumeClaimTemplate allows a user to specify volume
+                    claim for MySQL Server files
+                  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:
+                      description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
+                      type: object
+                    spec:
+                      description: 'Spec defines the desired characteristics of a
+                        volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the desired access modes
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        dataSource:
+                          description: This field requires the VolumeSnapshotDataSource
+                            alpha feature gate to be enabled and currently VolumeSnapshot
+                            is the only supported data source. If the provisioner
+                            can support VolumeSnapshot data source, it will create
+                            a new volume and data will be restored to the volume at
+                            the same time. If the provisioner does not support VolumeSnapshot
+                            data source, volume will not be created and the failure
+                            will be reported as an event. In the future, we plan to
+                            support more data source types and the behavior of the
+                            provisioner may change.
+                          properties:
+                            apiGroup:
+                              description: APIGroup is the group for the resource
+                                being referenced. If APIGroup is not specified, the
+                                specified Kind must be in the core API group. For
+                                any other third-party types, APIGroup is required.
+                              type: string
+                            kind:
+                              description: Kind is the type of resource being referenced
+                              type: string
+                            name:
+                              description: Name is the name of resource being referenced
+                              type: string
+                          required:
+                          - kind
+                          - name
+                          type: object
+                        resources:
+                          description: 'Resources represents the minimum resources
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                          properties:
+                            limits:
+                              additionalProperties:
+                                type: string
+                              description: 'Limits describes the maximum amount of
+                                compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                            requests:
+                              additionalProperties:
+                                type: string
+                              description: 'Requests describes the minimum amount
+                                of compute resources required. If Requests is omitted
+                                for a container, it defaults to Limits if that is
+                                explicitly specified, otherwise to an implementation-defined
+                                value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                          type: object
+                        selector:
+                          description: A label query over volumes to consider for
+                            binding.
+                          properties:
+                            matchExpressions:
+                              description: matchExpressions is a list of label selector
+                                requirements. The requirements are ANDed.
+                              items:
+                                description: A label selector requirement is a selector
+                                  that contains values, a key, and an operator that
+                                  relates the key and values.
+                                properties:
+                                  key:
+                                    description: key is the label key that the selector
+                                      applies to.
+                                    type: string
+                                  operator:
+                                    description: operator represents a key's relationship
+                                      to a set of values. Valid operators are In,
+                                      NotIn, Exists and DoesNotExist.
+                                    type: string
+                                  values:
+                                    description: values is an array of string values.
+                                      If the operator is In or NotIn, the values array
+                                      must be non-empty. If the operator is Exists
+                                      or DoesNotExist, the values array must be empty.
+                                      This array is replaced during a strategic merge
+                                      patch.
+                                    items:
+                                      type: string
+                                    type: array
+                                required:
+                                - key
+                                - operator
+                                type: object
+                              type: array
+                            matchLabels:
+                              additionalProperties:
+                                type: string
+                              description: matchLabels is a map of {key,value} pairs.
+                                A single {key,value} in the matchLabels map is equivalent
+                                to an element of matchExpressions, whose key field
+                                is "key", the operator is "In", and the values array
+                                contains only "value". The requirements are ANDed.
+                              type: object
+                          type: object
+                        storageClassName:
+                          description: 'Name of the StorageClass required by the claim.
+                            More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                          type: string
+                        volumeMode:
+                          description: volumeMode defines what type of volume is required
+                            by the claim. Value of Filesystem is implied when not
+                            included in claim spec. This is a beta feature.
+                          type: string
+                        volumeName:
+                          description: VolumeName is the binding reference to the
+                            PersistentVolume backing this claim.
+                          type: string
+                      type: object
+                    status:
+                      description: 'Status represents the current information/status
+                        of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the actual access modes
+                            the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        capacity:
+                          additionalProperties:
+                            type: string
+                          description: Represents the actual resources of the underlying
+                            volume.
+                          type: object
+                        conditions:
+                          description: Current Condition of persistent volume claim.
+                            If underlying persistent volume is being resized then
+                            the Condition will be set to 'ResizeStarted'.
+                          items:
+                            description: PersistentVolumeClaimCondition contails details
+                              about state of pvc
+                            properties:
+                              lastProbeTime:
+                                description: Last time we probed the condition.
+                                format: date-time
+                                type: string
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: Human-readable message indicating details
+                                  about last transition.
+                                type: string
+                              reason:
+                                description: Unique, this should be a short, machine
+                                  understandable string that gives the reason for
+                                  condition's last transition. If it reports "ResizeStarted"
+                                  that means the underlying persistent volume is being
+                                  resized.
+                                type: string
+                              status:
+                                type: string
+                              type:
+                                description: PersistentVolumeClaimConditionType is
+                                  a valid value of PersistentVolumeClaimCondition.Type
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                        phase:
+                          description: Phase represents the current phase of PersistentVolumeClaim.
+                          type: string
+                      type: object
+                  type: object
+              type: object
+            nodeSelector:
+              additionalProperties:
+                type: string
+              description: Selector for fitting pods to nodes whose labels match the
+                selector. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+              type: object
+            postgres:
+              description: 'PostgresSpec defines the attributes and desired state
+                of Postgres Component TODO - minimum spec needed .. for now it is
+                version: "" need to consider empty mysql'
+              properties:
+                image:
+                  description: Image defines the Postgres Docker image name
+                  type: string
+                operator:
+                  description: Flag when True generates PostgresOperator CustomResource
+                    to be handled by Postgres Operator If False, a StatefulSet with
+                    1 replica is created (not for production setups)
+                  type: boolean
+                options:
+                  additionalProperties:
+                    type: string
+                  description: Options command line options for postgres
+                  type: object
+                replicas:
+                  description: Replicas defines the number of running Postgres instances
+                    in a cluster
+                  format: int32
+                  type: integer
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the Postgres Docker image version
+                  type: string
+                volumeClaimTemplate:
+                  description: VolumeClaimTemplate allows a user to specify volume
+                    claim for Postgres Server files
+                  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:
+                      description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
+                      type: object
+                    spec:
+                      description: 'Spec defines the desired characteristics of a
+                        volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the desired access modes
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        dataSource:
+                          description: This field requires the VolumeSnapshotDataSource
+                            alpha feature gate to be enabled and currently VolumeSnapshot
+                            is the only supported data source. If the provisioner
+                            can support VolumeSnapshot data source, it will create
+                            a new volume and data will be restored to the volume at
+                            the same time. If the provisioner does not support VolumeSnapshot
+                            data source, volume will not be created and the failure
+                            will be reported as an event. In the future, we plan to
+                            support more data source types and the behavior of the
+                            provisioner may change.
+                          properties:
+                            apiGroup:
+                              description: APIGroup is the group for the resource
+                                being referenced. If APIGroup is not specified, the
+                                specified Kind must be in the core API group. For
+                                any other third-party types, APIGroup is required.
+                              type: string
+                            kind:
+                              description: Kind is the type of resource being referenced
+                              type: string
+                            name:
+                              description: Name is the name of resource being referenced
+                              type: string
+                          required:
+                          - kind
+                          - name
+                          type: object
+                        resources:
+                          description: 'Resources represents the minimum resources
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                          properties:
+                            limits:
+                              additionalProperties:
+                                type: string
+                              description: 'Limits describes the maximum amount of
+                                compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                            requests:
+                              additionalProperties:
+                                type: string
+                              description: 'Requests describes the minimum amount
+                                of compute resources required. If Requests is omitted
+                                for a container, it defaults to Limits if that is
+                                explicitly specified, otherwise to an implementation-defined
+                                value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                          type: object
+                        selector:
+                          description: A label query over volumes to consider for
+                            binding.
+                          properties:
+                            matchExpressions:
+                              description: matchExpressions is a list of label selector
+                                requirements. The requirements are ANDed.
+                              items:
+                                description: A label selector requirement is a selector
+                                  that contains values, a key, and an operator that
+                                  relates the key and values.
+                                properties:
+                                  key:
+                                    description: key is the label key that the selector
+                                      applies to.
+                                    type: string
+                                  operator:
+                                    description: operator represents a key's relationship
+                                      to a set of values. Valid operators are In,
+                                      NotIn, Exists and DoesNotExist.
+                                    type: string
+                                  values:
+                                    description: values is an array of string values.
+                                      If the operator is In or NotIn, the values array
+                                      must be non-empty. If the operator is Exists
+                                      or DoesNotExist, the values array must be empty.
+                                      This array is replaced during a strategic merge
+                                      patch.
+                                    items:
+                                      type: string
+                                    type: array
+                                required:
+                                - key
+                                - operator
+                                type: object
+                              type: array
+                            matchLabels:
+                              additionalProperties:
+                                type: string
+                              description: matchLabels is a map of {key,value} pairs.
+                                A single {key,value} in the matchLabels map is equivalent
+                                to an element of matchExpressions, whose key field
+                                is "key", the operator is "In", and the values array
+                                contains only "value". The requirements are ANDed.
+                              type: object
+                          type: object
+                        storageClassName:
+                          description: 'Name of the StorageClass required by the claim.
+                            More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                          type: string
+                        volumeMode:
+                          description: volumeMode defines what type of volume is required
+                            by the claim. Value of Filesystem is implied when not
+                            included in claim spec. This is a beta feature.
+                          type: string
+                        volumeName:
+                          description: VolumeName is the binding reference to the
+                            PersistentVolume backing this claim.
+                          type: string
+                      type: object
+                    status:
+                      description: 'Status represents the current information/status
+                        of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the actual access modes
+                            the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        capacity:
+                          additionalProperties:
+                            type: string
+                          description: Represents the actual resources of the underlying
+                            volume.
+                          type: object
+                        conditions:
+                          description: Current Condition of persistent volume claim.
+                            If underlying persistent volume is being resized then
+                            the Condition will be set to 'ResizeStarted'.
+                          items:
+                            description: PersistentVolumeClaimCondition contails details
+                              about state of pvc
+                            properties:
+                              lastProbeTime:
+                                description: Last time we probed the condition.
+                                format: date-time
+                                type: string
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: Human-readable message indicating details
+                                  about last transition.
+                                type: string
+                              reason:
+                                description: Unique, this should be a short, machine
+                                  understandable string that gives the reason for
+                                  condition's last transition. If it reports "ResizeStarted"
+                                  that means the underlying persistent volume is being
+                                  resized.
+                                type: string
+                              status:
+                                type: string
+                              type:
+                                description: PersistentVolumeClaimConditionType is
+                                  a valid value of PersistentVolumeClaimCondition.Type
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                        phase:
+                          description: Phase represents the current phase of PersistentVolumeClaim.
+                          type: string
+                      type: object
+                  type: object
+              type: object
+            sqlproxy:
+              description: SQLProxySpec defines the attributes to deploy SQL Proxy
+                component
+              properties:
+                image:
+                  description: Image defines the SQLProxy Docker image name
+                  type: string
+                instance:
+                  description: Instance defines the SQL instance name
+                  type: string
+                project:
+                  description: 'example: myProject:us-central1:myInstance=tcp:3306
+                    Project defines the SQL instance project'
+                  type: string
+                region:
+                  description: Region defines the SQL instance region
+                  type: string
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                type:
+                  description: Type defines the SQL instance type
+                  type: string
+                version:
+                  description: Version defines the SQL Proxy docker image version.
+                  type: string
+              required:
+              - instance
+              - project
+              - region
+              - type
+              type: object
+            storage:
+              description: Spec for NFS component.
+              properties:
+                image:
+                  description: Image defines the NFS Docker image.
+                  type: string
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the NFS Server Docker image version.
+                  type: string
+                volumeClaimTemplate:
+                  description: Volume allows a user to specify volume claim template
+                    to be used for fileserver
+                  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:
+                      description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
+                      type: object
+                    spec:
+                      description: 'Spec defines the desired characteristics of a
+                        volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the desired access modes
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        dataSource:
+                          description: This field requires the VolumeSnapshotDataSource
+                            alpha feature gate to be enabled and currently VolumeSnapshot
+                            is the only supported data source. If the provisioner
+                            can support VolumeSnapshot data source, it will create
+                            a new volume and data will be restored to the volume at
+                            the same time. If the provisioner does not support VolumeSnapshot
+                            data source, volume will not be created and the failure
+                            will be reported as an event. In the future, we plan to
+                            support more data source types and the behavior of the
+                            provisioner may change.
+                          properties:
+                            apiGroup:
+                              description: APIGroup is the group for the resource
+                                being referenced. If APIGroup is not specified, the
+                                specified Kind must be in the core API group. For
+                                any other third-party types, APIGroup is required.
+                              type: string
+                            kind:
+                              description: Kind is the type of resource being referenced
+                              type: string
+                            name:
+                              description: Name is the name of resource being referenced
+                              type: string
+                          required:
+                          - kind
+                          - name
+                          type: object
+                        resources:
+                          description: 'Resources represents the minimum resources
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                          properties:
+                            limits:
+                              additionalProperties:
+                                type: string
+                              description: 'Limits describes the maximum amount of
+                                compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                            requests:
+                              additionalProperties:
+                                type: string
+                              description: 'Requests describes the minimum amount
+                                of compute resources required. If Requests is omitted
+                                for a container, it defaults to Limits if that is
+                                explicitly specified, otherwise to an implementation-defined
+                                value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                          type: object
+                        selector:
+                          description: A label query over volumes to consider for
+                            binding.
+                          properties:
+                            matchExpressions:
+                              description: matchExpressions is a list of label selector
+                                requirements. The requirements are ANDed.
+                              items:
+                                description: A label selector requirement is a selector
+                                  that contains values, a key, and an operator that
+                                  relates the key and values.
+                                properties:
+                                  key:
+                                    description: key is the label key that the selector
+                                      applies to.
+                                    type: string
+                                  operator:
+                                    description: operator represents a key's relationship
+                                      to a set of values. Valid operators are In,
+                                      NotIn, Exists and DoesNotExist.
+                                    type: string
+                                  values:
+                                    description: values is an array of string values.
+                                      If the operator is In or NotIn, the values array
+                                      must be non-empty. If the operator is Exists
+                                      or DoesNotExist, the values array must be empty.
+                                      This array is replaced during a strategic merge
+                                      patch.
+                                    items:
+                                      type: string
+                                    type: array
+                                required:
+                                - key
+                                - operator
+                                type: object
+                              type: array
+                            matchLabels:
+                              additionalProperties:
+                                type: string
+                              description: matchLabels is a map of {key,value} pairs.
+                                A single {key,value} in the matchLabels map is equivalent
+                                to an element of matchExpressions, whose key field
+                                is "key", the operator is "In", and the values array
+                                contains only "value". The requirements are ANDed.
+                              type: object
+                          type: object
+                        storageClassName:
+                          description: 'Name of the StorageClass required by the claim.
+                            More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                          type: string
+                        volumeMode:
+                          description: volumeMode defines what type of volume is required
+                            by the claim. Value of Filesystem is implied when not
+                            included in claim spec. This is a beta feature.
+                          type: string
+                        volumeName:
+                          description: VolumeName is the binding reference to the
+                            PersistentVolume backing this claim.
+                          type: string
+                      type: object
+                    status:
+                      description: 'Status represents the current information/status
+                        of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the actual access modes
+                            the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        capacity:
+                          additionalProperties:
+                            type: string
+                          description: Represents the actual resources of the underlying
+                            volume.
+                          type: object
+                        conditions:
+                          description: Current Condition of persistent volume claim.
+                            If underlying persistent volume is being resized then
+                            the Condition will be set to 'ResizeStarted'.
+                          items:
+                            description: PersistentVolumeClaimCondition contails details
+                              about state of pvc
+                            properties:
+                              lastProbeTime:
+                                description: Last time we probed the condition.
+                                format: date-time
+                                type: string
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: Human-readable message indicating details
+                                  about last transition.
+                                type: string
+                              reason:
+                                description: Unique, this should be a short, machine
+                                  understandable string that gives the reason for
+                                  condition's last transition. If it reports "ResizeStarted"
+                                  that means the underlying persistent volume is being
+                                  resized.
+                                type: string
+                              status:
+                                type: string
+                              type:
+                                description: PersistentVolumeClaimConditionType is
+                                  a valid value of PersistentVolumeClaimCondition.Type
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                        phase:
+                          description: Phase represents the current phase of PersistentVolumeClaim.
+                          type: string
+                      type: object
+                  type: object
+              type: object
+          type: object
+        status:
+          description: AirflowBaseStatus defines the observed state of AirflowBase
+          properties:
+            components:
+              description: Object status array for all matching objects
+              items:
+                description: ObjectStatus is a generic status holder for objects
+                properties:
+                  group:
+                    description: Object group
+                    type: string
+                  kind:
+                    description: Kind of object
+                    type: string
+                  link:
+                    description: Link to object
+                    type: string
+                  name:
+                    description: Name of object
+                    type: string
+                  pdb:
+                    description: PDB status
+                    properties:
+                      currenthealthy:
+                        description: currentHealthy
+                        format: int32
+                        type: integer
+                      desiredhealthy:
+                        description: desiredHealthy
+                        format: int32
+                        type: integer
+                    required:
+                    - currenthealthy
+                    - desiredhealthy
+                    type: object
+                  status:
+                    description: 'Status. Values: InProgress, Ready, Unknown'
+                    type: string
+                  sts:
+                    description: StatefulSet status
+                    properties:
+                      currentcount:
+                        description: CurrentReplicas defines the no of MySQL instances
+                          that are created
+                        format: int32
+                        type: integer
+                      progress:
+                        description: 'progress is a fuzzy indicator. Interpret as
+                          a percentage (0-100) eg: for statefulsets, progress = 100*readyreplicas/replicas'
+                        format: int32
+                        type: integer
+                      readycount:
+                        description: ReadyReplicas defines the no of MySQL instances
+                          that are ready
+                        format: int32
+                        type: integer
+                      replicas:
+                        description: Replicas defines the no of MySQL instances desired
+                        format: int32
+                        type: integer
+                    required:
+                    - currentcount
+                    - progress
+                    - readycount
+                    - replicas
+                    type: object
+                type: object
+              type: array
+            conditions:
+              description: Conditions represents the latest state of the object
+              items:
+                description: Condition describes the state of an object at a certain
+                  point.
+                properties:
+                  lastTransitionTime:
+                    description: Last time the condition transitioned from one status
+                      to another.
+                    format: date-time
+                    type: string
+                  lastUpdateTime:
+                    description: Last time the condition was probed
+                    format: date-time
+                    type: string
+                  message:
+                    description: A human readable message indicating details about
+                      the transition.
+                    type: string
+                  reason:
+                    description: The reason for the condition's last transition.
+                    type: string
+                  status:
+                    description: Status of the condition, one of True, False, Unknown.
+                    type: string
+                  type:
+                    description: Type of condition.
+                    type: string
+                required:
+                - status
+                - type
+                type: object
+              type: array
+            observedGeneration:
+              description: ObservedGeneration is the most recent generation observed.
+                It corresponds to the Object's generation, which is updated on mutation
+                by the API Server.
+              format: int64
+              type: integer
+          type: object
+      type: object
+  version: v1alpha1
+  versions:
+  - name: v1alpha1
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/config/crd/bases/airflow.apache.org_airflowclusters.yaml b/config/crd/bases/airflow.apache.org_airflowclusters.yaml
new file mode 100644
index 0000000..71cc63b
--- /dev/null
+++ b/config/crd/bases/airflow.apache.org_airflowclusters.yaml
@@ -0,0 +1,1621 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.4
+  creationTimestamp: null
+  name: airflowclusters.airflow.apache.org
+spec:
+  group: airflow.apache.org
+  names:
+    kind: AirflowCluster
+    listKind: AirflowClusterList
+    plural: airflowclusters
+    singular: airflowcluster
+  scope: Namespaced
+  validation:
+    openAPIV3Schema:
+      description: AirflowCluster represents the Airflow Scheduler and workers for
+        a single DAG folder function. At a minimum they need a SQL service (MySQL
+        or SQLProxy) and Airflow UI. In addition for an installation with minimal
+        external dependencies, NFS and Airflow UI are also added.
+      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: AirflowClusterSpec defines the desired state of AirflowCluster
+          properties:
+            affinity:
+              description: Define scheduling constraints for pods.
+              properties:
+                nodeAffinity:
+                  description: Describes node affinity scheduling rules for the pod.
+                  properties:
+                    preferredDuringSchedulingIgnoredDuringExecution:
+                      description: The scheduler will prefer to schedule pods to nodes
+                        that satisfy the affinity expressions specified by this field,
+                        but it may choose a node that violates one or more of the
+                        expressions. The node that is most preferred is the one with
+                        the greatest sum of weights, i.e. for each node that meets
+                        all of the scheduling requirements (resource request, requiredDuringScheduling
+                        affinity expressions, etc.), compute a sum by iterating through
+                        the elements of this field and adding "weight" to the sum
+                        if the node matches the corresponding matchExpressions; the
+                        node(s) with the highest sum are the most preferred.
+                      items:
+                        description: An empty preferred scheduling term matches all
+                          objects with implicit weight 0 (i.e. it's a no-op). A null
+                          preferred scheduling term matches no objects (i.e. is also
+                          a no-op).
+                        properties:
+                          preference:
+                            description: A node selector term, associated with the
+                              corresponding weight.
+                            properties:
+                              matchExpressions:
+                                description: A list of node selector requirements
+                                  by node's labels.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchFields:
+                                description: A list of node selector requirements
+                                  by node's fields.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                            type: object
+                          weight:
+                            description: Weight associated with matching the corresponding
+                              nodeSelectorTerm, in the range 1-100.
+                            format: int32
+                            type: integer
+                        required:
+                        - preference
+                        - weight
+                        type: object
+                      type: array
+                    requiredDuringSchedulingIgnoredDuringExecution:
+                      description: If the affinity requirements specified by this
+                        field are not met at scheduling time, the pod will not be
+                        scheduled onto the node. If the affinity requirements specified
+                        by this field cease to be met at some point during pod execution
+                        (e.g. due to an update), the system may or may not try to
+                        eventually evict the pod from its node.
+                      properties:
+                        nodeSelectorTerms:
+                          description: Required. A list of node selector terms. The
+                            terms are ORed.
+                          items:
+                            description: A null or empty node selector term matches
+                              no objects. The requirements of them are ANDed. The
+                              TopologySelectorTerm type implements a subset of the
+                              NodeSelectorTerm.
+                            properties:
+                              matchExpressions:
+                                description: A list of node selector requirements
+                                  by node's labels.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchFields:
+                                description: A list of node selector requirements
+                                  by node's fields.
+                                items:
+                                  description: A node selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: The label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: Represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                      type: string
+                                    values:
+                                      description: An array of string values. If the
+                                        operator is In or NotIn, the values array
+                                        must be non-empty. If the operator is Exists
+                                        or DoesNotExist, the values array must be
+                                        empty. If the operator is Gt or Lt, the values
+                                        array must have a single element, which will
+                                        be interpreted as an integer. This array is
+                                        replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                            type: object
+                          type: array
+                      required:
+                      - nodeSelectorTerms
+                      type: object
+                  type: object
+                podAffinity:
+                  description: Describes pod affinity scheduling rules (e.g. co-locate
+                    this pod in the same node, zone, etc. as some other pod(s)).
+                  properties:
+                    preferredDuringSchedulingIgnoredDuringExecution:
+                      description: The scheduler will prefer to schedule pods to nodes
+                        that satisfy the affinity expressions specified by this field,
+                        but it may choose a node that violates one or more of the
+                        expressions. The node that is most preferred is the one with
+                        the greatest sum of weights, i.e. for each node that meets
+                        all of the scheduling requirements (resource request, requiredDuringScheduling
+                        affinity expressions, etc.), compute a sum by iterating through
+                        the elements of this field and adding "weight" to the sum
+                        if the node has pods which matches the corresponding podAffinityTerm;
+                        the node(s) with the highest sum are the most preferred.
+                      items:
+                        description: The weights of all of the matched WeightedPodAffinityTerm
+                          fields are added per-node to find the most preferred node(s)
+                        properties:
+                          podAffinityTerm:
+                            description: Required. A pod affinity term, associated
+                              with the corresponding weight.
+                            properties:
+                              labelSelector:
+                                description: A label query over a set of resources,
+                                  in this case pods.
+                                properties:
+                                  matchExpressions:
+                                    description: matchExpressions is a list of label
+                                      selector requirements. The requirements are
+                                      ANDed.
+                                    items:
+                                      description: A label selector requirement is
+                                        a selector that contains values, a key, and
+                                        an operator that relates the key and values.
+                                      properties:
+                                        key:
+                                          description: key is the label key that the
+                                            selector applies to.
+                                          type: string
+                                        operator:
+                                          description: operator represents a key's
+                                            relationship to a set of values. Valid
+                                            operators are In, NotIn, Exists and DoesNotExist.
+                                          type: string
+                                        values:
+                                          description: values is an array of string
+                                            values. If the operator is In or NotIn,
+                                            the values array must be non-empty. If
+                                            the operator is Exists or DoesNotExist,
+                                            the values array must be empty. This array
+                                            is replaced during a strategic merge patch.
+                                          items:
+                                            type: string
+                                          type: array
+                                      required:
+                                      - key
+                                      - operator
+                                      type: object
+                                    type: array
+                                  matchLabels:
+                                    additionalProperties:
+                                      type: string
+                                    description: matchLabels is a map of {key,value}
+                                      pairs. A single {key,value} in the matchLabels
+                                      map is equivalent to an element of matchExpressions,
+                                      whose key field is "key", the operator is "In",
+                                      and the values array contains only "value".
+                                      The requirements are ANDed.
+                                    type: object
+                                type: object
+                              namespaces:
+                                description: namespaces specifies which namespaces
+                                  the labelSelector applies to (matches against);
+                                  null or empty list means "this pod's namespace"
+                                items:
+                                  type: string
+                                type: array
+                              topologyKey:
+                                description: This pod should be co-located (affinity)
+                                  or not co-located (anti-affinity) with the pods
+                                  matching the labelSelector in the specified namespaces,
+                                  where co-located is defined as running on a node
+                                  whose value of the label with key topologyKey matches
+                                  that of any node on which any of the selected pods
+                                  is running. Empty topologyKey is not allowed.
+                                type: string
+                            required:
+                            - topologyKey
+                            type: object
+                          weight:
+                            description: weight associated with matching the corresponding
+                              podAffinityTerm, in the range 1-100.
+                            format: int32
+                            type: integer
+                        required:
+                        - podAffinityTerm
+                        - weight
+                        type: object
+                      type: array
+                    requiredDuringSchedulingIgnoredDuringExecution:
+                      description: If the affinity requirements specified by this
+                        field are not met at scheduling time, the pod will not be
+                        scheduled onto the node. If the affinity requirements specified
+                        by this field cease to be met at some point during pod execution
+                        (e.g. due to a pod label update), the system may or may not
+                        try to eventually evict the pod from its node. When there
+                        are multiple elements, the lists of nodes corresponding to
+                        each podAffinityTerm are intersected, i.e. all terms must
+                        be satisfied.
+                      items:
+                        description: Defines a set of pods (namely those matching
+                          the labelSelector relative to the given namespace(s)) that
+                          this pod should be co-located (affinity) or not co-located
+                          (anti-affinity) with, where co-located is defined as running
+                          on a node whose value of the label with key <topologyKey>
+                          matches that of any node on which a pod of the set of pods
+                          is running
+                        properties:
+                          labelSelector:
+                            description: A label query over a set of resources, in
+                              this case pods.
+                            properties:
+                              matchExpressions:
+                                description: matchExpressions is a list of label selector
+                                  requirements. The requirements are ANDed.
+                                items:
+                                  description: A label selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: key is the label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: operator represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists and DoesNotExist.
+                                      type: string
+                                    values:
+                                      description: values is an array of string values.
+                                        If the operator is In or NotIn, the values
+                                        array must be non-empty. If the operator is
+                                        Exists or DoesNotExist, the values array must
+                                        be empty. This array is replaced during a
+                                        strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchLabels:
+                                additionalProperties:
+                                  type: string
+                                description: matchLabels is a map of {key,value} pairs.
+                                  A single {key,value} in the matchLabels map is equivalent
+                                  to an element of matchExpressions, whose key field
+                                  is "key", the operator is "In", and the values array
+                                  contains only "value". The requirements are ANDed.
+                                type: object
+                            type: object
+                          namespaces:
+                            description: namespaces specifies which namespaces the
+                              labelSelector applies to (matches against); null or
+                              empty list means "this pod's namespace"
+                            items:
+                              type: string
+                            type: array
+                          topologyKey:
+                            description: This pod should be co-located (affinity)
+                              or not co-located (anti-affinity) with the pods matching
+                              the labelSelector in the specified namespaces, where
+                              co-located is defined as running on a node whose value
+                              of the label with key topologyKey matches that of any
+                              node on which any of the selected pods is running. Empty
+                              topologyKey is not allowed.
+                            type: string
+                        required:
+                        - topologyKey
+                        type: object
+                      type: array
+                  type: object
+                podAntiAffinity:
+                  description: Describes pod anti-affinity scheduling rules (e.g.
+                    avoid putting this pod in the same node, zone, etc. as some other
+                    pod(s)).
+                  properties:
+                    preferredDuringSchedulingIgnoredDuringExecution:
+                      description: The scheduler will prefer to schedule pods to nodes
+                        that satisfy the anti-affinity expressions specified by this
+                        field, but it may choose a node that violates one or more
+                        of the expressions. The node that is most preferred is the
+                        one with the greatest sum of weights, i.e. for each node that
+                        meets all of the scheduling requirements (resource request,
+                        requiredDuringScheduling anti-affinity expressions, etc.),
+                        compute a sum by iterating through the elements of this field
+                        and adding "weight" to the sum if the node has pods which
+                        matches the corresponding podAffinityTerm; the node(s) with
+                        the highest sum are the most preferred.
+                      items:
+                        description: The weights of all of the matched WeightedPodAffinityTerm
+                          fields are added per-node to find the most preferred node(s)
+                        properties:
+                          podAffinityTerm:
+                            description: Required. A pod affinity term, associated
+                              with the corresponding weight.
+                            properties:
+                              labelSelector:
+                                description: A label query over a set of resources,
+                                  in this case pods.
+                                properties:
+                                  matchExpressions:
+                                    description: matchExpressions is a list of label
+                                      selector requirements. The requirements are
+                                      ANDed.
+                                    items:
+                                      description: A label selector requirement is
+                                        a selector that contains values, a key, and
+                                        an operator that relates the key and values.
+                                      properties:
+                                        key:
+                                          description: key is the label key that the
+                                            selector applies to.
+                                          type: string
+                                        operator:
+                                          description: operator represents a key's
+                                            relationship to a set of values. Valid
+                                            operators are In, NotIn, Exists and DoesNotExist.
+                                          type: string
+                                        values:
+                                          description: values is an array of string
+                                            values. If the operator is In or NotIn,
+                                            the values array must be non-empty. If
+                                            the operator is Exists or DoesNotExist,
+                                            the values array must be empty. This array
+                                            is replaced during a strategic merge patch.
+                                          items:
+                                            type: string
+                                          type: array
+                                      required:
+                                      - key
+                                      - operator
+                                      type: object
+                                    type: array
+                                  matchLabels:
+                                    additionalProperties:
+                                      type: string
+                                    description: matchLabels is a map of {key,value}
+                                      pairs. A single {key,value} in the matchLabels
+                                      map is equivalent to an element of matchExpressions,
+                                      whose key field is "key", the operator is "In",
+                                      and the values array contains only "value".
+                                      The requirements are ANDed.
+                                    type: object
+                                type: object
+                              namespaces:
+                                description: namespaces specifies which namespaces
+                                  the labelSelector applies to (matches against);
+                                  null or empty list means "this pod's namespace"
+                                items:
+                                  type: string
+                                type: array
+                              topologyKey:
+                                description: This pod should be co-located (affinity)
+                                  or not co-located (anti-affinity) with the pods
+                                  matching the labelSelector in the specified namespaces,
+                                  where co-located is defined as running on a node
+                                  whose value of the label with key topologyKey matches
+                                  that of any node on which any of the selected pods
+                                  is running. Empty topologyKey is not allowed.
+                                type: string
+                            required:
+                            - topologyKey
+                            type: object
+                          weight:
+                            description: weight associated with matching the corresponding
+                              podAffinityTerm, in the range 1-100.
+                            format: int32
+                            type: integer
+                        required:
+                        - podAffinityTerm
+                        - weight
+                        type: object
+                      type: array
+                    requiredDuringSchedulingIgnoredDuringExecution:
+                      description: If the anti-affinity requirements specified by
+                        this field are not met at scheduling time, the pod will not
+                        be scheduled onto the node. If the anti-affinity requirements
+                        specified by this field cease to be met at some point during
+                        pod execution (e.g. due to a pod label update), the system
+                        may or may not try to eventually evict the pod from its node.
+                        When there are multiple elements, the lists of nodes corresponding
+                        to each podAffinityTerm are intersected, i.e. all terms must
+                        be satisfied.
+                      items:
+                        description: Defines a set of pods (namely those matching
+                          the labelSelector relative to the given namespace(s)) that
+                          this pod should be co-located (affinity) or not co-located
+                          (anti-affinity) with, where co-located is defined as running
+                          on a node whose value of the label with key <topologyKey>
+                          matches that of any node on which a pod of the set of pods
+                          is running
+                        properties:
+                          labelSelector:
+                            description: A label query over a set of resources, in
+                              this case pods.
+                            properties:
+                              matchExpressions:
+                                description: matchExpressions is a list of label selector
+                                  requirements. The requirements are ANDed.
+                                items:
+                                  description: A label selector requirement is a selector
+                                    that contains values, a key, and an operator that
+                                    relates the key and values.
+                                  properties:
+                                    key:
+                                      description: key is the label key that the selector
+                                        applies to.
+                                      type: string
+                                    operator:
+                                      description: operator represents a key's relationship
+                                        to a set of values. Valid operators are In,
+                                        NotIn, Exists and DoesNotExist.
+                                      type: string
+                                    values:
+                                      description: values is an array of string values.
+                                        If the operator is In or NotIn, the values
+                                        array must be non-empty. If the operator is
+                                        Exists or DoesNotExist, the values array must
+                                        be empty. This array is replaced during a
+                                        strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                  - key
+                                  - operator
+                                  type: object
+                                type: array
+                              matchLabels:
+                                additionalProperties:
+                                  type: string
+                                description: matchLabels is a map of {key,value} pairs.
+                                  A single {key,value} in the matchLabels map is equivalent
+                                  to an element of matchExpressions, whose key field
+                                  is "key", the operator is "In", and the values array
+                                  contains only "value". The requirements are ANDed.
+                                type: object
+                            type: object
+                          namespaces:
+                            description: namespaces specifies which namespaces the
+                              labelSelector applies to (matches against); null or
+                              empty list means "this pod's namespace"
+                            items:
+                              type: string
+                            type: array
+                          topologyKey:
+                            description: This pod should be co-located (affinity)
+                              or not co-located (anti-affinity) with the pods matching
+                              the labelSelector in the specified namespaces, where
+                              co-located is defined as running on a node whose value
+                              of the label with key topologyKey matches that of any
+                              node on which any of the selected pods is running. Empty
+                              topologyKey is not allowed.
+                            type: string
+                        required:
+                        - topologyKey
+                        type: object
+                      type: array
+                  type: object
+              type: object
+            airflowbase:
+              description: AirflowBaseRef is a reference to the AirflowBase CR
+              properties:
+                name:
+                  description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                    TODO: Add other useful fields. apiVersion, kind, uid?'
+                  type: string
+              type: object
+            annotations:
+              additionalProperties:
+                type: string
+              description: Custom annotations to be added to the pods.
+              type: object
+            config:
+              description: Airflow config as env list
+              properties:
+                airflow:
+                  additionalProperties:
+                    type: string
+                  description: Airflow defines a list of kv pairs that describe env
+                    variables injected into the nodes
+                  type: object
+                airflowsecret:
+                  description: AirflowSecret defines a list of secret envs
+                  items:
+                    description: SecretEnv secret env
+                    properties:
+                      env:
+                        description: Env - env variable name
+                        type: string
+                      field:
+                        description: Field - field name
+                        type: string
+                      secret:
+                        description: Secret - secret name
+                        type: string
+                    type: object
+                  type: array
+              type: object
+            dags:
+              description: Spec for DAG source and location
+              properties:
+                gcs:
+                  description: Gcs config which uses storage spec
+                  properties:
+                    bucket:
+                      description: Bucket describes the GCS bucket
+                      type: string
+                    once:
+                      description: Once syncs initially and quits (use init container
+                        instead of sidecar)
+                      type: boolean
+                  type: object
+                git:
+                  description: GitSpec defines details to pull DAGs from a git repo
+                    using github.com/kubernetes/git-sync sidecar
+                  properties:
+                    branch:
+                      description: Branch describes the branch name to be synced
+                      type: string
+                    cred:
+                      description: Reference to git credentials (user, password, ssh
+                        etc)
+                      properties:
+                        name:
+                          description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                            TODO: Add other useful fields. apiVersion, kind, uid?'
+                          type: string
+                      type: object
+                    once:
+                      description: Once syncs initially and quits (use init container
+                        instead of sidecar)
+                      type: boolean
+                    repo:
+                      description: Repo describes the http/ssh uri for git repo
+                      type: string
+                    rev:
+                      description: Rev is the git hash to be used for syncing
+                      type: string
+                    user:
+                      description: User for git access
+                      type: string
+                  required:
+                  - repo
+                  type: object
+                nfspv:
+                  description: NfsPVSpec
+                  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:
+                      description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
+                      type: object
+                    spec:
+                      description: 'Spec defines the desired characteristics of a
+                        volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the desired access modes
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        dataSource:
+                          description: This field requires the VolumeSnapshotDataSource
+                            alpha feature gate to be enabled and currently VolumeSnapshot
+                            is the only supported data source. If the provisioner
+                            can support VolumeSnapshot data source, it will create
+                            a new volume and data will be restored to the volume at
+                            the same time. If the provisioner does not support VolumeSnapshot
+                            data source, volume will not be created and the failure
+                            will be reported as an event. In the future, we plan to
+                            support more data source types and the behavior of the
+                            provisioner may change.
+                          properties:
+                            apiGroup:
+                              description: APIGroup is the group for the resource
+                                being referenced. If APIGroup is not specified, the
+                                specified Kind must be in the core API group. For
+                                any other third-party types, APIGroup is required.
+                              type: string
+                            kind:
+                              description: Kind is the type of resource being referenced
+                              type: string
+                            name:
+                              description: Name is the name of resource being referenced
+                              type: string
+                          required:
+                          - kind
+                          - name
+                          type: object
+                        resources:
+                          description: 'Resources represents the minimum resources
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                          properties:
+                            limits:
+                              additionalProperties:
+                                type: string
+                              description: 'Limits describes the maximum amount of
+                                compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                            requests:
+                              additionalProperties:
+                                type: string
+                              description: 'Requests describes the minimum amount
+                                of compute resources required. If Requests is omitted
+                                for a container, it defaults to Limits if that is
+                                explicitly specified, otherwise to an implementation-defined
+                                value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                          type: object
+                        selector:
+                          description: A label query over volumes to consider for
+                            binding.
+                          properties:
+                            matchExpressions:
+                              description: matchExpressions is a list of label selector
+                                requirements. The requirements are ANDed.
+                              items:
+                                description: A label selector requirement is a selector
+                                  that contains values, a key, and an operator that
+                                  relates the key and values.
+                                properties:
+                                  key:
+                                    description: key is the label key that the selector
+                                      applies to.
+                                    type: string
+                                  operator:
+                                    description: operator represents a key's relationship
+                                      to a set of values. Valid operators are In,
+                                      NotIn, Exists and DoesNotExist.
+                                    type: string
+                                  values:
+                                    description: values is an array of string values.
+                                      If the operator is In or NotIn, the values array
+                                      must be non-empty. If the operator is Exists
+                                      or DoesNotExist, the values array must be empty.
+                                      This array is replaced during a strategic merge
+                                      patch.
+                                    items:
+                                      type: string
+                                    type: array
+                                required:
+                                - key
+                                - operator
+                                type: object
+                              type: array
+                            matchLabels:
+                              additionalProperties:
+                                type: string
+                              description: matchLabels is a map of {key,value} pairs.
+                                A single {key,value} in the matchLabels map is equivalent
+                                to an element of matchExpressions, whose key field
+                                is "key", the operator is "In", and the values array
+                                contains only "value". The requirements are ANDed.
+                              type: object
+                          type: object
+                        storageClassName:
+                          description: 'Name of the StorageClass required by the claim.
+                            More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                          type: string
+                        volumeMode:
+                          description: volumeMode defines what type of volume is required
+                            by the claim. Value of Filesystem is implied when not
+                            included in claim spec. This is a beta feature.
+                          type: string
+                        volumeName:
+                          description: VolumeName is the binding reference to the
+                            PersistentVolume backing this claim.
+                          type: string
+                      type: object
+                    status:
+                      description: 'Status represents the current information/status
+                        of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the actual access modes
+                            the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        capacity:
+                          additionalProperties:
+                            type: string
+                          description: Represents the actual resources of the underlying
+                            volume.
+                          type: object
+                        conditions:
+                          description: Current Condition of persistent volume claim.
+                            If underlying persistent volume is being resized then
+                            the Condition will be set to 'ResizeStarted'.
+                          items:
+                            description: PersistentVolumeClaimCondition contails details
+                              about state of pvc
+                            properties:
+                              lastProbeTime:
+                                description: Last time we probed the condition.
+                                format: date-time
+                                type: string
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: Human-readable message indicating details
+                                  about last transition.
+                                type: string
+                              reason:
+                                description: Unique, this should be a short, machine
+                                  understandable string that gives the reason for
+                                  condition's last transition. If it reports "ResizeStarted"
+                                  that means the underlying persistent volume is being
+                                  resized.
+                                type: string
+                              status:
+                                type: string
+                              type:
+                                description: PersistentVolumeClaimConditionType is
+                                  a valid value of PersistentVolumeClaimCondition.Type
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                        phase:
+                          description: Phase represents the current phase of PersistentVolumeClaim.
+                          type: string
+                      type: object
+                  type: object
+                storage:
+                  description: Storage has s3 compatible storage spec for copying
+                    files from
+                  properties:
+                    config:
+                      additionalProperties:
+                        type: string
+                      description: Config is generic string based key-value map that
+                        defines non-secret configuration values for uploading the
+                        backup to storage w.r.t the configured storage provider.
+                      type: object
+                    secretRef:
+                      description: SecretRef is a reference to the Kubernetes secret
+                        containing the configuration for uploading the backup to authenticated
+                        storage.
+                      properties:
+                        name:
+                          description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                            TODO: Add other useful fields. apiVersion, kind, uid?'
+                          type: string
+                      type: object
+                    storageprovider:
+                      description: Provider is the storage type used for backup and
+                        restore e.g. s3, oci-s3-compat, aws-s3, gce-s3, etc.
+                      type: string
+                  required:
+                  - storageprovider
+                  type: object
+                subdir:
+                  description: DagSubdir is the directory under source where the dags
+                    are present
+                  type: string
+              type: object
+            executor:
+              description: 'Airflow Executor desired: local,celery,kubernetes'
+              type: string
+            flower:
+              description: Spec for Flower component.
+              properties:
+                image:
+                  description: Image defines the Flower Docker image.
+                  type: string
+                replicas:
+                  description: Replicas defines the number of running Flower instances
+                    in a cluster
+                  format: int32
+                  type: integer
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the Flower Docker image version.
+                  type: string
+              type: object
+            labels:
+              additionalProperties:
+                type: string
+              description: Custom labels to be added to the pods.
+              type: object
+            memoryStore:
+              description: Spec for MemoryStore component
+              properties:
+                alternativeLocationId:
+                  description: AlternativeLocationID - alt
+                  type: string
+                authorizedNetwork:
+                  description: AuthorizedNetwork
+                  type: string
+                locationId:
+                  description: LocationID The zone where the instance will be provisioned.
+                  type: string
+                maxMemoryPolicy:
+                  description: Specifies the behavior Redis follows when the memory
+                    size limit is reached.
+                  type: string
+                memorySizeGb:
+                  description: 'MemorySizeGb: Required. Redis memory size in GiB.'
+                  format: int64
+                  type: integer
+                notifyKeyspaceEvents:
+                  description: Allows clients to subscribe to notifications on certain
+                    keyspace events
+                  type: string
+                project:
+                  description: Project defines the SQL instance project
+                  type: string
+                redisConfigs:
+                  additionalProperties:
+                    type: string
+                  description: 'RedisConfigs: Optional. Redis configuration parameters'
+                  type: object
+                redisVersion:
+                  description: 'RedisVersion: Optional. The version of Redis software.'
+                  type: string
+                region:
+                  description: Region defines the SQL instance region
+                  type: string
+                status:
+                  description: Status
+                  properties:
+                    components:
+                      description: Object status array for all matching objects
+                      items:
+                        description: ObjectStatus is a generic status holder for objects
+                        properties:
+                          group:
+                            description: Object group
+                            type: string
+                          kind:
+                            description: Kind of object
+                            type: string
+                          link:
+                            description: Link to object
+                            type: string
+                          name:
+                            description: Name of object
+                            type: string
+                          pdb:
+                            description: PDB status
+                            properties:
+                              currenthealthy:
+                                description: currentHealthy
+                                format: int32
+                                type: integer
+                              desiredhealthy:
+                                description: desiredHealthy
+                                format: int32
+                                type: integer
+                            required:
+                            - currenthealthy
+                            - desiredhealthy
+                            type: object
+                          status:
+                            description: 'Status. Values: InProgress, Ready, Unknown'
+                            type: string
+                          sts:
+                            description: StatefulSet status
+                            properties:
+                              currentcount:
+                                description: CurrentReplicas defines the no of MySQL
+                                  instances that are created
+                                format: int32
+                                type: integer
+                              progress:
+                                description: 'progress is a fuzzy indicator. Interpret
+                                  as a percentage (0-100) eg: for statefulsets, progress
+                                  = 100*readyreplicas/replicas'
+                                format: int32
+                                type: integer
+                              readycount:
+                                description: ReadyReplicas defines the no of MySQL
+                                  instances that are ready
+                                format: int32
+                                type: integer
+                              replicas:
+                                description: Replicas defines the no of MySQL instances
+                                  desired
+                                format: int32
+                                type: integer
+                            required:
+                            - currentcount
+                            - progress
+                            - readycount
+                            - replicas
+                            type: object
+                        type: object
+                      type: array
+                    conditions:
+                      description: Conditions represents the latest state of the object
+                      items:
+                        description: Condition describes the state of an object at
+                          a certain point.
+                        properties:
+                          lastTransitionTime:
+                            description: Last time the condition transitioned from
+                              one status to another.
+                            format: date-time
+                            type: string
+                          lastUpdateTime:
+                            description: Last time the condition was probed
+                            format: date-time
+                            type: string
+                          message:
+                            description: A human readable message indicating details
+                              about the transition.
+                            type: string
+                          reason:
+                            description: The reason for the condition's last transition.
+                            type: string
+                          status:
+                            description: Status of the condition, one of True, False,
+                              Unknown.
+                            type: string
+                          type:
+                            description: Type of condition.
+                            type: string
+                        required:
+                        - status
+                        - type
+                        type: object
+                      type: array
+                    createTime:
+                      description: 'CreateTime: Output only. The time the instance
+                        was created.'
+                      type: string
+                    currentLocationId:
+                      description: 'CurrentLocationID: Output only. The current zone
+                        where the Redis endpoint is placed.'
+                      type: string
+                    host:
+                      description: 'Host: Output only. Hostname or IP address of the
+                        exposed Redis endpoint used by clients to connect to the service.'
+                      type: string
+                    observedGeneration:
+                      description: ObservedGeneration is the most recent generation
+                        observed. It corresponds to the Object's generation, which
+                        is updated on mutation by the API Server.
+                      format: int64
+                      type: integer
+                    port:
+                      description: 'Port: Output only. The port number of the exposed
+                        Redis endpoint.'
+                      format: int64
+                      type: integer
+                    state:
+                      description: 'State: Output only. The current state of this
+                        instance.'
+                      type: string
+                    statusMessage:
+                      description: 'StatusMessage: Output only. Additional information
+                        about the current status of this instance, if available.'
+                      type: string
+                  type: object
+                tier:
+                  description: 'Tier: Required. The service tier of the instance.'
+                  type: string
+              required:
+              - project
+              - region
+              type: object
+            nodeSelector:
+              additionalProperties:
+                type: string
+              description: Selector for fitting pods to nodes whose labels match the
+                selector. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+              type: object
+            redis:
+              description: Spec for Redis component.
+              properties:
+                additionalargs:
+                  description: AdditionalArgs for redis-server
+                  type: string
+                image:
+                  description: Image defines the Redis Docker image name
+                  type: string
+                operator:
+                  description: Flag when True generates RedisReplica CustomResource
+                    to be handled by Redis Operator If False, a StatefulSet with 1
+                    replica is created
+                  type: boolean
+                redisHost:
+                  description: Hostname or IP of existing Redis instance
+                  type: string
+                redisPassword:
+                  description: If the existing Redis instance uses password or not,
+                    as MemoryStore doesn't support password yet
+                  type: boolean
+                redisPort:
+                  description: Port of existing Redis instance
+                  type: string
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the Redis Docker image version.
+                  type: string
+                volumeClaimTemplate:
+                  description: VolumeClaimTemplate allows a user to specify volume
+                    claim for MySQL Server files
+                  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:
+                      description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
+                      type: object
+                    spec:
+                      description: 'Spec defines the desired characteristics of a
+                        volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the desired access modes
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        dataSource:
+                          description: This field requires the VolumeSnapshotDataSource
+                            alpha feature gate to be enabled and currently VolumeSnapshot
+                            is the only supported data source. If the provisioner
+                            can support VolumeSnapshot data source, it will create
+                            a new volume and data will be restored to the volume at
+                            the same time. If the provisioner does not support VolumeSnapshot
+                            data source, volume will not be created and the failure
+                            will be reported as an event. In the future, we plan to
+                            support more data source types and the behavior of the
+                            provisioner may change.
+                          properties:
+                            apiGroup:
+                              description: APIGroup is the group for the resource
+                                being referenced. If APIGroup is not specified, the
+                                specified Kind must be in the core API group. For
+                                any other third-party types, APIGroup is required.
+                              type: string
+                            kind:
+                              description: Kind is the type of resource being referenced
+                              type: string
+                            name:
+                              description: Name is the name of resource being referenced
+                              type: string
+                          required:
+                          - kind
+                          - name
+                          type: object
+                        resources:
+                          description: 'Resources represents the minimum resources
+                            the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
+                          properties:
+                            limits:
+                              additionalProperties:
+                                type: string
+                              description: 'Limits describes the maximum amount of
+                                compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                            requests:
+                              additionalProperties:
+                                type: string
+                              description: 'Requests describes the minimum amount
+                                of compute resources required. If Requests is omitted
+                                for a container, it defaults to Limits if that is
+                                explicitly specified, otherwise to an implementation-defined
+                                value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                              type: object
+                          type: object
+                        selector:
+                          description: A label query over volumes to consider for
+                            binding.
+                          properties:
+                            matchExpressions:
+                              description: matchExpressions is a list of label selector
+                                requirements. The requirements are ANDed.
+                              items:
+                                description: A label selector requirement is a selector
+                                  that contains values, a key, and an operator that
+                                  relates the key and values.
+                                properties:
+                                  key:
+                                    description: key is the label key that the selector
+                                      applies to.
+                                    type: string
+                                  operator:
+                                    description: operator represents a key's relationship
+                                      to a set of values. Valid operators are In,
+                                      NotIn, Exists and DoesNotExist.
+                                    type: string
+                                  values:
+                                    description: values is an array of string values.
+                                      If the operator is In or NotIn, the values array
+                                      must be non-empty. If the operator is Exists
+                                      or DoesNotExist, the values array must be empty.
+                                      This array is replaced during a strategic merge
+                                      patch.
+                                    items:
+                                      type: string
+                                    type: array
+                                required:
+                                - key
+                                - operator
+                                type: object
+                              type: array
+                            matchLabels:
+                              additionalProperties:
+                                type: string
+                              description: matchLabels is a map of {key,value} pairs.
+                                A single {key,value} in the matchLabels map is equivalent
+                                to an element of matchExpressions, whose key field
+                                is "key", the operator is "In", and the values array
+                                contains only "value". The requirements are ANDed.
+                              type: object
+                          type: object
+                        storageClassName:
+                          description: 'Name of the StorageClass required by the claim.
+                            More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+                          type: string
+                        volumeMode:
+                          description: volumeMode defines what type of volume is required
+                            by the claim. Value of Filesystem is implied when not
+                            included in claim spec. This is a beta feature.
+                          type: string
+                        volumeName:
+                          description: VolumeName is the binding reference to the
+                            PersistentVolume backing this claim.
+                          type: string
+                      type: object
+                    status:
+                      description: 'Status represents the current information/status
+                        of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
+                      properties:
+                        accessModes:
+                          description: 'AccessModes contains the actual access modes
+                            the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
+                          items:
+                            type: string
+                          type: array
+                        capacity:
+                          additionalProperties:
+                            type: string
+                          description: Represents the actual resources of the underlying
+                            volume.
+                          type: object
+                        conditions:
+                          description: Current Condition of persistent volume claim.
+                            If underlying persistent volume is being resized then
+                            the Condition will be set to 'ResizeStarted'.
+                          items:
+                            description: PersistentVolumeClaimCondition contails details
+                              about state of pvc
+                            properties:
+                              lastProbeTime:
+                                description: Last time we probed the condition.
+                                format: date-time
+                                type: string
+                              lastTransitionTime:
+                                description: Last time the condition transitioned
+                                  from one status to another.
+                                format: date-time
+                                type: string
+                              message:
+                                description: Human-readable message indicating details
+                                  about last transition.
+                                type: string
+                              reason:
+                                description: Unique, this should be a short, machine
+                                  understandable string that gives the reason for
+                                  condition's last transition. If it reports "ResizeStarted"
+                                  that means the underlying persistent volume is being
+                                  resized.
+                                type: string
+                              status:
+                                type: string
+                              type:
+                                description: PersistentVolumeClaimConditionType is
+                                  a valid value of PersistentVolumeClaimCondition.Type
+                                type: string
+                            required:
+                            - status
+                            - type
+                            type: object
+                          type: array
+                        phase:
+                          description: Phase represents the current phase of PersistentVolumeClaim.
+                          type: string
+                      type: object
+                  type: object
+              type: object
+            scheduler:
+              description: Spec for Airflow Scheduler component.
+              properties:
+                database:
+                  description: DBName defines the Airflow Database to be used
+                  type: string
+                dbuser:
+                  description: DBUser defines the Airflow Database user to be used
+                  type: string
+                image:
+                  description: Image defines the Airflow custom server Docker image.
+                  type: string
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the Airflow Docker image version
+                  type: string
+              type: object
+            ui:
+              description: Spec for Airflow UI component.
+              properties:
+                image:
+                  description: Image defines the AirflowUI Docker image.
+                  type: string
+                replicas:
+                  description: Replicas defines the number of running Airflow UI instances
+                    in a cluster
+                  format: int32
+                  type: integer
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the AirflowUI Docker image version.
+                  type: string
+              type: object
+            worker:
+              description: Spec for Airflow Workers
+              properties:
+                image:
+                  description: Image defines the Airflow worker Docker image.
+                  type: string
+                replicas:
+                  description: Replicas is the count of number of workers
+                  format: int32
+                  type: integer
+                resources:
+                  description: Resources is the resource requests and limits for the
+                    pods.
+                  properties:
+                    limits:
+                      additionalProperties:
+                        type: string
+                      description: 'Limits describes the maximum amount of compute
+                        resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                    requests:
+                      additionalProperties:
+                        type: string
+                      description: 'Requests describes the minimum amount of compute
+                        resources required. If Requests is omitted for a container,
+                        it defaults to Limits if that is explicitly specified, otherwise
+                        to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
+                      type: object
+                  type: object
+                version:
+                  description: Version defines the Airflow worker Docker image version
+                  type: string
+              type: object
+          type: object
+        status:
+          description: AirflowClusterStatus defines the observed state of AirflowCluster
+          properties:
+            components:
+              description: Object status array for all matching objects
+              items:
+                description: ObjectStatus is a generic status holder for objects
+                properties:
+                  group:
+                    description: Object group
+                    type: string
+                  kind:
+                    description: Kind of object
+                    type: string
+                  link:
+                    description: Link to object
+                    type: string
+                  name:
+                    description: Name of object
+                    type: string
+                  pdb:
+                    description: PDB status
+                    properties:
+                      currenthealthy:
+                        description: currentHealthy
+                        format: int32
+                        type: integer
+                      desiredhealthy:
+                        description: desiredHealthy
+                        format: int32
+                        type: integer
+                    required:
+                    - currenthealthy
+                    - desiredhealthy
+                    type: object
+                  status:
+                    description: 'Status. Values: InProgress, Ready, Unknown'
+                    type: string
+                  sts:
+                    description: StatefulSet status
+                    properties:
+                      currentcount:
+                        description: CurrentReplicas defines the no of MySQL instances
+                          that are created
+                        format: int32
+                        type: integer
+                      progress:
+                        description: 'progress is a fuzzy indicator. Interpret as
+                          a percentage (0-100) eg: for statefulsets, progress = 100*readyreplicas/replicas'
+                        format: int32
+                        type: integer
+                      readycount:
+                        description: ReadyReplicas defines the no of MySQL instances
+                          that are ready
+                        format: int32
+                        type: integer
+                      replicas:
+                        description: Replicas defines the no of MySQL instances desired
+                        format: int32
+                        type: integer
+                    required:
+                    - currentcount
+                    - progress
+                    - readycount
+                    - replicas
+                    type: object
+                type: object
+              type: array
+            conditions:
+              description: Conditions represents the latest state of the object
+              items:
+                description: Condition describes the state of an object at a certain
+                  point.
+                properties:
+                  lastTransitionTime:
+                    description: Last time the condition transitioned from one status
+                      to another.
+                    format: date-time
+                    type: string
+                  lastUpdateTime:
+                    description: Last time the condition was probed
+                    format: date-time
+                    type: string
+                  message:
+                    description: A human readable message indicating details about
+                      the transition.
+                    type: string
+                  reason:
+                    description: The reason for the condition's last transition.
+                    type: string
+                  status:
+                    description: Status of the condition, one of True, False, Unknown.
+                    type: string
+                  type:
+                    description: Type of condition.
+                    type: string
+                required:
+                - status
+                - type
+                type: object
+              type: array
+            observedGeneration:
+              description: ObservedGeneration is the most recent generation observed.
+                It corresponds to the Object's generation, which is updated on mutation
+                by the API Server.
+              format: int64
+              type: integer
+          type: object
+      type: object
+  version: v1alpha1
+  versions:
+  - name: v1alpha1
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
new file mode 100644
index 0000000..b36028f
--- /dev/null
+++ b/config/crd/kustomization.yaml
@@ -0,0 +1,24 @@
+# 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/airflow.apache.org_airflowbases.yaml
+- bases/airflow.apache.org_airflowclusters.yaml
+# +kubebuilder:scaffold:crdkustomizeresource
+
+patchesStrategicMerge:
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
+# patches here are for enabling the conversion webhook for each CRD
+#- patches/webhook_in_airflowbases.yaml
+#- patches/webhook_in_airflowclusters.yaml
+# +kubebuilder:scaffold:crdkustomizewebhookpatch
+
+# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
+# patches here are for enabling the CA injection for each CRD
+#- patches/cainjection_in_airflowbases.yaml
+#- patches/cainjection_in_airflowclusters.yaml
+# +kubebuilder:scaffold:crdkustomizecainjectionpatch
+
+# the following config is for teaching kustomize how to do kustomization for CRDs.
+configurations:
+- kustomizeconfig.yaml
diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml
new file mode 100644
index 0000000..6f83d9a
--- /dev/null
+++ b/config/crd/kustomizeconfig.yaml
@@ -0,0 +1,17 @@
+# This file is for teaching kustomize how to substitute name and namespace reference in CRD
+nameReference:
+- kind: Service
+  version: v1
+  fieldSpecs:
+  - kind: CustomResourceDefinition
+    group: apiextensions.k8s.io
+    path: spec/conversion/webhookClientConfig/service/name
+
+namespace:
+- kind: CustomResourceDefinition
+  group: apiextensions.k8s.io
+  path: spec/conversion/webhookClientConfig/service/namespace
+  create: false
+
+varReference:
+- path: metadata/annotations
diff --git a/config/crd/patches/cainjection_in_airflowbases.yaml b/config/crd/patches/cainjection_in_airflowbases.yaml
new file mode 100644
index 0000000..baddfe2
--- /dev/null
+++ b/config/crd/patches/cainjection_in_airflowbases.yaml
@@ -0,0 +1,8 @@
+# The following patch adds a directive for certmanager to inject CA into the CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: airflowbases.airflow.apache.org
diff --git a/config/crd/patches/cainjection_in_airflowclusters.yaml b/config/crd/patches/cainjection_in_airflowclusters.yaml
new file mode 100644
index 0000000..38562bb
--- /dev/null
+++ b/config/crd/patches/cainjection_in_airflowclusters.yaml
@@ -0,0 +1,8 @@
+# The following patch adds a directive for certmanager to inject CA into the CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: airflowclusters.airflow.apache.org
diff --git a/config/crd/patches/webhook_in_airflowbases.yaml b/config/crd/patches/webhook_in_airflowbases.yaml
new file mode 100644
index 0000000..34f612f
--- /dev/null
+++ b/config/crd/patches/webhook_in_airflowbases.yaml
@@ -0,0 +1,17 @@
+# The following patch enables conversion webhook for CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: airflowbases.airflow.apache.org
+spec:
+  conversion:
+    strategy: Webhook
+    webhookClientConfig:
+      # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
+      # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
+      caBundle: Cg==
+      service:
+        namespace: system
+        name: webhook-service
+        path: /convert
diff --git a/config/crd/patches/webhook_in_airflowclusters.yaml b/config/crd/patches/webhook_in_airflowclusters.yaml
new file mode 100644
index 0000000..1b81d85
--- /dev/null
+++ b/config/crd/patches/webhook_in_airflowclusters.yaml
@@ -0,0 +1,17 @@
+# The following patch enables conversion webhook for CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: airflowclusters.airflow.apache.org
+spec:
+  conversion:
+    strategy: Webhook
+    webhookClientConfig:
+      # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
+      # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
+      caBundle: Cg==
+      service:
+        namespace: system
+        name: webhook-service
+        path: /convert
diff --git a/config/crds/airflow_v1alpha1_airflowbase.yaml b/config/crds/airflow_v1alpha1_airflowbase.yaml
deleted file mode 100644
index 3b97406..0000000
--- a/config/crds/airflow_v1alpha1_airflowbase.yaml
+++ /dev/null
@@ -1,217 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  labels:
-    controller-tools.k8s.io: "1.0"
-  name: airflowbases.airflow.k8s.io
-spec:
-  group: airflow.k8s.io
-  names:
-    kind: AirflowBase
-    plural: airflowbases
-  scope: Namespaced
-  validation:
-    openAPIV3Schema:
-      properties:
-        apiVersion:
-          type: string
-        kind:
-          type: string
-        metadata:
-          type: object
-        spec:
-          properties:
-            affinity:
-              type: object
-            annotations:
-              type: object
-            labels:
-              type: object
-            mysql:
-              properties:
-                backup:
-                  properties:
-                    schedule:
-                      type: string
-                    storage:
-                      properties:
-                        config:
-                          type: object
-                        secretRef:
-                          type: object
-                        storageprovider:
-                          type: string
-                      required:
-                      - storageprovider
-                      type: object
-                  required:
-                  - schedule
-                  - storage
-                  type: object
-                backupVolumeClaimTemplate:
-                  type: object
-                image:
-                  type: string
-                operator:
-                  type: boolean
-                replicas:
-                  format: int32
-                  type: integer
-                resources:
-                  type: object
-                version:
-                  type: string
-                volumeClaimTemplate:
-                  type: object
-              type: object
-            nodeSelector:
-              type: object
-            postgres:
-              properties:
-                image:
-                  type: string
-                operator:
-                  type: boolean
-                replicas:
-                  format: int32
-                  type: integer
-                resources:
-                  type: object
-                version:
-                  type: string
-                volumeClaimTemplate:
-                  type: object
-              type: object
-            sqlproxy:
-              properties:
-                image:
-                  type: string
-                instance:
-                  type: string
-                project:
-                  type: string
-                region:
-                  type: string
-                resources:
-                  type: object
-                type:
-                  type: string
-                version:
-                  type: string
-              required:
-              - project
-              - region
-              - instance
-              - type
-              type: object
-            storage:
-              properties:
-                image:
-                  type: string
-                resources:
-                  type: object
-                version:
-                  type: string
-                volumeClaimTemplate:
-                  type: object
-              type: object
-          type: object
-        status:
-          properties:
-            components:
-              items:
-                properties:
-                  group:
-                    type: string
-                  kind:
-                    type: string
-                  link:
-                    type: string
-                  name:
-                    type: string
-                  pdb:
-                    properties:
-                      currenthealthy:
-                        format: int32
-                        type: integer
-                      desiredhealthy:
-                        format: int32
-                        type: integer
-                    required:
-                    - currenthealthy
-                    - desiredhealthy
-                    type: object
-                  status:
-                    type: string
-                  sts:
-                    properties:
-                      currentcount:
-                        format: int32
-                        type: integer
-                      progress:
-                        format: int32
-                        type: integer
-                      readycount:
-                        format: int32
-                        type: integer
-                      replicas:
-                        format: int32
-                        type: integer
-                    required:
-                    - replicas
-                    - readycount
-                    - currentcount
-                    - progress
-                    type: object
-                type: object
-              type: array
-            conditions:
-              items:
-                properties:
-                  lastTransitionTime:
-                    format: date-time
-                    type: string
-                  lastUpdateTime:
-                    format: date-time
-                    type: string
-                  message:
-                    type: string
-                  reason:
-                    type: string
-                  status:
-                    type: string
-                  type:
-                    type: string
-                required:
-                - type
-                - status
-                type: object
-              type: array
-            observedGeneration:
-              format: int64
-              type: integer
-          type: object
-  version: v1alpha1
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
diff --git a/config/crds/airflow_v1alpha1_airflowcluster.yaml b/config/crds/airflow_v1alpha1_airflowcluster.yaml
deleted file mode 100644
index ca3cbef..0000000
--- a/config/crds/airflow_v1alpha1_airflowcluster.yaml
+++ /dev/null
@@ -1,373 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: apiextensions.k8s.io/v1beta1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  labels:
-    controller-tools.k8s.io: "1.0"
-  name: airflowclusters.airflow.k8s.io
-spec:
-  group: airflow.k8s.io
-  names:
-    kind: AirflowCluster
-    plural: airflowclusters
-  scope: Namespaced
-  validation:
-    openAPIV3Schema:
-      properties:
-        apiVersion:
-          type: string
-        kind:
-          type: string
-        metadata:
-          type: object
-        spec:
-          properties:
-            affinity:
-              type: object
-            airflowbase:
-              type: object
-            annotations:
-              type: object
-            config:
-              properties:
-                airflow:
-                  type: object
-                airflowsecret:
-                  items:
-                    type: object
-                  type: array
-              type: object
-            dags:
-              properties:
-                gcs:
-                  properties:
-                    bucket:
-                      type: string
-                    once:
-                      type: boolean
-                  type: object
-                git:
-                  properties:
-                    branch:
-                      type: string
-                    cred:
-                      type: object
-                    once:
-                      type: boolean
-                    repo:
-                      type: string
-                    rev:
-                      type: string
-                    user:
-                      type: string
-                  required:
-                  - repo
-                  type: object
-                nfspv:
-                  type: object
-                storage:
-                  properties:
-                    config:
-                      type: object
-                    secretRef:
-                      type: object
-                    storageprovider:
-                      type: string
-                  required:
-                  - storageprovider
-                  type: object
-                subdir:
-                  type: string
-              type: object
-            executor:
-              type: string
-            flower:
-              properties:
-                image:
-                  type: string
-                replicas:
-                  format: int32
-                  type: integer
-                resources:
-                  type: object
-                version:
-                  type: string
-              type: object
-            labels:
-              type: object
-            memoryStore:
-              properties:
-                alternativeLocationId:
-                  type: string
-                authorizedNetwork:
-                  type: string
-                locationId:
-                  type: string
-                maxMemoryPolicy:
-                  type: string
-                memorySizeGb:
-                  format: int64
-                  type: integer
-                notifyKeyspaceEvents:
-                  type: string
-                project:
-                  type: string
-                redisConfigs:
-                  type: object
-                redisVersion:
-                  type: string
-                region:
-                  type: string
-                status:
-                  properties:
-                    components:
-                      items:
-                        properties:
-                          group:
-                            type: string
-                          kind:
-                            type: string
-                          link:
-                            type: string
-                          name:
-                            type: string
-                          pdb:
-                            properties:
-                              currenthealthy:
-                                format: int32
-                                type: integer
-                              desiredhealthy:
-                                format: int32
-                                type: integer
-                            required:
-                            - currenthealthy
-                            - desiredhealthy
-                            type: object
-                          status:
-                            type: string
-                          sts:
-                            properties:
-                              currentcount:
-                                format: int32
-                                type: integer
-                              progress:
-                                format: int32
-                                type: integer
-                              readycount:
-                                format: int32
-                                type: integer
-                              replicas:
-                                format: int32
-                                type: integer
-                            required:
-                            - replicas
-                            - readycount
-                            - currentcount
-                            - progress
-                            type: object
-                        type: object
-                      type: array
-                    conditions:
-                      items:
-                        properties:
-                          lastTransitionTime:
-                            format: date-time
-                            type: string
-                          lastUpdateTime:
-                            format: date-time
-                            type: string
-                          message:
-                            type: string
-                          reason:
-                            type: string
-                          status:
-                            type: string
-                          type:
-                            type: string
-                        required:
-                        - type
-                        - status
-                        type: object
-                      type: array
-                    createTime:
-                      type: string
-                    currentLocationId:
-                      type: string
-                    host:
-                      type: string
-                    observedGeneration:
-                      format: int64
-                      type: integer
-                    port:
-                      format: int64
-                      type: integer
-                    state:
-                      type: string
-                    statusMessage:
-                      type: string
-                  type: object
-                tier:
-                  type: string
-              required:
-              - project
-              - region
-              type: object
-            nodeSelector:
-              type: object
-            redis:
-              properties:
-                additionalargs:
-                  type: string
-                image:
-                  type: string
-                operator:
-                  type: boolean
-                redisHost:
-                  type: string
-                redisPassword:
-                  type: boolean
-                redisPort:
-                  type: string
-                resources:
-                  type: object
-                version:
-                  type: string
-                volumeClaimTemplate:
-                  type: object
-              type: object
-            scheduler:
-              properties:
-                database:
-                  type: string
-                dbuser:
-                  type: string
-                image:
-                  type: string
-                resources:
-                  type: object
-                version:
-                  type: string
-              type: object
-            ui:
-              properties:
-                image:
-                  type: string
-                replicas:
-                  format: int32
-                  type: integer
-                resources:
-                  type: object
-                version:
-                  type: string
-              type: object
-            worker:
-              properties:
-                image:
-                  type: string
-                replicas:
-                  format: int32
-                  type: integer
-                resources:
-                  type: object
-                version:
-                  type: string
-              type: object
-          type: object
-        status:
-          properties:
-            components:
-              items:
-                properties:
-                  group:
-                    type: string
-                  kind:
-                    type: string
-                  link:
-                    type: string
-                  name:
-                    type: string
-                  pdb:
-                    properties:
-                      currenthealthy:
-                        format: int32
-                        type: integer
-                      desiredhealthy:
-                        format: int32
-                        type: integer
-                    required:
-                    - currenthealthy
-                    - desiredhealthy
-                    type: object
-                  status:
-                    type: string
-                  sts:
-                    properties:
-                      currentcount:
-                        format: int32
-                        type: integer
-                      progress:
-                        format: int32
-                        type: integer
-                      readycount:
-                        format: int32
-                        type: integer
-                      replicas:
-                        format: int32
-                        type: integer
-                    required:
-                    - replicas
-                    - readycount
-                    - currentcount
-                    - progress
-                    type: object
-                type: object
-              type: array
-            conditions:
-              items:
-                properties:
-                  lastTransitionTime:
-                    format: date-time
-                    type: string
-                  lastUpdateTime:
-                    format: date-time
-                    type: string
-                  message:
-                    type: string
-                  reason:
-                    type: string
-                  status:
-                    type: string
-                  type:
-                    type: string
-                required:
-                - type
-                - status
-                type: object
-              type: array
-            observedGeneration:
-              format: int64
-              type: integer
-          type: object
-  version: v1alpha1
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml
index fd64183..f82fbf3 100644
--- a/config/default/kustomization.yaml
+++ b/config/default/kustomization.yaml
@@ -1,49 +1,74 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
 # Adds namespace to all resources.
-namespace: airflowop-system
+namespace: airflow-on-k8s-operator-system
 
 # 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: airflowop-
+namePrefix: airflow-on-k8s-operator-
 
 # Labels to add to all resources and selectors.
 #commonLabels:
 #  someName: someValue
 
-# Each entry in this list must resolve to an existing
-# resource definition in YAML.  These are the resource
-# files that kustomize reads, modifies and emits as a
-# YAML string, with resources separated by document
-# markers ("---").
-resources:
-- ./rbac/rbac_role.yaml
-- ./rbac/rbac_role_binding.yaml
-- ./manager/manager.yaml
+bases:
+- ../crd
+- ../rbac
+- ../manager
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml
+#- ../webhook
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
+#- ../certmanager
+# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 
+#- ../prometheus
 
-patches:
-- manager_image_patch.yaml
+patchesStrategicMerge:
+  # Protect the /metrics endpoint by putting it behind auth.
+  # Only one of manager_auth_proxy_patch.yaml and
+  # manager_prometheus_metrics_patch.yaml should be enabled.
+- manager_auth_proxy_patch.yaml
+  # If you want your controller-manager to expose the /metrics
+  # endpoint w/o any authn/z, uncomment the following line and
+  # comment manager_auth_proxy_patch.yaml.
+  # Only one of manager_auth_proxy_patch.yaml and
+  # manager_prometheus_metrics_patch.yaml should be enabled.
+#- manager_prometheus_metrics_patch.yaml
 
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml
+#- manager_webhook_patch.yaml
+
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
+# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
+# 'CERTMANAGER' needs to be enabled to use ca injection
+#- webhookcainjection_patch.yaml
+
+# the following config is for teaching kustomize how to do var substitution
 vars:
-- name: WEBHOOK_SECRET_NAME
-  objref:
-    kind: Secret
-    name: webhook-server-secret
-    apiVersion: v1
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
+#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
+#  objref:
+#    kind: Certificate
+#    group: cert-manager.io
+#    version: v1alpha2
+#    name: serving-cert # this name should match the one in certificate.yaml
+#  fieldref:
+#    fieldpath: metadata.namespace
+#- name: CERTIFICATE_NAME
+#  objref:
+#    kind: Certificate
+#    group: cert-manager.io
+#    version: v1alpha2
+#    name: serving-cert # this name should match the one in certificate.yaml
+#- name: SERVICE_NAMESPACE # namespace of the service
+#  objref:
+#    kind: Service
+#    version: v1
+#    name: webhook-service
+#  fieldref:
+#    fieldpath: metadata.namespace
+#- name: SERVICE_NAME
+#  objref:
+#    kind: Service
+#    version: v1
+#    name: webhook-service
diff --git a/config/default/manager/manager.yaml b/config/default/manager/manager.yaml
deleted file mode 100644
index 9dd4c7f..0000000
--- a/config/default/manager/manager.yaml
+++ /dev/null
@@ -1,98 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: v1
-kind: Namespace
-metadata:
-  labels:
-    controller-tools.k8s.io: "1.0"
-  name: system
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: controller-manager-service
-  namespace: system
-  labels:
-    control-plane: controller-manager
-    controller-tools.k8s.io: "1.0"
-spec:
-  selector:
-    control-plane: controller-manager
-    controller-tools.k8s.io: "1.0"
-  ports:
-  - port: 443
----
-apiVersion: apps/v1
-kind: StatefulSet
-metadata:
-  name: controller-manager
-  namespace: system
-  labels:
-    control-plane: controller-manager
-    controller-tools.k8s.io: "1.0"
-spec:
-  selector:
-    matchLabels:
-      control-plane: controller-manager
-      controller-tools.k8s.io: "1.0"
-  serviceName: controller-manager-service
-  template:
-    metadata:
-      labels:
-        control-plane: controller-manager
-        controller-tools.k8s.io: "1.0"
-    spec:
-      containers:
-      - command:
-        - /root/manager
-        image: controller:latest
-        imagePullPolicy: Always
-        name: manager
-        env:
-          - name: POD_NAMESPACE
-            valueFrom:
-              fieldRef:
-                fieldPath: metadata.namespace
-          - name: SECRET_NAME
-            value: $(WEBHOOK_SECRET_NAME)
-        resources:
-          limits:
-            cpu: 100m
-            memory: 30Mi
-          requests:
-            cpu: 100m
-            memory: 20Mi
-        ports:
-        - containerPort: 9876
-          name: webhook-server
-          protocol: TCP
-        volumeMounts:
-        - mountPath: /tmp/cert
-          name: cert
-          readOnly: true
-      terminationGracePeriodSeconds: 10
-      volumes:
-      - name: cert
-        secret:
-          defaultMode: 420
-          secretName: webhook-server-secret
----
-apiVersion: v1
-kind: Secret
-metadata:
-  name: webhook-server-secret
-  namespace: system
diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml
new file mode 100644
index 0000000..61cb5e7
--- /dev/null
+++ b/config/default/manager_auth_proxy_patch.yaml
@@ -0,0 +1,25 @@
+# This patch inject a sidecar container which is a HTTP proxy for the controller manager,
+# it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller-manager
+  namespace: system
+spec:
+  template:
+    spec:
+      containers:
+      - name: kube-rbac-proxy
+        image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.1
+        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
+      - name: manager
+        args:
+        - "--metrics-addr=127.0.0.1:8080"
+        - "--enable-leader-election"
diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml
deleted file mode 100644
index 3b35de1..0000000
--- a/config/default/manager_image_patch.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: apps/v1
-kind: StatefulSet
-metadata:
-  name: controller-manager
-  namespace: system
-spec:
-  template:
-    spec:
-      containers:
-      # Change the value of image field below to your controller image URL
-      - image: gcr.io/kubeflow-193622/airflow-operator:db92567
-        name: manager
----
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  name: manager-rolebinding
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: cluster-admin
diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml
new file mode 100644
index 0000000..738de35
--- /dev/null
+++ b/config/default/manager_webhook_patch.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller-manager
+  namespace: system
+spec:
+  template:
+    spec:
+      containers:
+      - name: manager
+        ports:
+        - containerPort: 9443
+          name: webhook-server
+          protocol: TCP
+        volumeMounts:
+        - mountPath: /tmp/k8s-webhook-server/serving-certs
+          name: cert
+          readOnly: true
+      volumes:
+      - name: cert
+        secret:
+          defaultMode: 420
+          secretName: webhook-server-cert
diff --git a/config/default/rbac/rbac_role.yaml b/config/default/rbac/rbac_role.yaml
deleted file mode 100644
index bd3fb46..0000000
--- a/config/default/rbac/rbac_role.yaml
+++ /dev/null
@@ -1,191 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  creationTimestamp: null
-  name: manager-role
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - statefulsets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - services
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - configmaps
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - airflow.k8s.io
-  resources:
-  - airflowbases
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - airflow.k8s.io
-  resources:
-  - airflowclusters
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - app.k8s.io
-  resources:
-  - applications
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - policy
-  resources:
-  - poddisruptionbudgets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - rbac.authorization.k8s.io
-  resources:
-  - rolebindings
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - storage.k8s.io
-  resources:
-  - storageclasses
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - admissionregistration.k8s.io
-  resources:
-  - mutatingwebhookconfigurations
-  - validatingwebhookconfigurations
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - services
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
diff --git a/config/default/rbac/rbac_role_binding.yaml b/config/default/rbac/rbac_role_binding.yaml
deleted file mode 100644
index 80cd916..0000000
--- a/config/default/rbac/rbac_role_binding.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  creationTimestamp: null
-  name: manager-rolebinding
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: manager-role
-subjects:
-- kind: ServiceAccount
-  name: default
-  namespace: system
diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml
new file mode 100644
index 0000000..7e79bf9
--- /dev/null
+++ b/config/default/webhookcainjection_patch.yaml
@@ -0,0 +1,15 @@
+# This patch add annotation to admission webhook config and
+# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
+apiVersion: admissionregistration.k8s.io/v1beta1
+kind: MutatingWebhookConfiguration
+metadata:
+  name: mutating-webhook-configuration
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+---
+apiVersion: admissionregistration.k8s.io/v1beta1
+kind: ValidatingWebhookConfiguration
+metadata:
+  name: validating-webhook-configuration
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
new file mode 100644
index 0000000..5c5f0b8
--- /dev/null
+++ b/config/manager/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- manager.yaml
diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml
new file mode 100644
index 0000000..b6c85a5
--- /dev/null
+++ b/config/manager/manager.yaml
@@ -0,0 +1,39 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  labels:
+    control-plane: controller-manager
+  name: system
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller-manager
+  namespace: system
+  labels:
+    control-plane: controller-manager
+spec:
+  selector:
+    matchLabels:
+      control-plane: controller-manager
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        control-plane: controller-manager
+    spec:
+      containers:
+      - command:
+        - /manager
+        args:
+        - --enable-leader-election
+        image: controller:latest
+        name: manager
+        resources:
+          limits:
+            cpu: 100m
+            memory: 30Mi
+          requests:
+            cpu: 100m
+            memory: 20Mi
+      terminationGracePeriodSeconds: 10
diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml
new file mode 100644
index 0000000..ed13716
--- /dev/null
+++ b/config/prometheus/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- monitor.yaml
diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml
new file mode 100644
index 0000000..e2d9b08
--- /dev/null
+++ b/config/prometheus/monitor.yaml
@@ -0,0 +1,15 @@
+
+# Prometheus Monitor Service (Metrics)
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+  labels:
+    control-plane: controller-manager
+  name: controller-manager-metrics-monitor
+  namespace: system
+spec:
+  endpoints:
+    - path: /metrics
+      port: https
+  selector:
+    control-plane: controller-manager
diff --git a/config/rbac/airflowbase_editor_role.yaml b/config/rbac/airflowbase_editor_role.yaml
new file mode 100644
index 0000000..7ae48e4
--- /dev/null
+++ b/config/rbac/airflowbase_editor_role.yaml
@@ -0,0 +1,26 @@
+# permissions to do edit airflowbases.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: airflowbase-editor-role
+rules:
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowbases
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowbases/status
+  verbs:
+  - get
+  - patch
+  - update
diff --git a/config/rbac/airflowbase_viewer_role.yaml b/config/rbac/airflowbase_viewer_role.yaml
new file mode 100644
index 0000000..d51d5a6
--- /dev/null
+++ b/config/rbac/airflowbase_viewer_role.yaml
@@ -0,0 +1,20 @@
+# permissions to do viewer airflowbases.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: airflowbase-viewer-role
+rules:
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowbases
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowbases/status
+  verbs:
+  - get
diff --git a/config/rbac/airflowcluster_editor_role.yaml b/config/rbac/airflowcluster_editor_role.yaml
new file mode 100644
index 0000000..8888d10
--- /dev/null
+++ b/config/rbac/airflowcluster_editor_role.yaml
@@ -0,0 +1,26 @@
+# permissions to do edit airflowclusters.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: airflowcluster-editor-role
+rules:
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowclusters
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowclusters/status
+  verbs:
+  - get
+  - patch
+  - update
diff --git a/config/rbac/airflowcluster_viewer_role.yaml b/config/rbac/airflowcluster_viewer_role.yaml
new file mode 100644
index 0000000..54502ec
--- /dev/null
+++ b/config/rbac/airflowcluster_viewer_role.yaml
@@ -0,0 +1,20 @@
+# permissions to do viewer airflowclusters.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: airflowcluster-viewer-role
+rules:
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowclusters
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowclusters/status
+  verbs:
+  - get
diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml
new file mode 100644
index 0000000..618f5e4
--- /dev/null
+++ b/config/rbac/auth_proxy_role.yaml
@@ -0,0 +1,13 @@
+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/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml
new file mode 100644
index 0000000..48ed1e4
--- /dev/null
+++ b/config/rbac/auth_proxy_role_binding.yaml
@@ -0,0 +1,12 @@
+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: default
+  namespace: system
diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml
new file mode 100644
index 0000000..6cf656b
--- /dev/null
+++ b/config/rbac/auth_proxy_service.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    control-plane: controller-manager
+  name: controller-manager-metrics-service
+  namespace: system
+spec:
+  ports:
+  - name: https
+    port: 8443
+    targetPort: https
+  selector:
+    control-plane: controller-manager
diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml
new file mode 100644
index 0000000..817f1fe
--- /dev/null
+++ b/config/rbac/kustomization.yaml
@@ -0,0 +1,11 @@
+resources:
+- role.yaml
+- role_binding.yaml
+- leader_election_role.yaml
+- leader_election_role_binding.yaml
+# Comment the following 3 lines if you want to disable
+# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
+# which protects your /metrics endpoint.
+- auth_proxy_service.yaml
+- auth_proxy_role.yaml
+- auth_proxy_role_binding.yaml
diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml
new file mode 100644
index 0000000..eaa7915
--- /dev/null
+++ b/config/rbac/leader_election_role.yaml
@@ -0,0 +1,32 @@
+# permissions to do leader election.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: leader-election-role
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - ""
+  resources:
+  - configmaps/status
+  verbs:
+  - get
+  - update
+  - patch
+- apiGroups:
+  - ""
+  resources:
+  - events
+  verbs:
+  - create
diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml
new file mode 100644
index 0000000..eed1690
--- /dev/null
+++ b/config/rbac/leader_election_role_binding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: leader-election-rolebinding
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: leader-election-role
+subjects:
+- kind: ServiceAccount
+  name: default
+  namespace: system
diff --git a/config/rbac/rbac_role.yaml b/config/rbac/rbac_role.yaml
deleted file mode 100644
index bd3fb46..0000000
--- a/config/rbac/rbac_role.yaml
+++ /dev/null
@@ -1,191 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  creationTimestamp: null
-  name: manager-role
-rules:
-- apiGroups:
-  - apps
-  resources:
-  - statefulsets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - services
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - configmaps
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - airflow.k8s.io
-  resources:
-  - airflowbases
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - airflow.k8s.io
-  resources:
-  - airflowclusters
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - app.k8s.io
-  resources:
-  - applications
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - policy
-  resources:
-  - poddisruptionbudgets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - rbac.authorization.k8s.io
-  resources:
-  - rolebindings
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - serviceaccounts
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - storage.k8s.io
-  resources:
-  - storageclasses
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - admissionregistration.k8s.io
-  resources:
-  - mutatingwebhookconfigurations
-  - validatingwebhookconfigurations
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - secrets
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
-- apiGroups:
-  - ""
-  resources:
-  - services
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - update
-  - patch
-  - delete
diff --git a/config/rbac/rbac_role_binding.yaml b/config/rbac/rbac_role_binding.yaml
deleted file mode 100644
index 80cd916..0000000
--- a/config/rbac/rbac_role_binding.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
-  creationTimestamp: null
-  name: manager-rolebinding
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: manager-role
-subjects:
-- kind: ServiceAccount
-  name: default
-  namespace: system
diff --git a/config/crd/bases/role.yaml b/config/rbac/role.yaml
similarity index 84%
rename from config/crd/bases/role.yaml
rename to config/rbac/role.yaml
index ef5a8be..7c2331d 100644
--- a/config/crd/bases/role.yaml
+++ b/config/rbac/role.yaml
@@ -47,7 +47,7 @@ rules:
   - update
   - watch
 - apiGroups:
-  - airflow.k8s.io
+  - airflow.apache.org
   resources:
   - airflowbases
   verbs:
@@ -59,7 +59,15 @@ rules:
   - update
   - watch
 - apiGroups:
-  - airflow.k8s.io
+  - airflow.apache.org
+  resources:
+  - airflowbases/status
+  verbs:
+  - get
+  - patch
+  - update
+- apiGroups:
+  - airflow.apache.org
   resources:
   - airflowclusters
   verbs:
@@ -71,6 +79,14 @@ rules:
   - update
   - watch
 - apiGroups:
+  - airflow.apache.org
+  resources:
+  - airflowclusters/status
+  verbs:
+  - get
+  - patch
+  - update
+- apiGroups:
   - app.k8s.io
   resources:
   - applications
diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml
new file mode 100644
index 0000000..8f26587
--- /dev/null
+++ b/config/rbac/role_binding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: manager-rolebinding
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: manager-role
+subjects:
+- kind: ServiceAccount
+  name: default
+  namespace: system
diff --git a/config/samples/airflow_v1alpha1_airflowbase.yaml b/config/samples/airflow_v1alpha1_airflowbase.yaml
index 5ac0be7..d415f9a 100644
--- a/config/samples/airflow_v1alpha1_airflowbase.yaml
+++ b/config/samples/airflow_v1alpha1_airflowbase.yaml
@@ -1,24 +1,6 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowBase
 metadata:
-  labels:
-    controller-tools.k8s.io: "1.0"
   name: airflowbase-sample
 spec:
   # Add fields here
diff --git a/config/samples/airflow_v1alpha1_airflowcluster.yaml b/config/samples/airflow_v1alpha1_airflowcluster.yaml
index 14b9145..415de17 100644
--- a/config/samples/airflow_v1alpha1_airflowcluster.yaml
+++ b/config/samples/airflow_v1alpha1_airflowcluster.yaml
@@ -1,24 +1,6 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
-  labels:
-    controller-tools.k8s.io: "1.0"
   name: airflowcluster-sample
 spec:
   # Add fields here
diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml
new file mode 100644
index 0000000..9cf2613
--- /dev/null
+++ b/config/webhook/kustomization.yaml
@@ -0,0 +1,6 @@
+resources:
+- manifests.yaml
+- service.yaml
+
+configurations:
+- kustomizeconfig.yaml
diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml
new file mode 100644
index 0000000..25e21e3
--- /dev/null
+++ b/config/webhook/kustomizeconfig.yaml
@@ -0,0 +1,25 @@
+# the following config is for teaching kustomize where to look at when substituting vars.
+# It requires kustomize v2.1.0 or newer to work properly.
+nameReference:
+- kind: Service
+  version: v1
+  fieldSpecs:
+  - kind: MutatingWebhookConfiguration
+    group: admissionregistration.k8s.io
+    path: webhooks/clientConfig/service/name
+  - kind: ValidatingWebhookConfiguration
+    group: admissionregistration.k8s.io
+    path: webhooks/clientConfig/service/name
+
+namespace:
+- kind: MutatingWebhookConfiguration
+  group: admissionregistration.k8s.io
+  path: webhooks/clientConfig/service/namespace
+  create: true
+- kind: ValidatingWebhookConfiguration
+  group: admissionregistration.k8s.io
+  path: webhooks/clientConfig/service/namespace
+  create: true
+
+varReference:
+- path: metadata/annotations
diff --git a/config/crd/bases/manifests.yaml b/config/webhook/manifests.yaml
similarity index 100%
rename from config/crd/bases/manifests.yaml
rename to config/webhook/manifests.yaml
diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml
new file mode 100644
index 0000000..31e0f82
--- /dev/null
+++ b/config/webhook/service.yaml
@@ -0,0 +1,12 @@
+
+apiVersion: v1
+kind: Service
+metadata:
+  name: webhook-service
+  namespace: system
+spec:
+  ports:
+    - port: 443
+      targetPort: 9443
+  selector:
+    control-plane: controller-manager
diff --git a/pkg/controller/airflowbase/airflowbase_controller.go b/controllers/airflowbase_controller.go
similarity index 89%
rename from pkg/controller/airflowbase/airflowbase_controller.go
rename to controllers/airflowbase_controller.go
index adeebcf..bb78421 100644
--- a/pkg/controller/airflowbase/airflowbase_controller.go
+++ b/controllers/airflowbase_controller.go
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package airflowbase
+package controllers
 
 // Major:
 // TODO retry.Retry
@@ -25,10 +25,17 @@ package airflowbase
 // TODO documentation for CRD spec
 
 import (
+	"context"
+
+	"github.com/go-logr/logr"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
 	"encoding/base64"
-	alpha1 "github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/application"
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/common"
+	alpha1 "github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
+	"github.com/apache/airflow-on-k8s-operator/controllers/application"
+	"github.com/apache/airflow-on-k8s-operator/controllers/common"
 	app "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -40,30 +47,50 @@ import (
 	"time"
 )
 
-// Add creates a new AirflowBase Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
-// and Start it when the Manager is Started.
-func Add(mgr manager.Manager) error {
-	r := newReconciler(mgr)
-	return r.Controller(nil)
+// AirflowBaseReconciler reconciles a AirflowBase object
+type AirflowBaseReconciler struct {
+	client.Client
+	Log    logr.Logger
+	Scheme *runtime.Scheme
+}
+
+// +kubebuilder:rbac:groups=airflow.apache.org,resources=airflowbases,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=airflow.apache.org,resources=airflowbases/status,verbs=get;update;patch
+
+// Reconcile - Dummy TODO remove this
+func (r *AirflowBaseReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
+	_ = context.Background()
+	_ = r.Log.WithValues("airflowbase", req.NamespacedName)
+
+	// your logic here
+
+	return ctrl.Result{}, nil
+}
+
+// SetupWithManager - called by main
+func (r *AirflowBaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&alpha1.AirflowBase{}).
+		Complete(abReconciler(mgr))
 }
 
-func newReconciler(mgr manager.Manager) *gr.Reconciler {
+func abReconciler(mgr manager.Manager) *gr.Reconciler {
 	return gr.
 		WithManager(mgr).
-		For(&alpha1.AirflowBase{}, alpha1.SchemeGroupVersion).
+		For(&alpha1.AirflowBase{}, alpha1.GroupVersion).
 		Using(&MySQL{}).
 		Using(&Postgres{}).
 		Using(&SQLProxy{}).
 		Using(&NFS{}).
 		Using(&AirflowBase{}).
-		WithErrorHandler(handleError).
+		WithErrorHandler(abHandleError).
 		WithValidator(validate).
 		WithDefaulter(applyDefaults).
 		RegisterSchemeBuilder(app.SchemeBuilder).
 		Build()
 }
 
-func handleError(resource interface{}, err error, kind string) {
+func abHandleError(resource interface{}, err error, kind string) {
 	ab := resource.(*alpha1.AirflowBase)
 	if err != nil {
 		ab.Status.SetError("ErrorSeen", err.Error())
diff --git a/pkg/controller/airflowcluster/airflowcluster_controller.go b/controllers/airflowcluster_controller.go
similarity index 92%
rename from pkg/controller/airflowcluster/airflowcluster_controller.go
rename to controllers/airflowcluster_controller.go
index d565566..aadb4bc 100644
--- a/pkg/controller/airflowcluster/airflowcluster_controller.go
+++ b/controllers/airflowcluster_controller.go
@@ -13,14 +13,24 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package airflowcluster
+package controllers
 
 import (
 	"context"
 	"encoding/base64"
-	alpha1 "github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/application"
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/common"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/go-logr/logr"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	alpha1 "github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
+	"github.com/apache/airflow-on-k8s-operator/controllers/application"
+	"github.com/apache/airflow-on-k8s-operator/controllers/common"
 	app "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -34,10 +44,6 @@ import (
 	"sigs.k8s.io/controller-reconciler/pkg/reconciler/manager/k8s"
 	"sigs.k8s.io/controller-reconciler/pkg/status"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sort"
-	"strconv"
-	"strings"
-	"time"
 )
 
 const (
@@ -49,11 +55,19 @@ const (
 	airflowDagsBase = airflowHome + "/dags/"
 )
 
+// Add creates a new AirflowBase Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
+// and Start it when the Manager is Started.
+
+// AirflowClusterReconciler reconciles a AirflowCluster object
+type AirflowClusterReconciler struct {
+	client.Client
+	Log    logr.Logger
+	Scheme *runtime.Scheme
+}
+
 // +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=,resources=services,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=,resources=configmaps,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=airflow.k8s.io,resources=airflowbases,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=airflow.k8s.io,resources=airflowclusters,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=app.k8s.io,resources=applications,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete
@@ -61,18 +75,31 @@ const (
 // +kubebuilder:rbac:groups=,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch;create;update;patch;delete
 
-// Add creates a new AirflowBase Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
-// and Start it when the Manager is Started.
-func Add(mgr manager.Manager) error {
-	r := newReconciler(mgr)
-	return r.Controller(nil)
+// +kubebuilder:rbac:groups=airflow.apache.org,resources=airflowclusters,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=airflow.apache.org,resources=airflowclusters/status,verbs=get;update;patch
+
+// Reconcile - Dummy TODO remove this
+func (r *AirflowClusterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
+	_ = context.Background()
+	_ = r.Log.WithValues("airflowcluster", req.NamespacedName)
+
+	// your logic here
+
+	return ctrl.Result{}, nil
+}
+
+// SetupWithManager - called by main
+func (r *AirflowClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&alpha1.AirflowCluster{}).
+		Complete(acReconciler(mgr))
 }
 
-func newReconciler(mgr manager.Manager) *gr.Reconciler {
+func acReconciler(mgr manager.Manager) *gr.Reconciler {
 	return gr.
 		WithManager(mgr).
 		WithResourceManager(redis.Getter(context.TODO())).
-		For(&alpha1.AirflowCluster{}, alpha1.SchemeGroupVersion).
+		For(&alpha1.AirflowCluster{}, alpha1.GroupVersion).
 		Using(&UI{}).
 		Using(&Redis{}).
 		Using(&MemoryStore{}).
@@ -80,14 +107,14 @@ func newReconciler(mgr manager.Manager) *gr.Reconciler {
 		Using(&Scheduler{}).
 		Using(&Worker{}).
 		Using(&Cluster{}).
-		WithErrorHandler(handleError).
-		WithValidator(validate).
-		WithDefaulter(applyDefaults).
+		WithErrorHandler(acHandleError).
+		WithValidator(acValidate).
+		WithDefaulter(acApplyDefaults).
 		RegisterSchemeBuilder(app.SchemeBuilder).
 		Build()
 }
 
-func handleError(resource interface{}, err error, kind string) {
+func acHandleError(resource interface{}, err error, kind string) {
 	ac := resource.(*alpha1.AirflowCluster)
 	if err != nil {
 		ac.Status.SetError("ErrorSeen", err.Error())
@@ -96,12 +123,12 @@ func handleError(resource interface{}, err error, kind string) {
 	}
 }
 
-func validate(resource interface{}) error {
+func acValidate(resource interface{}) error {
 	ac := resource.(*alpha1.AirflowCluster)
 	return ac.Validate()
 }
 
-func applyDefaults(resource interface{}) {
+func acApplyDefaults(resource interface{}) {
 	ac := resource.(*alpha1.AirflowCluster)
 	ac.ApplyDefaults()
 }
@@ -160,7 +187,7 @@ func updateSts(o *reconciler.Object, v interface{}) (*appsv1.StatefulSet, *commo
 	return sts, r
 }
 
-func templateValue(r *alpha1.AirflowCluster, dependent []reconciler.Object, component string, label, selector, ports map[string]string) *common.TemplateValue {
+func acTemplateValue(r *alpha1.AirflowCluster, dependent []reconciler.Object, component string, label, selector, ports map[string]string) *common.TemplateValue {
 	b := k8s.GetItem(dependent, &alpha1.AirflowBase{}, r.Spec.AirflowBaseRef.Name, r.Namespace)
 	base := b.(*alpha1.AirflowBase)
 	return &common.TemplateValue{
@@ -418,7 +445,7 @@ func (c *Cluster) Objects(rsrc interface{}, rsrclabels map[string]string, observ
 	}
 	delete(selectors, gr.LabelUsing)
 
-	ngdata := templateValue(r, dependent, common.ValueAirflowComponentCluster, rsrclabels, selectors, nil)
+	ngdata := acTemplateValue(r, dependent, common.ValueAirflowComponentCluster, rsrclabels, selectors, nil)
 	ngdata.Expected = aggregated
 
 	return k8s.NewObjects().
@@ -469,7 +496,7 @@ func (s *UI) Objects(rsrc interface{}, rsrclabels map[string]string, observed, d
 		return []reconciler.Object{}, nil
 	}
 
-	ngdata := templateValue(r, dependent, common.ValueAirflowComponentUI, rsrclabels, rsrclabels, map[string]string{"web": "8080"})
+	ngdata := acTemplateValue(r, dependent, common.ValueAirflowComponentUI, rsrclabels, rsrclabels, map[string]string{"web": "8080"})
 	ngdata.Secret = map[string]string{
 		"password": base64.StdEncoding.EncodeToString(common.RandomAlphanumericString(16)),
 	}
@@ -526,7 +553,7 @@ func (s *Redis) Objects(rsrc interface{}, rsrclabels map[string]string, observed
 	if r.Spec.Redis == nil || r.Spec.Redis.RedisHost != "" {
 		return []reconciler.Object{}, nil
 	}
-	ngdata := templateValue(r, dependent, common.ValueAirflowComponentRedis, rsrclabels, rsrclabels, map[string]string{"redis": "6379"})
+	ngdata := acTemplateValue(r, dependent, common.ValueAirflowComponentRedis, rsrclabels, rsrclabels, map[string]string{"redis": "6379"})
 	ngdata.Secret = map[string]string{
 		"password": base64.StdEncoding.EncodeToString(common.RandomAlphanumericString(16)),
 	}
@@ -678,7 +705,7 @@ func (s *Scheduler) Objects(rsrc interface{}, rsrclabels map[string]string, obse
 		}
 	}
 
-	ngdata := templateValue(r, dependent, common.ValueAirflowComponentScheduler, rsrclabels, rsrclabels, nil)
+	ngdata := acTemplateValue(r, dependent, common.ValueAirflowComponentScheduler, rsrclabels, rsrclabels, nil)
 	bag.WithValue(ngdata).WithFolder("templates/")
 
 	if r.Spec.Executor == alpha1.ExecutorK8s {
@@ -739,7 +766,7 @@ func (s *Worker) Objects(rsrc interface{}, rsrclabels map[string]string, observe
 		return []reconciler.Object{}, nil
 	}
 
-	ngdata := templateValue(r, dependent, common.ValueAirflowComponentWorker, rsrclabels, rsrclabels, map[string]string{"wlog": "8793"})
+	ngdata := acTemplateValue(r, dependent, common.ValueAirflowComponentWorker, rsrclabels, rsrclabels, map[string]string{"wlog": "8793"})
 
 	return k8s.NewObjects().
 		WithValue(ngdata).
@@ -772,7 +799,7 @@ func (s *Flower) Objects(rsrc interface{}, rsrclabels map[string]string, observe
 	if r.Spec.MemoryStore != nil && r.Spec.MemoryStore.Status.Host == "" {
 		return []reconciler.Object{}, nil
 	}
-	ngdata := templateValue(r, dependent, common.ValueAirflowComponentFlower, rsrclabels, rsrclabels, map[string]string{"flower": "5555"})
+	ngdata := acTemplateValue(r, dependent, common.ValueAirflowComponentFlower, rsrclabels, rsrclabels, map[string]string{"flower": "5555"})
 
 	return k8s.NewObjects().
 		WithValue(ngdata).
diff --git a/pkg/controller/application/application.go b/controllers/application/application.go
similarity index 100%
rename from pkg/controller/application/application.go
rename to controllers/application/application.go
diff --git a/pkg/controller/application/application_suite_test.go b/controllers/application/application_suite_test.go
similarity index 100%
rename from pkg/controller/application/application_suite_test.go
rename to controllers/application/application_suite_test.go
diff --git a/pkg/controller/application/application_test.go b/controllers/application/application_test.go
similarity index 97%
rename from pkg/controller/application/application_test.go
rename to controllers/application/application_test.go
index bd630de..b66f326 100644
--- a/pkg/controller/application/application_test.go
+++ b/controllers/application/application_test.go
@@ -16,7 +16,7 @@
 package application_test
 
 import (
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/application"
+	"github.com/apache/airflow-on-k8s-operator/controllers/application"
 	app "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
diff --git a/pkg/controller/application/doc.go b/controllers/application/doc.go
similarity index 100%
rename from pkg/controller/application/doc.go
rename to controllers/application/doc.go
diff --git a/pkg/controller/common/common.go b/controllers/common/common.go
similarity index 97%
rename from pkg/controller/common/common.go
rename to controllers/common/common.go
index b712f63..405c504 100644
--- a/pkg/controller/common/common.go
+++ b/controllers/common/common.go
@@ -18,7 +18,7 @@ package common
 import (
 	"bytes"
 	"fmt"
-	alpha1 "github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
+	alpha1 "github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
 	corev1 "k8s.io/api/core/v1"
 	"math/rand"
 	"sigs.k8s.io/controller-reconciler/pkg/reconciler"
diff --git a/controllers/suite_test.go b/controllers/suite_test.go
new file mode 100644
index 0000000..b7ed27d
--- /dev/null
+++ b/controllers/suite_test.go
@@ -0,0 +1,82 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package controllers
+
+import (
+	"path/filepath"
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	airflowv1alpha1 "github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
+	"k8s.io/client-go/kubernetes/scheme"
+	"k8s.io/client-go/rest"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/envtest"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+	// +kubebuilder:scaffold:imports
+)
+
+// These tests use Ginkgo (BDD-style Go testing framework). Refer to
+// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
+
+var cfg *rest.Config
+var k8sClient client.Client
+var testEnv *envtest.Environment
+
+func TestAPIs(t *testing.T) {
+	RegisterFailHandler(Fail)
+
+	RunSpecsWithDefaultAndCustomReporters(t,
+		"Controller Suite",
+		[]Reporter{envtest.NewlineReporter{}})
+}
+
+var _ = BeforeSuite(func(done Done) {
+	logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
+
+	By("bootstrapping test environment")
+	testEnv = &envtest.Environment{
+		CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
+	}
+
+	var err error
+	cfg, err = testEnv.Start()
+	Expect(err).ToNot(HaveOccurred())
+	Expect(cfg).ToNot(BeNil())
+
+	err = airflowv1alpha1.AddToScheme(scheme.Scheme)
+	Expect(err).NotTo(HaveOccurred())
+
+	err = airflowv1alpha1.AddToScheme(scheme.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/docs/userguide.md b/docs/userguide.md
index 6aac99e..f745015 100644
--- a/docs/userguide.md
+++ b/docs/userguide.md
@@ -13,7 +13,7 @@ By default dags are refreshed every 5 minutes.
 To enable continuous sync, use git or gcs dag source with once disabled.
 
 ```yaml
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 ...
 spec:
diff --git a/go.mod b/go.mod
index e3935bc..bc05c31 100644
--- a/go.mod
+++ b/go.mod
@@ -3,12 +3,11 @@ module github.com/apache/airflow-on-k8s-operator
 go 1.13
 
 require (
-	github.com/go-logr/zapr v0.1.1 // indirect
+	github.com/go-logr/logr v0.1.0
 	github.com/kr/pretty v0.2.0 // indirect
 	github.com/kubernetes-sigs/application v0.8.1
 	github.com/onsi/ginkgo v1.11.0
 	github.com/onsi/gomega v1.8.1
-	golang.org/x/net v0.0.0-20191004110552-13f9640d40b9
 	k8s.io/api v0.17.2
 	k8s.io/apimachinery v0.17.2
 	k8s.io/client-go v0.17.1
diff --git a/go.sum b/go.sum
index 025f4d6..c06bf73 100644
--- a/go.sum
+++ b/go.sum
@@ -79,9 +79,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
 github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
-github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
-github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
 github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
 github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -135,8 +134,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff h1:kOkM9whyQYodu09SJ6W3NCsHG7crFaJILQ22Gozp3lg=
-github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -262,24 +259,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
-github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
@@ -367,7 +356,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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 h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
diff --git a/hack/sample/cloudsql-celery/base.yaml b/hack/sample/cloudsql-celery/base.yaml
index 592b720..45ff630 100644
--- a/hack/sample/cloudsql-celery/base.yaml
+++ b/hack/sample/cloudsql-celery/base.yaml
@@ -14,7 +14,7 @@
 # KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations
 # under the License.
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowBase
 metadata:
   name: cc-base
diff --git a/hack/sample/cloudsql-celery/cluster.yaml b/hack/sample/cloudsql-celery/cluster.yaml
index a4ec6dc..fcba099 100644
--- a/hack/sample/cloudsql-celery/cluster.yaml
+++ b/hack/sample/cloudsql-celery/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: cc-cluster
diff --git a/hack/sample/cloudsql-k8s/cluster.yaml b/hack/sample/cloudsql-k8s/cluster.yaml
index aeaab4c..0c92082 100644
--- a/hack/sample/cloudsql-k8s/cluster.yaml
+++ b/hack/sample/cloudsql-k8s/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: ck-cluster
diff --git a/hack/sample/cloudsql-local/cluster.yaml b/hack/sample/cloudsql-local/cluster.yaml
index 0c4434e..857936f 100644
--- a/hack/sample/cloudsql-local/cluster.yaml
+++ b/hack/sample/cloudsql-local/cluster.yaml
@@ -14,7 +14,7 @@
 # KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations
 # under the License.
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: cl-cluster
diff --git a/hack/sample/mysql-celery-gcs/cluster.yaml b/hack/sample/mysql-celery-gcs/cluster.yaml
index bd2d597..fb663b9 100644
--- a/hack/sample/mysql-celery-gcs/cluster.yaml
+++ b/hack/sample/mysql-celery-gcs/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: mcg-cluster
diff --git a/hack/sample/mysql-celery/base.yaml b/hack/sample/mysql-celery/base.yaml
index 11c2bc3..5720709 100644
--- a/hack/sample/mysql-celery/base.yaml
+++ b/hack/sample/mysql-celery/base.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowBase
 metadata:
   name: mc-base
diff --git a/hack/sample/mysql-celery/cluster.yaml b/hack/sample/mysql-celery/cluster.yaml
index 71c4a2e..f6c9d62 100644
--- a/hack/sample/mysql-celery/cluster.yaml
+++ b/hack/sample/mysql-celery/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: mc-cluster
diff --git a/hack/sample/mysql-k8s/cluster.yaml b/hack/sample/mysql-k8s/cluster.yaml
index 8817c4c..6cb858c 100644
--- a/hack/sample/mysql-k8s/cluster.yaml
+++ b/hack/sample/mysql-k8s/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: mk-cluster
diff --git a/hack/sample/mysql-local/cluster.yaml b/hack/sample/mysql-local/cluster.yaml
index ebf7b8c..78edd1f 100644
--- a/hack/sample/mysql-local/cluster.yaml
+++ b/hack/sample/mysql-local/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: ml-cluster
diff --git a/hack/sample/postgres-celery-memorystore/cluster.yaml b/hack/sample/postgres-celery-memorystore/cluster.yaml
index 5c72b2e..f6b15e7 100644
--- a/hack/sample/postgres-celery-memorystore/cluster.yaml
+++ b/hack/sample/postgres-celery-memorystore/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: pc-cluster
diff --git a/hack/sample/postgres-celery-redis/cluster.yaml b/hack/sample/postgres-celery-redis/cluster.yaml
index 2d0ca7d..09a8b01 100644
--- a/hack/sample/postgres-celery-redis/cluster.yaml
+++ b/hack/sample/postgres-celery-redis/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: pc-cluster
diff --git a/hack/sample/postgres-celery/base.yaml b/hack/sample/postgres-celery/base.yaml
index e728093..49412b7 100644
--- a/hack/sample/postgres-celery/base.yaml
+++ b/hack/sample/postgres-celery/base.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowBase
 metadata:
   name: pc-base
diff --git a/hack/sample/postgres-celery/cluster.yaml b/hack/sample/postgres-celery/cluster.yaml
index bfea43a..b42e1fc 100644
--- a/hack/sample/postgres-celery/cluster.yaml
+++ b/hack/sample/postgres-celery/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: pc-cluster
diff --git a/hack/sample/postgres-k8s/cluster.yaml b/hack/sample/postgres-k8s/cluster.yaml
index e5da34a..68fb0bb 100644
--- a/hack/sample/postgres-k8s/cluster.yaml
+++ b/hack/sample/postgres-k8s/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: pk-cluster
diff --git a/hack/sample/postgres-local/cluster.yaml b/hack/sample/postgres-local/cluster.yaml
index f3eeecc..0b33071 100644
--- a/hack/sample/postgres-local/cluster.yaml
+++ b/hack/sample/postgres-local/cluster.yaml
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-apiVersion: airflow.k8s.io/v1alpha1
+apiVersion: airflow.apache.org/v1alpha1
 kind: AirflowCluster
 metadata:
   name: pl-cluster
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..c083011
--- /dev/null
+++ b/main.go
@@ -0,0 +1,90 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"flag"
+	"os"
+
+	airflowv1alpha1 "github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
+	"github.com/apache/airflow-on-k8s-operator/controllers"
+	"k8s.io/apimachinery/pkg/runtime"
+	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/log/zap"
+	// +kubebuilder:scaffold:imports
+)
+
+var (
+	scheme   = runtime.NewScheme()
+	setupLog = ctrl.Log.WithName("setup")
+)
+
+func init() {
+	_ = clientgoscheme.AddToScheme(scheme)
+
+	_ = airflowv1alpha1.AddToScheme(scheme)
+	// +kubebuilder:scaffold:scheme
+}
+
+func main() {
+	var metricsAddr string
+	var enableLeaderElection bool
+	flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
+	flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
+		"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
+	flag.Parse()
+
+	ctrl.SetLogger(zap.New(func(o *zap.Options) {
+		o.Development = true
+	}))
+
+	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+		Scheme:             scheme,
+		MetricsBindAddress: metricsAddr,
+		LeaderElection:     enableLeaderElection,
+		Port:               9443,
+	})
+	if err != nil {
+		setupLog.Error(err, "unable to start manager")
+		os.Exit(1)
+	}
+
+	if err = (&controllers.AirflowBaseReconciler{
+		Client: mgr.GetClient(),
+		Log:    ctrl.Log.WithName("controllers").WithName("AirflowBase"),
+		Scheme: mgr.GetScheme(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "AirflowBase")
+		os.Exit(1)
+	}
+	if err = (&controllers.AirflowClusterReconciler{
+		Client: mgr.GetClient(),
+		Log:    ctrl.Log.WithName("controllers").WithName("AirflowCluster"),
+		Scheme: mgr.GetScheme(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "AirflowCluster")
+		os.Exit(1)
+	}
+	// +kubebuilder:scaffold:builder
+
+	setupLog.Info("starting manager")
+	if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
+		setupLog.Error(err, "problem running manager")
+		os.Exit(1)
+	}
+}
diff --git a/pkg/apis/addtoscheme_airflow_v1alpha1.go b/pkg/apis/addtoscheme_airflow_v1alpha1.go
deleted file mode 100644
index 1009dcd..0000000
--- a/pkg/apis/addtoscheme_airflow_v1alpha1.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package apis
-
-import (
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
-)
-
-func init() {
-	// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
-	AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
-}
diff --git a/pkg/apis/airflow/group.go b/pkg/apis/airflow/group.go
deleted file mode 100644
index b476ef4..0000000
--- a/pkg/apis/airflow/group.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package airflow contains airflow API versions
-package airflow
diff --git a/pkg/apis/airflow/v1alpha1/airflowbase_types_test.go b/pkg/apis/airflow/v1alpha1/airflowbase_types_test.go
deleted file mode 100644
index bb70b33..0000000
--- a/pkg/apis/airflow/v1alpha1/airflowbase_types_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package v1alpha1
-
-import (
-	"testing"
-
-	"github.com/onsi/gomega"
-	"golang.org/x/net/context"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-func TestStorageAirflowBase(t *testing.T) {
-	key := types.NamespacedName{
-		Name:      "foo",
-		Namespace: "default",
-	}
-	created := &AirflowBase{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "foo",
-			Namespace: "default",
-		}}
-	g := gomega.NewGomegaWithT(t)
-
-	// Test Create
-	fetched := &AirflowBase{}
-	g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred())
-
-	g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
-	g.Expect(fetched).To(gomega.Equal(created))
-
-	// Test Updating the Labels
-	updated := fetched.DeepCopy()
-	updated.Labels = map[string]string{"hello": "world"}
-	g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred())
-
-	g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
-	g.Expect(fetched).To(gomega.Equal(updated))
-
-	// Test Delete
-	g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred())
-	g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred())
-}
diff --git a/pkg/apis/airflow/v1alpha1/airflowcluster_types_test.go b/pkg/apis/airflow/v1alpha1/airflowcluster_types_test.go
deleted file mode 100644
index 282eb63..0000000
--- a/pkg/apis/airflow/v1alpha1/airflowcluster_types_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package v1alpha1
-
-import (
-	"testing"
-
-	"github.com/onsi/gomega"
-	"golang.org/x/net/context"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-)
-
-func TestStorageAirflowCluster(t *testing.T) {
-	key := types.NamespacedName{
-		Name:      "foo",
-		Namespace: "default",
-	}
-	created := &AirflowCluster{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "foo",
-			Namespace: "default",
-		}}
-	g := gomega.NewGomegaWithT(t)
-
-	// Test Create
-	fetched := &AirflowCluster{}
-	g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred())
-
-	g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
-	g.Expect(fetched).To(gomega.Equal(created))
-
-	// Test Updating the Labels
-	updated := fetched.DeepCopy()
-	updated.Labels = map[string]string{"hello": "world"}
-	g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred())
-
-	g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
-	g.Expect(fetched).To(gomega.Equal(updated))
-
-	// Test Delete
-	g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred())
-	g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred())
-}
diff --git a/pkg/apis/airflow/v1alpha1/register.go b/pkg/apis/airflow/v1alpha1/register.go
deleted file mode 100644
index cb742a4..0000000
--- a/pkg/apis/airflow/v1alpha1/register.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You 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.
-
-// NOTE: Boilerplate only.  Ignore this file.
-
-// Package v1alpha1 contains API Schema definitions for the airflow v1alpha1 API group
-// +k8s:openapi-gen=true
-// +k8s:deepcopy-gen=package,register
-// +k8s:conversion-gen=github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow
-// +k8s:defaulter-gen=TypeMeta
-// +groupName=airflow.k8s.io
-package v1alpha1
-
-import (
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	"sigs.k8s.io/controller-runtime/pkg/runtime/scheme"
-)
-
-var (
-	// SchemeGroupVersion is group version used to register these objects
-	SchemeGroupVersion = schema.GroupVersion{Group: "airflow.k8s.io", Version: "v1alpha1"}
-
-	// SchemeBuilder is used to add go types to the GroupVersionKind scheme
-	SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
-
-	// AddToScheme is required by pkg/client/...
-	AddToScheme = SchemeBuilder.AddToScheme
-)
-
-// Resource is required by pkg/client/listers/...
-func Resource(resource string) schema.GroupResource {
-	return SchemeGroupVersion.WithResource(resource).GroupResource()
-}
diff --git a/pkg/apis/airflow/v1alpha1/v1alpha1_suite_test.go b/pkg/apis/airflow/v1alpha1/v1alpha1_suite_test.go
deleted file mode 100644
index 1cc96e5..0000000
--- a/pkg/apis/airflow/v1alpha1/v1alpha1_suite_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package v1alpha1
-
-import (
-	"log"
-	"os"
-	"path/filepath"
-	"testing"
-
-	"k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-)
-
-var cfg *rest.Config
-var c client.Client
-
-func TestMain(m *testing.M) {
-	t := &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
-	}
-
-	err := SchemeBuilder.AddToScheme(scheme.Scheme)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	if cfg, err = t.Start(); err != nil {
-		log.Fatal(err)
-	}
-
-	if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil {
-		log.Fatal(err)
-	}
-
-	code := m.Run()
-	t.Stop()
-	os.Exit(code)
-}
diff --git a/pkg/apis/apis.go b/pkg/apis/apis.go
deleted file mode 100644
index 52280df..0000000
--- a/pkg/apis/apis.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You 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.
-
-// Generate deepcopy for apis
-// +k8s:deepcopy-gen=true
-
-// Package apis contains Kubernetes API groups.
-package apis
-
-import (
-	"k8s.io/apimachinery/pkg/runtime"
-)
-
-// AddToSchemes may be used to add all resources defined in the project to a Scheme
-var AddToSchemes runtime.SchemeBuilder
-
-// AddToScheme adds all Resources to the Scheme
-func AddToScheme(s *runtime.Scheme) error {
-	return AddToSchemes.AddToScheme(s)
-}
diff --git a/pkg/apis/doc.go b/pkg/apis/doc.go
deleted file mode 100644
index 8d0aaa9..0000000
--- a/pkg/apis/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// +domain=k8s.io
-package apis
diff --git a/pkg/controller/add_airflowbase.go b/pkg/controller/add_airflowbase.go
deleted file mode 100644
index 6bd5eb4..0000000
--- a/pkg/controller/add_airflowbase.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package controller
-
-import (
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/airflowbase"
-)
-
-func init() {
-	// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
-	AddToManagerFuncs = append(AddToManagerFuncs, airflowbase.Add)
-}
diff --git a/pkg/controller/add_airflowcluster.go b/pkg/controller/add_airflowcluster.go
deleted file mode 100644
index 5823155..0000000
--- a/pkg/controller/add_airflowcluster.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package controller
-
-import (
-	"github.com/apache/airflow-on-k8s-operator/pkg/controller/airflowcluster"
-)
-
-func init() {
-	// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
-	AddToManagerFuncs = append(AddToManagerFuncs, airflowcluster.Add)
-}
diff --git a/pkg/controller/airflowbase/airflowbase_controller_suite_test.go b/pkg/controller/airflowbase/airflowbase_controller_suite_test.go
deleted file mode 100644
index 34b144b..0000000
--- a/pkg/controller/airflowbase/airflowbase_controller_suite_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package airflowbase
-
-import (
-	"log"
-	"os"
-	"path/filepath"
-	"sync"
-	"testing"
-
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis"
-	"github.com/onsi/gomega"
-	"k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-)
-
-var cfg *rest.Config
-
-func TestMain(m *testing.M) {
-	t := &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
-	}
-	apis.AddToScheme(scheme.Scheme)
-
-	var err error
-	if cfg, err = t.Start(); err != nil {
-		log.Fatal(err)
-	}
-
-	code := m.Run()
-	t.Stop()
-	os.Exit(code)
-}
-
-// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
-// writes the request to requests after Reconcile is finished.
-func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
-	requests := make(chan reconcile.Request)
-	fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
-		result, err := inner.Reconcile(req)
-		requests <- req
-		return result, err
-	})
-	return fn, requests
-}
-
-// StartTestManager adds recFn
-func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
-	stop := make(chan struct{})
-	wg := &sync.WaitGroup{}
-	go func() {
-		wg.Add(1)
-		g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
-		wg.Done()
-	}()
-	return stop, wg
-}
diff --git a/pkg/controller/airflowbase/airflowbase_controller_test.go b/pkg/controller/airflowbase/airflowbase_controller_test.go
deleted file mode 100644
index 13c4b13..0000000
--- a/pkg/controller/airflowbase/airflowbase_controller_test.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package airflowbase
-
-import (
-	"testing"
-	"time"
-
-	airflowv1alpha1 "github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
-	"github.com/onsi/gomega"
-	"golang.org/x/net/context"
-	appsv1 "k8s.io/api/apps/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-)
-
-var c client.Client
-
-var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
-var mysqlkey = types.NamespacedName{Name: "foo-mysql", Namespace: "default"}
-var nfskey = types.NamespacedName{Name: "foo-nfs", Namespace: "default"}
-
-const timeout = time.Second * 5
-
-func TestReconcile(t *testing.T) {
-	g := gomega.NewGomegaWithT(t)
-	instance := &airflowv1alpha1.AirflowBase{
-		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
-		Spec: airflowv1alpha1.AirflowBaseSpec{
-			MySQL: &airflowv1alpha1.MySQLSpec{
-				Operator: false,
-			},
-			Storage: &airflowv1alpha1.NFSStoreSpec{
-				Version: "",
-			},
-		},
-	}
-
-	// Setup the Manager and Controller.  Wrap the Controller Reconcile function so it writes each request to a
-	// channel when it is finished.
-	mgr, err := manager.New(cfg, manager.Options{})
-	g.Expect(err).NotTo(gomega.HaveOccurred())
-	c = mgr.GetClient()
-
-	r := newReconciler(mgr)
-	recFn, requests := SetupTestReconcile(r)
-	g.Expect(r.Controller(recFn)).NotTo(gomega.HaveOccurred())
-
-	stopMgr, mgrStopped := StartTestManager(mgr, g)
-
-	defer func() {
-		close(stopMgr)
-		mgrStopped.Wait()
-	}()
-
-	// Create the AirflowBase object and expect the Reconcile and Deployment to be created
-	err = c.Create(context.TODO(), instance)
-	// The instance object may not be a valid object because it might be missing some required fields.
-	// Please modify the instance object by adding required fields and then remove the following if statement.
-	if apierrors.IsInvalid(err) {
-		t.Logf("failed to create object, got an invalid object error: %v", err)
-		return
-	}
-	g.Expect(err).NotTo(gomega.HaveOccurred())
-	defer c.Delete(context.TODO(), instance)
-	g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
-
-	mysqlsts := &appsv1.StatefulSet{}
-	nfssts := &appsv1.StatefulSet{}
-	g.Eventually(func() error { return c.Get(context.TODO(), mysqlkey, mysqlsts) }, timeout).Should(gomega.Succeed())
-	g.Eventually(func() error { return c.Get(context.TODO(), nfskey, nfssts) }, timeout).Should(gomega.Succeed())
-
-	// Delete the Deployment and expect Reconcile to be called for Deployment deletion
-	g.Expect(c.Delete(context.TODO(), mysqlsts)).NotTo(gomega.HaveOccurred())
-	g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
-	g.Eventually(func() error { return c.Get(context.TODO(), mysqlkey, mysqlsts) }, timeout).Should(gomega.Succeed())
-
-	// Manually delete Deployment since GC isn't enabled in the test control plane
-	g.Expect(c.Delete(context.TODO(), nfssts)).To(gomega.Succeed())
-
-}
diff --git a/pkg/controller/airflowcluster/airflowcluster_controller_suite_test.go b/pkg/controller/airflowcluster/airflowcluster_controller_suite_test.go
deleted file mode 100644
index 12214b3..0000000
--- a/pkg/controller/airflowcluster/airflowcluster_controller_suite_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package airflowcluster
-
-import (
-	"log"
-	"os"
-	"path/filepath"
-	"sync"
-	"testing"
-
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis"
-	"github.com/onsi/gomega"
-	"k8s.io/client-go/kubernetes/scheme"
-	"k8s.io/client-go/rest"
-	"sigs.k8s.io/controller-runtime/pkg/envtest"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-)
-
-var cfg *rest.Config
-
-func TestMain(m *testing.M) {
-	t := &envtest.Environment{
-		CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
-	}
-	apis.AddToScheme(scheme.Scheme)
-
-	var err error
-	if cfg, err = t.Start(); err != nil {
-		log.Fatal(err)
-	}
-
-	code := m.Run()
-	t.Stop()
-	os.Exit(code)
-}
-
-// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
-// writes the request to requests after Reconcile is finished.
-func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
-	requests := make(chan reconcile.Request)
-	fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
-		result, err := inner.Reconcile(req)
-		requests <- req
-		return result, err
-	})
-	return fn, requests
-}
-
-// StartTestManager adds recFn
-func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
-	stop := make(chan struct{})
-	wg := &sync.WaitGroup{}
-	go func() {
-		wg.Add(1)
-		g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
-		wg.Done()
-	}()
-	return stop, wg
-}
diff --git a/pkg/controller/airflowcluster/airflowcluster_controller_test.go b/pkg/controller/airflowcluster/airflowcluster_controller_test.go
deleted file mode 100644
index a552c62..0000000
--- a/pkg/controller/airflowcluster/airflowcluster_controller_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package airflowcluster
-
-import (
-	"testing"
-	"time"
-
-	airflowv1alpha1 "github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
-	"github.com/onsi/gomega"
-	"golang.org/x/net/context"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-)
-
-var c client.Client
-
-var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
-var rediskey = types.NamespacedName{Name: "foo-redis", Namespace: "default"}
-var uikey = types.NamespacedName{Name: "foo-airflowui", Namespace: "default"}
-var workerkey = types.NamespacedName{Name: "foo-worker", Namespace: "default"}
-var flowerkey = types.NamespacedName{Name: "foo-flower", Namespace: "default"}
-var schedulerkey = types.NamespacedName{Name: "foo-scheduler", Namespace: "default"}
-
-const timeout = time.Second * 5
-
-func TestReconcile(t *testing.T) {
-	g := gomega.NewGomegaWithT(t)
-	base := &airflowv1alpha1.AirflowBase{
-		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
-		Spec: airflowv1alpha1.AirflowBaseSpec{
-			MySQL: &airflowv1alpha1.MySQLSpec{
-				Operator: false,
-			},
-			Storage: &airflowv1alpha1.NFSStoreSpec{
-				Version: "",
-			},
-		},
-	}
-
-	cluster := &airflowv1alpha1.AirflowCluster{
-		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
-		Spec: airflowv1alpha1.AirflowClusterSpec{
-			Executor:  "Celery",
-			Redis:     &airflowv1alpha1.RedisSpec{Operator: false},
-			Scheduler: &airflowv1alpha1.SchedulerSpec{Version: "1.10.2"},
-			UI:        &airflowv1alpha1.AirflowUISpec{Replicas: 1, Version: "1.10.2"},
-			Worker:    &airflowv1alpha1.WorkerSpec{Replicas: 2, Version: "1.10.2"},
-			Flower:    &airflowv1alpha1.FlowerSpec{Replicas: 1, Version: "1.10.2"},
-			DAGs: &airflowv1alpha1.DagSpec{
-				DagSubdir: "airflow/example_dags/",
-				Git: &airflowv1alpha1.GitSpec{
-					Repo: "https://github.com/apache/incubator-airflow/",
-					Once: true,
-				},
-			},
-			AirflowBaseRef: &corev1.LocalObjectReference{Name: "foo"},
-		},
-	}
-
-	// Setup the Manager and Controller.  Wrap the Controller Reconcile function so it writes each request to a
-	// channel when it is finished.
-	mgr, err := manager.New(cfg, manager.Options{})
-	g.Expect(err).NotTo(gomega.HaveOccurred())
-	c = mgr.GetClient()
-
-	r := newReconciler(mgr)
-	recFn, requests := SetupTestReconcile(r)
-	g.Expect(r.Controller(recFn)).NotTo(gomega.HaveOccurred())
-
-	stopMgr, mgrStopped := StartTestManager(mgr, g)
-
-	defer func() {
-		close(stopMgr)
-		mgrStopped.Wait()
-	}()
-
-	// Create the AirflowCluster object and expect the Reconcile and Deployment to be created
-	err = c.Create(context.TODO(), base)
-	err = c.Create(context.TODO(), cluster)
-	// The cluster object may not be a valid object because it might be missing some required fields.
-	// Please modify the cluster object by adding required fields and then remove the following if statement.
-	if apierrors.IsInvalid(err) {
-		t.Logf("failed to create object, got an invalid object error: %v", err)
-		return
-	}
-	g.Expect(err).NotTo(gomega.HaveOccurred())
-	defer c.Delete(context.TODO(), cluster)
-	g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
-
-	redis := &appsv1.StatefulSet{}
-	ui := &appsv1.StatefulSet{}
-	worker := &appsv1.StatefulSet{}
-	flower := &appsv1.StatefulSet{}
-	scheduler := &appsv1.StatefulSet{}
-	g.Eventually(func() error { return c.Get(context.TODO(), rediskey, redis) }, timeout).Should(gomega.Succeed())
-	g.Eventually(func() error { return c.Get(context.TODO(), uikey, ui) }, timeout).Should(gomega.Succeed())
-	g.Eventually(func() error { return c.Get(context.TODO(), workerkey, worker) }, timeout).Should(gomega.Succeed())
-	g.Eventually(func() error { return c.Get(context.TODO(), schedulerkey, scheduler) }, timeout).Should(gomega.Succeed())
-	g.Eventually(func() error { return c.Get(context.TODO(), flowerkey, flower) }, timeout).Should(gomega.Succeed())
-
-	// Delete the Deployment and expect Reconcile to be called for Deployment deletion
-	g.Expect(c.Delete(context.TODO(), scheduler)).NotTo(gomega.HaveOccurred())
-	g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
-	g.Eventually(func() error { return c.Get(context.TODO(), schedulerkey, scheduler) }, timeout).
-		Should(gomega.Succeed())
-
-	// Manually delete Deployment since GC isn't enabled in the test control plane
-	g.Expect(c.Delete(context.TODO(), redis)).To(gomega.Succeed())
-	g.Expect(c.Delete(context.TODO(), ui)).To(gomega.Succeed())
-	g.Expect(c.Delete(context.TODO(), worker)).To(gomega.Succeed())
-	g.Expect(c.Delete(context.TODO(), flower)).To(gomega.Succeed())
-	g.Expect(c.Delete(context.TODO(), scheduler)).To(gomega.Succeed())
-}
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
deleted file mode 100644
index 35786a7..0000000
--- a/pkg/controller/controller.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package controller
-
-import (
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-)
-
-// AddToManagerFuncs is a list of functions to add all Controllers to the Manager
-var AddToManagerFuncs []func(manager.Manager) error
-
-// AddToManager adds all Controllers to the Manager
-func AddToManager(m manager.Manager) error {
-	for _, f := range AddToManagerFuncs {
-		if err := f(m); err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go
deleted file mode 100644
index b1a01ba..0000000
--- a/pkg/webhook/webhook.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one or more
-// contributor license agreements.  See the NOTICE file distributed with
-// this work for additional information regarding copyright ownership.
-// The ASF licenses this file to You under the Apache License, Version 2.0
-// (the "License"); you may not use this file except in compliance with
-// the License.  You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package webhook
-
-import (
-	"sigs.k8s.io/controller-runtime/pkg/manager"
-)
-
-// AddToManagerFuncs is a list of functions to add all Controllers to the Manager
-var AddToManagerFuncs []func(manager.Manager) error
-
-// AddToManager adds all Controllers to the Manager
-// +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
-func AddToManager(m manager.Manager) error {
-	for _, f := range AddToManagerFuncs {
-		if err := f(m); err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/test/e2e/base_test.go b/test/e2e/base/base_test.go
similarity index 97%
rename from test/e2e/base_test.go
rename to test/e2e/base/base_test.go
index 62ed7cf..740389b 100644
--- a/test/e2e/base_test.go
+++ b/test/e2e/base/base_test.go
@@ -13,14 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package airflowbase
+package e2etest
 
 import (
 	"testing"
 
+	"github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
 	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 	"sigs.k8s.io/controller-reconciler/pkg/test"
 )
diff --git a/test/e2e/cluster_test.go b/test/e2e/cluster/cluster_test.go
similarity index 98%
rename from test/e2e/cluster_test.go
rename to test/e2e/cluster/cluster_test.go
index 6987e7a..2d56a68 100644
--- a/test/e2e/cluster_test.go
+++ b/test/e2e/cluster/cluster_test.go
@@ -13,14 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package airflowcluster
+package e2etest
 
 import (
 	"testing"
 
+	"github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
 	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 	"sigs.k8s.io/controller-reconciler/pkg/test"
 )
diff --git a/test/e2e/gcp_test.go b/test/e2e/gcp/gcp_test.go
similarity index 98%
rename from test/e2e/gcp_test.go
rename to test/e2e/gcp/gcp_test.go
index d092e4a..c369909 100644
--- a/test/e2e/gcp_test.go
+++ b/test/e2e/gcp/gcp_test.go
@@ -13,14 +13,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package gcp
+package e2etest
 
 import (
 	"testing"
 
+	"github.com/apache/airflow-on-k8s-operator/api/v1alpha1"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-	"github.com/apache/airflow-on-k8s-operator/pkg/apis/airflow/v1alpha1"
 	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 	"sigs.k8s.io/controller-reconciler/pkg/test"
 )