diff --git a/Makefile b/Makefile index 9408a64..09baa06 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,9 @@ integration-test: setup-envtest setup-envtest: bash ./hack/install-setup-envtest.sh + + + # Docker Image KOCACHE ?= /tmp/ko-cache KO_TAGS ?= "latest" @@ -51,9 +54,9 @@ LD_FLAGS := "-X main.Version=$(VERSION) \ .PHONY: ko-build ko-build: ko @LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(FULL_IMG) \ - $(KO) build ./cmd/cloudflare-tunnel-ingress-controller/ --preserve-import-paths --tags=$(KO_TAGS) --push=false + $(KO) build ./cmd/cloudflare-tunnel-ingress-controller/ --bare --local --tags=$(KO_TAGS) --push=false -.PHONY: docker-build-all +.PHONY: docker-build docker-build: ko-build REGISTRY_PASSWORD ?= dummy diff --git a/charts/cloudflare-tunnel-ingress-controller/templates/_helpers.tpl b/charts/cloudflare-tunnel-ingress-controller/templates/_helpers.tpl index 87c7f9a..86ca14f 100644 --- a/charts/cloudflare-tunnel-ingress-controller/templates/_helpers.tpl +++ b/charts/cloudflare-tunnel-ingress-controller/templates/_helpers.tpl @@ -60,3 +60,8 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + + +{{- define "cloudflare-tunnel-ingress-controller.controllerclass" -}} + {{- default (printf "oliverbaehler.io/%s" (include "cloudflare-tunnel-ingress-controller.fullname" $)) .Values.ingressClass.controllerValue -}} +{{- end -}} diff --git a/charts/cloudflare-tunnel-ingress-controller/templates/deployment.yaml b/charts/cloudflare-tunnel-ingress-controller/templates/deployment.yaml index d883002..d6d4a5a 100644 --- a/charts/cloudflare-tunnel-ingress-controller/templates/deployment.yaml +++ b/charts/cloudflare-tunnel-ingress-controller/templates/deployment.yaml @@ -48,7 +48,7 @@ spec: command: - cloudflare-tunnel-ingress-controller - --ingress-class={{ .Values.ingressClass.name }} - - --controller-class={{ .Values.ingressClass.controllerValue }} + - --controller-class={{ include "cloudflare-tunnel-ingress-controller.controllerclass" $ }} - --cloudflare-api-token=$(CLOUDFLARE_API_TOKEN) - --cloudflare-account-id=$(CLOUDFLARE_ACCOUNT_ID) - --cloudflare-tunnel-name=$(CLOUDFLARE_TUNNEL_NAME) diff --git a/charts/cloudflare-tunnel-ingress-controller/templates/ingressclass.yaml b/charts/cloudflare-tunnel-ingress-controller/templates/ingressclass.yaml index 69a168d..e0afb0e 100644 --- a/charts/cloudflare-tunnel-ingress-controller/templates/ingressclass.yaml +++ b/charts/cloudflare-tunnel-ingress-controller/templates/ingressclass.yaml @@ -5,8 +5,4 @@ metadata: ingressclass.kubernetes.io/is-default-class: {{ .Values.ingressClass.isDefaultClass | quote }} name: {{ .Values.ingressClass.name }} spec: - {{- if .Values.ingressClass.controllerValue }} - controller: {{ .Values.ingressClass.controllerValue }} - {{- else }} - controller: "oliverbaehler.io/{{ include "cloudflare-tunnel-ingress-controller.fullname" $ }}" - {{- end }} + controller: {{ include "cloudflare-tunnel-ingress-controller.controllerclass" $ }} \ No newline at end of file diff --git a/pkg/controller/controlled-cloudflared-connector.go b/pkg/controller/controlled-cloudflared-connector.go index c7b12d1..bc9dcec 100644 --- a/pkg/controller/controlled-cloudflared-connector.go +++ b/pkg/controller/controlled-cloudflared-connector.go @@ -9,10 +9,8 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" ) func CreateControlledCloudflaredIfNotExist( @@ -42,18 +40,9 @@ func CreateControlledCloudflaredIfNotExist( // When a tunnel is found, compare if the current is deployed if len(list.Items) > 0 { - deployment.APIVersion = "apps/v1" - deployment.Kind = "Deployment" - patchBytes, err := yaml.Marshal(deployment) + err := kubeClient.Update(ctx, deployment) if err != nil { - return errors.Wrap(err, "marshal deployment for patch") - } - - err = kubeClient.Patch(ctx, deployment, client.RawPatch(types.ApplyPatchType, patchBytes), &client.PatchOptions{ - FieldManager: "cloudflare-controller", - }) - if err != nil { - return errors.Wrap(err, "patch controlled-cloudflared-connector deployment") + return errors.Wrap(err, "update controlled-cloudflared-connector deployment") } } else { err = kubeClient.Create(ctx, deployment) diff --git a/pkg/controller/finalizer.go b/pkg/controller/finalizer.go new file mode 100644 index 0000000..131f8c6 --- /dev/null +++ b/pkg/controller/finalizer.go @@ -0,0 +1,38 @@ +package controller + +import ( + "context" + + "github.com/pkg/errors" + networkingv1 "k8s.io/api/networking/v1" +) + +const IngressControllerFinalizer = "strrl.dev/cloudflare-tunnel-ingress-controller-controlled" + +func (i *IngressController) hasFinalizer(ctx context.Context, ingress networkingv1.Ingress) bool { + return stringSliceContains(ingress.Finalizers, IngressControllerFinalizer) +} + +func (i *IngressController) attachFinalizer(ctx context.Context, ingress networkingv1.Ingress) error { + if stringSliceContains(ingress.Finalizers, IngressControllerFinalizer) { + return nil + } + ingress.Finalizers = append(ingress.Finalizers, IngressControllerFinalizer) + err := i.kubeClient.Update(ctx, &ingress) + if err != nil { + return errors.Wrapf(err, "attach finalizer for %s/%s", ingress.Namespace, ingress.Name) + } + return nil +} + +func (i *IngressController) cleanFinalizer(ctx context.Context, ingress networkingv1.Ingress) error { + if !stringSliceContains(ingress.Finalizers, IngressControllerFinalizer) { + return nil + } + ingress.Finalizers = removeStringFromSlice(ingress.Finalizers, IngressControllerFinalizer) + err := i.kubeClient.Update(ctx, &ingress) + if err != nil { + return errors.Wrapf(err, "clean finalizer for %s/%s", ingress.Namespace, ingress.Name) + } + return nil +} diff --git a/pkg/controller/ingress-controller.go b/pkg/controller/ingress-controller.go index ab1850b..482702b 100644 --- a/pkg/controller/ingress-controller.go +++ b/pkg/controller/ingress-controller.go @@ -18,7 +18,6 @@ import ( var _ reconcile.Reconciler = &IngressController{} const WellKnownIngressAnnotation = "kubernetes.io/ingress.class" -const IngressControllerFinalizer = "strrl.dev/cloudflare-tunnel-ingress-controller-controlled" type IngressController struct { logger logr.Logger @@ -48,7 +47,7 @@ func (i *IngressController) Reconcile(ctx context.Context, request reconcile.Req } if !controlled { - i.logger.V(1).Info("ingress is NOT controlled by this controller", + i.logger.Info("ingress is NOT controlled by this controller", "ingress", request.NamespacedName, "controlled-ingress-class", i.ingressClassName, "controlled-controller-class", i.controllerClassName, @@ -66,9 +65,22 @@ func (i *IngressController) Reconcile(ctx context.Context, request reconcile.Req i.logger.Info("update cloudflare tunnel config", "triggered-by", request.NamespacedName) - err = i.attachFinalizer(ctx, *(origin.DeepCopy())) - if err != nil { - return reconcile.Result{}, errors.Wrapf(err, "attach finalizer to ingress %s", request.NamespacedName) + if origin.DeletionTimestamp == nil { + err = i.attachFinalizer(ctx, *(origin.DeepCopy())) + if err != nil { + return reconcile.Result{}, errors.Wrapf(err, "attach finalizer to ingress %s", request.NamespacedName) + } + } else { + if !i.hasFinalizer(ctx, origin) { + i.logger.V(1).Info("ingress is being deleted and already finillized by this controller", + "ingress", request.NamespacedName, + "controlled-ingress-class", i.ingressClassName, + "controlled-controller-class", i.controllerClassName, + ) + return reconcile.Result{ + Requeue: false, + }, nil + } } ingresses, err := i.listControlledIngresses(ctx) @@ -112,16 +124,11 @@ func (i *IngressController) isControlledByThisController(ctx context.Context, ta return false, nil } - controlledIngressClasses, err := i.listControlledIngressClasses(ctx) + controlledIngressClassNames, err := i.listControlledIngressClasses(ctx) if err != nil { return false, errors.Wrapf(err, "fetch controlled ingress classes with controller name %s", i.controllerClassName) } - var controlledIngressClassNames []string - for _, controlledIngressClass := range controlledIngressClasses { - controlledIngressClassNames = append(controlledIngressClassNames, controlledIngressClass.Name) - } - if stringSliceContains(controlledIngressClassNames, *target.Spec.IngressClassName) { return true, nil } @@ -129,26 +136,30 @@ func (i *IngressController) isControlledByThisController(ctx context.Context, ta return false, nil } -func (i *IngressController) listControlledIngressClasses(ctx context.Context) ([]networkingv1.IngressClass, error) { +func (i *IngressController) listControlledIngressClasses(ctx context.Context) ([]string, error) { list := networkingv1.IngressClassList{} err := i.kubeClient.List(ctx, &list) if err != nil { return nil, errors.Wrap(err, "list ingress classes") } - return list.Items, nil + + var controlledNames []string + for _, ingressClass := range list.Items { + // Check if the IngressClass is controlled by the specified controller + if ingressClass.Spec.Controller == i.controllerClassName { + controlledNames = append(controlledNames, ingressClass.Name) + } + } + + return controlledNames, nil } func (i *IngressController) listControlledIngresses(ctx context.Context) ([]networkingv1.Ingress, error) { - controlledIngressClasses, err := i.listControlledIngressClasses(ctx) + controlledIngressClassNames, err := i.listControlledIngressClasses(ctx) if err != nil { return nil, errors.Wrapf(err, "fetch controlled ingress classes with controller name %s", i.controllerClassName) } - var controlledIngressClassNames []string - for _, controlledIngressClass := range controlledIngressClasses { - controlledIngressClassNames = append(controlledIngressClassNames, controlledIngressClass.Name) - } - var result []networkingv1.Ingress list := networkingv1.IngressList{} err = i.kubeClient.List(ctx, &list) @@ -177,30 +188,6 @@ func (i *IngressController) listControlledIngresses(ctx context.Context) ([]netw return result, nil } -func (i *IngressController) attachFinalizer(ctx context.Context, ingress networkingv1.Ingress) error { - if stringSliceContains(ingress.Finalizers, IngressControllerFinalizer) { - return nil - } - ingress.Finalizers = append(ingress.Finalizers, IngressControllerFinalizer) - err := i.kubeClient.Update(ctx, &ingress) - if err != nil { - return errors.Wrapf(err, "attach finalizer for %s/%s", ingress.Namespace, ingress.Name) - } - return nil -} - -func (i *IngressController) cleanFinalizer(ctx context.Context, ingress networkingv1.Ingress) error { - if !stringSliceContains(ingress.Finalizers, IngressControllerFinalizer) { - return nil - } - ingress.Finalizers = removeStringFromSlice(ingress.Finalizers, IngressControllerFinalizer) - err := i.kubeClient.Update(ctx, &ingress) - if err != nil { - return errors.Wrapf(err, "clean finalizer for %s/%s", ingress.Namespace, ingress.Name) - } - return nil -} - func removeStringFromSlice(finalizers []string, finalizer string) []string { var result []string for _, f := range finalizers {