diff --git a/cmd/main.go b/cmd/main.go index 51f8b8a..2523e9a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -57,11 +57,13 @@ func main() { var metricsAddr string var enableLeaderElection bool var probeAddr string + var controllerDomain string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + flag.StringVar(&controllerDomain, "controller-domain", "k8s.checklyhq.com", "Domain to use for annotations and finalizers.") opts := zap.Options{ Development: true, } @@ -70,6 +72,8 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + setupLog.Info("Controller domain setup", "value", controllerDomain) + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, Metrics: metricsserver.Options{ @@ -107,32 +111,36 @@ func main() { client.SetAccountId(accountId) if err = (&networkingcontrollers.IngressReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ControllerDomain: controllerDomain, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Ingress") os.Exit(1) } if err = (&checklycontrollers.ApiCheckReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - ApiClient: client, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ApiClient: client, + ControllerDomain: controllerDomain, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ApiCheck") os.Exit(1) } if err = (&checklycontrollers.GroupReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - ApiClient: client, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ApiClient: client, + ControllerDomain: controllerDomain, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Group") os.Exit(1) } if err = (&checklycontrollers.AlertChannelReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - ApiClient: client, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + ApiClient: client, + ControllerDomain: controllerDomain, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "AlertChannel") os.Exit(1) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index f6be818..123c1ac 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -32,6 +32,7 @@ spec: - /manager args: - --leader-elect + - --controller-domain=k8s.checklyhq.com image: controller:latest name: manager env: diff --git a/docs/README.md b/docs/README.md index 7722abc..adb6fe2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,7 +39,7 @@ If you just want to try out the checkly-operator, you need a local kubernetes in First we'll download the provided `install.yaml` files, please change the version number accordingly, we might have newer [releases](https://github.com/checkly/checkly-operator/releases) since we've written these docs. ```bash -export CHECKLY_OPERATOR_RELEASE=v1.4.1 +export CHECKLY_OPERATOR_RELEASE=v1.7.0 wget "https://github.com/checkly/checkly-operator/releases/download/$CHECKLY_OPERATOR_RELEASE/install-$CHECKLY_OPERATOR_RELEASE.yaml" -O install.yaml unset CHECKLY_OPERATOR_RELEASE ``` @@ -53,6 +53,12 @@ You can apply the `install.yaml`, this will create the namespace, we need this t kubectl apply -f install.yaml ``` +#### Controller Domain + +We're using a domain name for finalizers and annotations, the default value is `k8s.checklyhq.com`, but it can be changed by supplying the `--controller-domain=other.domain.tld` runtime option. + +This option allows you to run multiple independent deployments of the operator and each would handle different resources based on the controller domain configuration. + ### Create secret Grab your [checklyhq.com](checklyhq.com) API key and Account ID, [the official docs](https://www.checklyhq.com/docs/integrations/pulumi/#define-your-checkly-account-id-and-api-key) can help you get this information. Substitute the values into the below command: diff --git a/internal/controller/checkly/alertchannel_controller.go b/internal/controller/checkly/alertchannel_controller.go index 4332efe..22105e7 100644 --- a/internal/controller/checkly/alertchannel_controller.go +++ b/internal/controller/checkly/alertchannel_controller.go @@ -18,6 +18,7 @@ package checkly import ( "context" + "fmt" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -36,8 +37,9 @@ import ( // AlertChannelReconciler reconciles a AlertChannel object type AlertChannelReconciler struct { client.Client - Scheme *runtime.Scheme - ApiClient checkly.Client + Scheme *runtime.Scheme + ApiClient checkly.Client + ControllerDomain string } //+kubebuilder:rbac:groups=k8s.checklyhq.com,resources=alertchannels,verbs=get;list;watch;create;update;patch;delete @@ -55,7 +57,7 @@ func (r *AlertChannelReconciler) Reconcile(ctx context.Context, req ctrl.Request logger.Info("Reconciler started") - acFinalizer := "k8s.checklyhq.com/finalizer" + acFinalizer := fmt.Sprintf("%s/finalizer", r.ControllerDomain) ac := &checklyv1alpha1.AlertChannel{} diff --git a/internal/controller/checkly/alertchannel_controller_test.go b/internal/controller/checkly/alertchannel_controller_test.go index d6237ad..1c96909 100644 --- a/internal/controller/checkly/alertchannel_controller_test.go +++ b/internal/controller/checkly/alertchannel_controller_test.go @@ -115,11 +115,15 @@ var _ = Describe("ApiCheck Controller", func() { Eventually(func() bool { f := &checklyv1alpha1.AlertChannel{} err := k8sClient.Get(context.Background(), acKey, f) - if len(f.Finalizers) == 1 && err == nil { - return true - } else { + if err != nil { return false } + + for _, finalizer := range f.Finalizers { + Expect(finalizer).To(Equal("testing.domain.tld/finalizer"), "Finalizer should match") + } + + return true }, timeout, interval).Should(BeTrue()) // Update diff --git a/internal/controller/checkly/apicheck_controller.go b/internal/controller/checkly/apicheck_controller.go index 1a0b4a0..db34136 100644 --- a/internal/controller/checkly/apicheck_controller.go +++ b/internal/controller/checkly/apicheck_controller.go @@ -18,6 +18,7 @@ package checkly import ( "context" + "fmt" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -35,8 +36,9 @@ import ( // ApiCheckReconciler reconciles a ApiCheck object type ApiCheckReconciler struct { client.Client - Scheme *runtime.Scheme - ApiClient checkly.Client + Scheme *runtime.Scheme + ApiClient checkly.Client + ControllerDomain string } //+kubebuilder:rbac:groups=k8s.checklyhq.com,resources=apichecks,verbs=get;list;watch;create;update;patch;delete @@ -56,7 +58,7 @@ type ApiCheckReconciler struct { func (r *ApiCheckReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) - apiCheckFinalizer := "k8s.checklyhq.com/finalizer" + apiCheckFinalizer := fmt.Sprintf("%s/finalizer", r.ControllerDomain) apiCheck := &checklyv1alpha1.ApiCheck{} diff --git a/internal/controller/checkly/apicheck_controller_test.go b/internal/controller/checkly/apicheck_controller_test.go index 4904757..d19339e 100644 --- a/internal/controller/checkly/apicheck_controller_test.go +++ b/internal/controller/checkly/apicheck_controller_test.go @@ -117,11 +117,15 @@ var _ = Describe("ApiCheck Controller", func() { Eventually(func() bool { f := &checklyv1alpha1.ApiCheck{} err := k8sClient.Get(context.Background(), key, f) - if len(f.Finalizers) == 1 && err == nil { - return true - } else { + if err != nil { return false } + + for _, finalizer := range f.Finalizers { + Expect(finalizer).To(Equal("testing.domain.tld/finalizer"), "Finalizer should match") + } + + return true }, timeout, interval).Should(BeTrue()) // Delete diff --git a/internal/controller/checkly/group_controller.go b/internal/controller/checkly/group_controller.go index d6e6244..30c5089 100644 --- a/internal/controller/checkly/group_controller.go +++ b/internal/controller/checkly/group_controller.go @@ -18,6 +18,7 @@ package checkly import ( "context" + "fmt" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -35,8 +36,9 @@ import ( // GroupReconciler reconciles a Group object type GroupReconciler struct { client.Client - Scheme *runtime.Scheme - ApiClient checkly.Client + Scheme *runtime.Scheme + ApiClient checkly.Client + ControllerDomain string } //+kubebuilder:rbac:groups=k8s.checklyhq.com,resources=groups,verbs=get;list;watch;create;update;patch;delete @@ -53,7 +55,7 @@ func (r *GroupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl logger.Info("Reconciler started") - groupFinalizer := "k8s.checklyhq.com/finalizer" + groupFinalizer := fmt.Sprintf("%s/finalizer", r.ControllerDomain) group := &checklyv1alpha1.Group{} diff --git a/internal/controller/checkly/group_controller_test.go b/internal/controller/checkly/group_controller_test.go index b7b676a..d1e50d9 100644 --- a/internal/controller/checkly/group_controller_test.go +++ b/internal/controller/checkly/group_controller_test.go @@ -111,11 +111,15 @@ var _ = Describe("ApiCheck Controller", func() { Eventually(func() bool { f := &checklyv1alpha1.Group{} err := k8sClient.Get(context.Background(), groupKey, f) - if len(f.Finalizers) == 1 && err == nil { - return true - } else { + if err != nil { return false } + + for _, finalizer := range f.Finalizers { + Expect(finalizer).To(Equal("testing.domain.tld/finalizer"), "Finalizer should match") + } + + return true }, timeout, interval).Should(BeTrue()) // Update diff --git a/internal/controller/checkly/suite_test.go b/internal/controller/checkly/suite_test.go index f02b810..ea9c0b2 100644 --- a/internal/controller/checkly/suite_test.go +++ b/internal/controller/checkly/suite_test.go @@ -168,24 +168,29 @@ var _ = BeforeSuite(func() { http.ListenAndServe(":5555", nil) }() + testControllerDomain := "testing.domain.tld" + err = (&ApiCheckReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - ApiClient: testClient, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + ApiClient: testClient, + ControllerDomain: testControllerDomain, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) err = (&GroupReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - ApiClient: testClient, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + ApiClient: testClient, + ControllerDomain: testControllerDomain, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) err = (&AlertChannelReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - ApiClient: testClient, + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + ApiClient: testClient, + ControllerDomain: testControllerDomain, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) diff --git a/internal/controller/networking/ingress_controller.go b/internal/controller/networking/ingress_controller.go index e90cb21..df500c0 100644 --- a/internal/controller/networking/ingress_controller.go +++ b/internal/controller/networking/ingress_controller.go @@ -33,7 +33,8 @@ import ( // IngressReconciler reconciles a Ingress object type IngressReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + ControllerDomain string } //+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;update;patch @@ -54,6 +55,8 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct ingress := &networkingv1.Ingress{} apiCheck := &checklyv1alpha1.ApiCheck{} + annotationEnabled := fmt.Sprintf("%s/enabled", r.ControllerDomain) + // Check if ingress object is still present err := r.Get(ctx, req.NamespacedName, ingress) if err != nil { @@ -67,7 +70,7 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct logger.Info("Ingress Object found") // Check if annotation is present on the object - checklyAnnotation := ingress.Annotations["k8s.checklyhq.com/enabled"] == "true" + checklyAnnotation := ingress.Annotations[annotationEnabled] == "true" if !checklyAnnotation { // Annotation may have been removed or updated, we have to determine if we need to delete a previously created ApiCheck resource logger.Info("annotation is not present, checking if ApiCheck was created") @@ -138,7 +141,7 @@ func (r *IngressReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *IngressReconciler) gatherApiCheckData(ingress *networkingv1.Ingress) (apiCheckSpec checklyv1alpha1.ApiCheckSpec, err error) { - annotationHost := "k8s.checklyhq.com" + annotationHost := r.ControllerDomain annotationPath := fmt.Sprintf("%s/path", annotationHost) annotationEndpoint := fmt.Sprintf("%s/endpoint", annotationHost) annotationSuccess := fmt.Sprintf("%s/success", annotationHost) diff --git a/internal/controller/networking/ingress_controller_test.go b/internal/controller/networking/ingress_controller_test.go index db6d411..dc6d107 100644 --- a/internal/controller/networking/ingress_controller_test.go +++ b/internal/controller/networking/ingress_controller_test.go @@ -45,10 +45,10 @@ var _ = Describe("Ingress Controller", func() { } annotation := make(map[string]string) - annotation["k8s.checklyhq.com/enabled"] = "true" - annotation["k8s.checklyhq.com/path"] = testPath - annotation["k8s.checklyhq.com/success"] = testSuccessCode - annotation["k8s.checklyhq.com/group"] = testGroup + annotation["testing.domain.tld/enabled"] = "true" + annotation["testing.domain.tld/path"] = testPath + annotation["testing.domain.tld/success"] = testSuccessCode + annotation["testing.domain.tld/group"] = testGroup rules := make([]networkingv1.IngressRule, 0) rules = append(rules, networkingv1.IngressRule{ @@ -116,10 +116,10 @@ var _ = Describe("Ingress Controller", func() { // Update updatePath := "baaz" updateHost := "foo.update" - annotation["k8s.checklyhq.com/path"] = updatePath - annotation["k8s.checklyhq.com/endpoint"] = updateHost - annotation["k8s.checklyhq.com/success"] = "" - annotation["k8s.checklyhq.com/muted"] = "false" + annotation["testing.domain.tld/path"] = updatePath + annotation["testing.domain.tld/endpoint"] = updateHost + annotation["testing.domain.tld/success"] = "" + annotation["testing.domain.tld/muted"] = "false" ingress = &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, @@ -164,7 +164,7 @@ var _ = Describe("Ingress Controller", func() { }, timeout, interval).Should(BeTrue()) // Remove enabled label - annotation["k8s.checklyhq.com/enabled"] = "false" + annotation["testing.domain.tld/enabled"] = "false" ingress = &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, @@ -221,10 +221,10 @@ var _ = Describe("Ingress Controller", func() { } annotation := make(map[string]string) - annotation["k8s.checklyhq.com/enabled"] = "false" - annotation["k8s.checklyhq.com/path"] = testPath - annotation["k8s.checklyhq.com/success"] = testSuccessCode - annotation["k8s.checklyhq.com/muted"] = "false" + annotation["testing.domain.tld/enabled"] = "false" + annotation["testing.domain.tld/path"] = testPath + annotation["testing.domain.tld/success"] = testSuccessCode + annotation["testing.domain.tld/muted"] = "false" rules := make([]networkingv1.IngressRule, 0) rules = append(rules, networkingv1.IngressRule{ @@ -255,7 +255,7 @@ var _ = Describe("Ingress Controller", func() { updated := &networkingv1.Ingress{} Expect(k8sClient.Get(context.Background(), key, updated)).Should(Succeed()) - annotation["k8s.checklyhq.com/enabled"] = "true" + annotation["testing.domain.tld/enabled"] = "true" updated.Annotations = annotation Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed()) diff --git a/internal/controller/networking/suite_test.go b/internal/controller/networking/suite_test.go index 47f53c5..8fc90b7 100644 --- a/internal/controller/networking/suite_test.go +++ b/internal/controller/networking/suite_test.go @@ -83,9 +83,12 @@ var _ = BeforeSuite(func() { }) Expect(err).ToNot(HaveOccurred()) + testControllerDomain := "testing.domain.tld" + err = (&IngressReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + ControllerDomain: testControllerDomain, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred())