From 481b28216cbb213cd803b14dd4f176d2f85ad04c Mon Sep 17 00:00:00 2001 From: PatrickLaabs Date: Fri, 13 Jun 2025 12:09:11 +0200 Subject: [PATCH 1/4] doing some refactoring and code cleanup --- Makefile | 3 +- README.md | 32 ++- api/v1alpha1/cdk8sappproxy_types.go | 4 - ...dons.cluster.x-k8s.io_cdk8sappproxies.yaml | 4 - config/default/manager_image_patch.yaml | 2 +- config/default/manager_image_patch.yaml-e | 2 +- config/default/manager_pull_policy.yaml | 2 +- config/default/manager_pull_policy.yaml-e | 2 +- .../cdk8sappproxy/cdk8sappproxy_controller.go | 184 ++++++++++-------- .../cdk8sappproxy/cdk8sappproxy_reconciler.go | 6 +- examples/cdk8sappproxy_sample.yaml | 14 ++ go.mod | 7 - go.sum | 2 - internal/mocks/doc.go | 18 -- internal/testdata/ca-bundle.pem | 31 --- internal/value_substitutions.go | 98 ---------- 16 files changed, 147 insertions(+), 264 deletions(-) delete mode 100644 internal/mocks/doc.go delete mode 100644 internal/testdata/ca-bundle.pem delete mode 100644 internal/value_substitutions.go diff --git a/Makefile b/Makefile index 68e125f..b99e5a0 100644 --- a/Makefile +++ b/Makefile @@ -230,7 +230,8 @@ CAPI_KIND_CLUSTER_NAME ?= capi-test # It is set by Prow GIT_TAG, a git-based tag of the form vYYYYMMDD-hash, e.g., v20210120-v0.3.10-308-gc61521971 -TAG ?= v0.3.0-alpha +# Next release is: v0.3.1-alpha +TAG ?= v0.3.1-preview ARCH ?= $(shell go env GOARCH) ALL_ARCH = amd64 arm arm64 diff --git a/README.md b/README.md index 6843697..b847c94 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,6 @@ # Cluster API Add-on Provider for Cdk8s -### 👋 Welcome! Here are some links to help you get started: - -- [Project design outline](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20220712-cluster-api-addon-orchestration.md) - ## ✨ What is Cluster API Add-on Provider for Cdk8s? Cluster API Add-on Provider for Cdk8s extends Cluster API by managing the installation, configuration, upgrade, and deletion of cluster add-ons using cdk8s applications. This provider is based on the [Cluster API Add-on Orchestration Proposal](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/proposals/20220712-cluster-api-addon-orchestration.md), a larger effort to bring orchestration for add-ons to CAPI by using existing package management tools. @@ -47,14 +43,30 @@ apiVersion: addons.cluster.x-k8s.io/v1alpha1 kind: Cdk8sAppProxy metadata: name: cdk8s-sample-app - namespace: caapc-system + namespace: default spec: gitRepository: - url: https://github.com/PatrickLaabs/cdk8s-sample-deployment - # reference: main # Optional: specify a branch, tag, or commit - # path: "" # Optional: specify a path within the repository if cdk8s app is not at root - clusterSelector: - matchLabels: {} + url: "https://github.com/PatrickLaabs/cluster-api-addon-provider-cdk8s/examples/cdk8s-sample-deployment" + reference: "main" + path: "." + # authSecretRef: + # name: git-credentials + clusterSelector: {} + # matchLabels: + # environment: development +# --- +# apiVersion: v1 +# kind: Secret +# metadata: +# name: git-credentials +# namespace: default +# type: Opaque +# data: +# # Base64 encoded username +# username: +# # Base64 encoded password/token +# password: + ``` ### Cdk8sAppProxySpec Fields diff --git a/api/v1alpha1/cdk8sappproxy_types.go b/api/v1alpha1/cdk8sappproxy_types.go index 020a03c..bb9d406 100644 --- a/api/v1alpha1/cdk8sappproxy_types.go +++ b/api/v1alpha1/cdk8sappproxy_types.go @@ -56,10 +56,6 @@ type Cdk8sAppProxySpec struct { // +kubebuilder:validation:Optional GitRepository *GitRepositorySpec `json:"gitRepository,omitempty"` - // Values is a string containing the values to be passed to the cdk8s app - // +kubebuilder:validation:Optional - Values string `json:"values,omitempty"` - // ClusterSelector selects the clusters to deploy the cdk8s app to. // +kubebuilder:validation:Required ClusterSelector metav1.LabelSelector `json:"clusterSelector"` diff --git a/config/crd/bases/addons.cluster.x-k8s.io_cdk8sappproxies.yaml b/config/crd/bases/addons.cluster.x-k8s.io_cdk8sappproxies.yaml index 63000a0..78a9b31 100644 --- a/config/crd/bases/addons.cluster.x-k8s.io_cdk8sappproxies.yaml +++ b/config/crd/bases/addons.cluster.x-k8s.io_cdk8sappproxies.yaml @@ -140,10 +140,6 @@ spec: LocalPath is the local filesystem path to the cdk8s app. One of LocalPath or GitRepository must be specified. type: string - values: - description: Values is a string containing the values to be passed - to the cdk8s app - type: string required: - clusterSelector type: object diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 3be7adc..81b8031 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller-amd64:v0.3.0-alpha + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview name: manager diff --git a/config/default/manager_image_patch.yaml-e b/config/default/manager_image_patch.yaml-e index d768a40..81b8031 100644 --- a/config/default/manager_image_patch.yaml-e +++ b/config/default/manager_image_patch.yaml-e @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.2.11-preview + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview name: manager diff --git a/config/default/manager_pull_policy.yaml b/config/default/manager_pull_policy.yaml index 74a0879..cd7ae12 100644 --- a/config/default/manager_pull_policy.yaml +++ b/config/default/manager_pull_policy.yaml @@ -8,4 +8,4 @@ spec: spec: containers: - name: manager - imagePullPolicy: Always + imagePullPolicy: IfNotPresent diff --git a/config/default/manager_pull_policy.yaml-e b/config/default/manager_pull_policy.yaml-e index cd7ae12..74a0879 100644 --- a/config/default/manager_pull_policy.yaml-e +++ b/config/default/manager_pull_policy.yaml-e @@ -8,4 +8,4 @@ spec: spec: containers: - name: manager - imagePullPolicy: IfNotPresent + imagePullPolicy: Always diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_controller.go b/controllers/cdk8sappproxy/cdk8sappproxy_controller.go index 5f9cd24..d37e857 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_controller.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_controller.go @@ -186,57 +186,46 @@ func (r *Reconciler) parseManifestFiles(manifestFiles []string, logger logr.Logg var parsedResources []*unstructured.Unstructured for _, manifestFile := range manifestFiles { - resources, err := r.parseManifestFile(manifestFile, logger, operation) - if err != nil { - return nil, err - } - parsedResources = append(parsedResources, resources...) - } + logger.Info("Processing manifest file", "file", manifestFile, "operation", operation) - logger.Info("Total resources parsed", "count", len(parsedResources), "operation", operation) + fileContent, readErr := os.ReadFile(manifestFile) + if readErr != nil { + logger.Error(readErr, "Failed to read manifest file", "file", manifestFile, "operation", operation) - return parsedResources, nil -} - -func (r *Reconciler) parseManifestFile(manifestFile string, logger logr.Logger, operation string) ([]*unstructured.Unstructured, error) { - logger.Info("Processing manifest file", "file", manifestFile, "operation", operation) - - fileContent, readErr := os.ReadFile(manifestFile) - if readErr != nil { - logger.Error(readErr, "Failed to read manifest file", "file", manifestFile, "operation", operation) + return nil, readErr + } - return nil, readErr - } + yamlDecoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(fileContent), 100) - var parsedResources []*unstructured.Unstructured - yamlDecoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(fileContent), 100) + for { + var rawObj runtime.RawExtension + if err := yamlDecoder.Decode(&rawObj); err != nil { + if err.Error() == "EOF" { + break + } + logger.Error(err, "Failed to decode YAML from manifest file", "file", manifestFile, "operation", operation) - for { - var rawObj runtime.RawExtension - if err := yamlDecoder.Decode(&rawObj); err != nil { - if err.Error() == "EOF" { - break + return nil, err } - logger.Error(err, "Failed to decode YAML from manifest file", "file", manifestFile, "operation", operation) - return nil, err - } + if rawObj.Raw == nil { + continue + } - if rawObj.Raw == nil { - continue - } + u := &unstructured.Unstructured{} + if _, _, err := unstructured.UnstructuredJSONScheme.Decode(rawObj.Raw, nil, u); err != nil { + logger.Error(err, "Failed to decode RawExtension to Unstructured", "file", manifestFile, "operation", operation) - u := &unstructured.Unstructured{} - if _, _, err := unstructured.UnstructuredJSONScheme.Decode(rawObj.Raw, nil, u); err != nil { - logger.Error(err, "Failed to decode RawExtension to Unstructured", "file", manifestFile, "operation", operation) + return nil, err + } - return nil, err + parsedResources = append(parsedResources, u) + logger.Info("Parsed resource", "GVK", u.GroupVersionKind().String(), "Name", u.GetName(), "Namespace", u.GetNamespace(), "operation", operation) } - - parsedResources = append(parsedResources, u) - logger.Info("Parsed resource", "GVK", u.GroupVersionKind().String(), "Name", u.GetName(), "Namespace", u.GetNamespace(), "operation", operation) } + logger.Info("Total resources parsed", "count", len(parsedResources), "operation", operation) + return parsedResources, nil } @@ -258,6 +247,39 @@ func (r *Reconciler) deleteResourcesFromClusters(ctx context.Context, cdk8sAppPr return nil } +func (r *Reconciler) deleteResourcesFromSingleCluster(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, cluster clusterv1.Cluster, parsedResources []*unstructured.Unstructured, logger logr.Logger) error { + clusterLogger := logger.WithValues("targetCluster", cluster.Name) + clusterLogger.Info("Deleting resources from cluster") + + dynamicClient, err := r.getDynamicClientForCluster(ctx, cdk8sAppProxy.Namespace, cluster.Name) + if err != nil { + clusterLogger.Error(err, "Failed to get dynamic client for cluster during deletion, skipping this cluster") + + return err + } + + clusterLogger.Info("Successfully created dynamic client for cluster deletion") + + for _, resource := range parsedResources { + gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) + clusterLogger.Info("Deleting resource from cluster", "GVK", resource.GroupVersionKind().String(), "Name", resource.GetName(), "Namespace", resource.GetNamespace()) + + err := dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) + + switch { + case err != nil && !apierrors.IsNotFound(err): + clusterLogger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) + // Log but continue with other resources + case apierrors.IsNotFound(err): + clusterLogger.Info("Resource already deleted from cluster", "resourceName", resource.GetName()) + case err == nil: + clusterLogger.Info("Successfully deleted resource from cluster", "resourceName", resource.GetName()) + } + } + + return nil +} + func (r *Reconciler) getTargetClustersForDeletion(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) (*clusterv1.ClusterList, error) { clusterList := &clusterv1.ClusterList{} selector, err := metav1.LabelSelectorAsSelector(&cdk8sAppProxy.Spec.ClusterSelector) @@ -283,83 +305,77 @@ func (r *Reconciler) getTargetClustersForDeletion(ctx context.Context, cdk8sAppP return clusterList, nil } -func (r *Reconciler) deleteResourcesFromSingleCluster(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, cluster clusterv1.Cluster, parsedResources []*unstructured.Unstructured, logger logr.Logger) error { - clusterLogger := logger.WithValues("targetCluster", cluster.Name) - clusterLogger.Info("Deleting resources from cluster") - - dynamicClient, err := r.getDynamicClientForCluster(ctx, cdk8sAppProxy.Namespace, cluster.Name) - if err != nil { - clusterLogger.Error(err, "Failed to get dynamic client for cluster during deletion, skipping this cluster") +// func (r *Reconciler) deleteResourcesFromSingleCluster(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, cluster clusterv1.Cluster, parsedResources []*unstructured.Unstructured, logger logr.Logger) error { +// clusterLogger := logger.WithValues("targetCluster", cluster.Name) +// clusterLogger.Info("Deleting resources from cluster") - return err - } +// dynamicClient, err := r.getDynamicClientForCluster(ctx, cdk8sAppProxy.Namespace, cluster.Name) +// if err != nil { +// clusterLogger.Error(err, "Failed to get dynamic client for cluster during deletion, skipping this cluster") - clusterLogger.Info("Successfully created dynamic client for cluster deletion") +// return err +// } - for _, resource := range parsedResources { - if err := r.deleteResourceFromCluster(ctx, dynamicClient, resource, clusterLogger); err != nil { - // Log but continue with other resources - clusterLogger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) - } - } +// clusterLogger.Info("Successfully created dynamic client for cluster deletion") - return nil -} +// for _, resource := range parsedResources { +// if err := r.deleteResourceFromCluster(ctx, dynamicClient, resource, clusterLogger); err != nil { +// // Log but continue with other resources +// clusterLogger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) +// } +// } -func (r *Reconciler) deleteResourceFromCluster(ctx context.Context, dynamicClient dynamic.Interface, resource *unstructured.Unstructured, logger logr.Logger) error { - gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) - logger.Info("Deleting resource from cluster", "GVK", resource.GroupVersionKind().String(), "Name", resource.GetName(), "Namespace", resource.GetNamespace()) +// return nil +// } - err := dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) +// func (r *Reconciler) deleteResourceFromCluster(ctx context.Context, dynamicClient dynamic.Interface, resource *unstructured.Unstructured, logger logr.Logger) error { +// gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) +// logger.Info("Deleting resource from cluster", "GVK", resource.GroupVersionKind().String(), "Name", resource.GetName(), "Namespace", resource.GetNamespace()) - switch { - case err != nil && !apierrors.IsNotFound(err): - logger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) +// err := dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) - return err - case apierrors.IsNotFound(err): - logger.Info("Resource already deleted from cluster", "resourceName", resource.GetName()) - case err == nil: - logger.Info("Successfully deleted resource from cluster", "resourceName", resource.GetName()) - } +// switch { +// case err != nil && !apierrors.IsNotFound(err): +// logger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) - return nil -} +// return err +// case apierrors.IsNotFound(err): +// logger.Info("Resource already deleted from cluster", "resourceName", resource.GetName()) +// case err == nil: +// logger.Info("Successfully deleted resource from cluster", "resourceName", resource.GetName()) +// } -func (r *Reconciler) finalizeDeletion(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, proxyNamespacedName types.NamespacedName, logger logr.Logger) (ctrl.Result, error) { - // Cancel any active watches for this Cdk8sAppProxy - r.cancelActiveWatches(proxyNamespacedName, logger) +// return nil +// } - // Remove finalizer - return r.removeFinalizer(ctx, cdk8sAppProxy, logger) -} +func (r *Reconciler) finalizeDeletion(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, proxyNamespacedName types.NamespacedName, logger logr.Logger) error { + logger.Info("Starting finalization process") -func (r *Reconciler) cancelActiveWatches(proxyNamespacedName types.NamespacedName, logger logr.Logger) { + // Cancel any active watches for this Cdk8sAppProxy if watchesForProxy, ok := r.ActiveWatches[proxyNamespacedName]; ok { logger.Info("Cancelling active watches for Cdk8sAppProxy before deletion", "count", len(watchesForProxy)) for watchKey, cancelFunc := range watchesForProxy { logger.Info("Cancelling watch", "watchKey", watchKey) cancelFunc() // Stop the goroutine and its associated Kubernetes watch } - // After all, watches for this proxy are canceled, remove its entry from the main map + // After all watches for this proxy are canceled, remove its entry from the main map delete(r.ActiveWatches, proxyNamespacedName) logger.Info("Removed Cdk8sAppProxy entry from ActiveWatches map") } else { - logger.Info("No active watches found for this Cdk8sAppProxy to cancel.") + logger.Info("No active watches found for this Cdk8sAppProxy to cancel") } -} -func (r *Reconciler) removeFinalizer(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) (ctrl.Result, error) { + // Remove finalizer logger.Info("Finished deletion logic, removing finalizer") controllerutil.RemoveFinalizer(cdk8sAppProxy, Finalizer) if err := r.Update(ctx, cdk8sAppProxy); err != nil { logger.Error(err, "Failed to remove finalizer") - return ctrl.Result{}, err + return err } logger.Info("Finalizer removed successfully") - return ctrl.Result{}, nil + return nil } func (r *Reconciler) getDynamicClientForCluster(ctx context.Context, secretNamespace, clusterName string) (dynamic.Interface, error) { diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go b/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go index 72beaec..426b3f9 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go @@ -103,7 +103,11 @@ func (r *Reconciler) reconcileDelete(ctx context.Context, cdk8sAppProxy *addonsv } // Clean up watches and remove finalizer - return r.finalizeDeletion(ctx, cdk8sAppProxy, proxyNamespacedName, logger) + if err := r.finalizeDeletion(ctx, cdk8sAppProxy, proxyNamespacedName, logger); err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil } func (r *Reconciler) reconcileNormal(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy) (ctrl.Result, error) { diff --git a/examples/cdk8sappproxy_sample.yaml b/examples/cdk8sappproxy_sample.yaml index 5c8ae69..dbb7b98 100644 --- a/examples/cdk8sappproxy_sample.yaml +++ b/examples/cdk8sappproxy_sample.yaml @@ -8,6 +8,20 @@ spec: url: "https://github.com/PatrickLaabs/cluster-api-addon-provider-cdk8s/examples/cdk8s-sample-deployment" reference: "main" path: "." + # authSecretRef: + # name: git-credentials clusterSelector: {} # matchLabels: # environment: development +# --- +# apiVersion: v1 +# kind: Secret +# metadata: +# name: git-credentials +# namespace: default +# type: Opaque +# data: +# # Base64 encoded username +# username: +# # Base64 encoded password/token +# password: \ No newline at end of file diff --git a/go.mod b/go.mod index 5641fc5..0746efb 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/PatrickLaabs/cluster-api-addon-provider-cdk8s go 1.24.4 require ( - github.com/Masterminds/sprig/v3 v3.3.0 github.com/go-git/go-git/v5 v5.16.2 github.com/go-logr/logr v1.4.3 github.com/onsi/ginkgo/v2 v2.23.4 @@ -22,8 +21,6 @@ require ( require ( cel.dev/expr v0.19.1 // indirect dario.cat/mergo v1.0.1 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect @@ -62,7 +59,6 @@ require ( github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect - github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -70,8 +66,6 @@ require ( github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -81,7 +75,6 @@ require ( github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/shopspring/decimal v1.4.0 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect diff --git a/go.sum b/go.sum index 5a06c60..bfe3dc6 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,6 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= diff --git a/internal/mocks/doc.go b/internal/mocks/doc.go deleted file mode 100644 index 8329426..0000000 --- a/internal/mocks/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2023 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package mocks contains generated mocks. -package mocks diff --git a/internal/testdata/ca-bundle.pem b/internal/testdata/ca-bundle.pem deleted file mode 100644 index b85c803..0000000 --- a/internal/testdata/ca-bundle.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- diff --git a/internal/value_substitutions.go b/internal/value_substitutions.go deleted file mode 100644 index a7cb499..0000000 --- a/internal/value_substitutions.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright 2022 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package internal - -import ( - "bytes" - "context" - "text/template" - - "github.com/Masterminds/sprig/v3" - addonsv1alpha1 "github.com/PatrickLaabs/cluster-api-addon-provider-cdk8s/api/v1alpha1" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" - "sigs.k8s.io/cluster-api/controllers/external" - ctrl "sigs.k8s.io/controller-runtime" - ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" -) - -// initializeBuiltins takes a map of keys to object references, attempts to get the referenced objects, and returns a map of keys to the actual objects. -// These objects are a map[string]interface{} so that they can be used as values in the template. -func initializeBuiltins(ctx context.Context, c ctrlClient.Client, referenceMap map[string]corev1.ObjectReference, cluster *clusterv1.Cluster) (map[string]interface{}, error) { - log := ctrl.LoggerFrom(ctx) - - valueLookUp := make(map[string]interface{}) - - for name, ref := range referenceMap { - if ref.Namespace == "" { - ref.Namespace = cluster.Namespace - } - log.V(2).Info("Getting object for reference", "ref", ref) - obj, err := external.Get(ctx, c, &ref) - if err != nil { - return nil, errors.Wrapf(err, "failed to get object %s", ref.Name) - } - valueLookUp[name] = obj.Object - } - - return valueLookUp, nil -} - -// ParseValues parses the values template and returns the expanded template. It attempts to populate a map of supported templating objects. -func ParseValues(ctx context.Context, c ctrlClient.Client, spec addonsv1alpha1.Cdk8sAppProxySpec, cluster *clusterv1.Cluster) (string, error) { - log := ctrl.LoggerFrom(ctx) - - log.V(2).Info("Rendering templating in values:", "values", spec.Values) // Assuming Cdk8sAppProxySpec.Values holds the template string - references := map[string]corev1.ObjectReference{ - "Cluster": { - APIVersion: cluster.APIVersion, - Kind: cluster.Kind, - Namespace: cluster.Namespace, - Name: cluster.Name, - }, - } - - if cluster.Spec.ControlPlaneRef != nil { - references["ControlPlane"] = *cluster.Spec.ControlPlaneRef - } - if cluster.Spec.InfrastructureRef != nil { - references["InfraCluster"] = *cluster.Spec.InfrastructureRef - } - // TODO: would we want to add ControlPlaneMachineTemplate? - - valueLookUp, err := initializeBuiltins(ctx, c, references, cluster) - if err != nil { - return "", err - } - - tmpl, err := template.New(cluster.GetName()). // ChartName is not available in Cdk8sAppProxySpec, using cluster name for template name - Funcs(sprig.TxtFuncMap()). - Parse(spec.Values) // Assuming Cdk8sAppProxySpec.Values holds the template string - if err != nil { - return "", err - } - var buffer bytes.Buffer - - if err := tmpl.Execute(&buffer, valueLookUp); err != nil { - return "", errors.Wrapf(err, "error executing template string '%s' on cluster '%s'", spec.Values, cluster.GetName()) - } - expandedTemplate := buffer.String() - log.V(2).Info("Expanded values to", "result", expandedTemplate) - - return expandedTemplate, nil -} From 8c50115076858ee75671e1d9641e0f975b354a3f Mon Sep 17 00:00:00 2001 From: PatrickLaabs Date: Fri, 13 Jun 2025 16:25:02 +0200 Subject: [PATCH 2/4] and some more refactoring --- Dockerfile | 2 +- Makefile | 2 +- config/default/manager_image_patch.yaml | 2 +- config/default/manager_image_patch.yaml-e | 2 +- config/default/manager_pull_policy.yaml | 2 +- .../cdk8sappproxy/cdk8sappproxy_consts.go | 6 +- .../cdk8sappproxy/cdk8sappproxy_controller.go | 141 ++------- .../cdk8sappproxy_git_operator.go | 299 ++++++++++++------ .../cdk8sappproxy/cdk8sappproxy_reconciler.go | 242 +------------- .../cdk8sappproxy_resourcewatcher.go | 294 +++++------------ .../cdk8sappproxy/cdk8sappproxy_types.go | 5 +- main.go | 9 +- 12 files changed, 340 insertions(+), 666 deletions(-) diff --git a/Dockerfile b/Dockerfile index af29c65..d8a4923 100644 --- a/Dockerfile +++ b/Dockerfile @@ -80,7 +80,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates=20240203~22.04.1 curl=7.81.0-1ubuntu1.20 && \ curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \ apt-get install -y nodejs=18.19.1-1nodesource1 && \ - npm install -g cdk8s-cli@2.200.97 && \ + npm install -g cdk8s-cli@2.200.99 && \ curl -fsSL -o go1.24.4.linux-amd64.tar.gz https://go.dev/dl/go1.24.4.linux-amd64.tar.gz && \ tar -C /usr/local -xzf go1.24.4.linux-amd64.tar.gz && \ rm go1.24.4.linux-amd64.tar.gz && \ diff --git a/Makefile b/Makefile index b99e5a0..93ffbd2 100644 --- a/Makefile +++ b/Makefile @@ -231,7 +231,7 @@ CAPI_KIND_CLUSTER_NAME ?= capi-test # It is set by Prow GIT_TAG, a git-based tag of the form vYYYYMMDD-hash, e.g., v20210120-v0.3.10-308-gc61521971 # Next release is: v0.3.1-alpha -TAG ?= v0.3.1-preview +TAG ?= v0.3.1-preview.3 ARCH ?= $(shell go env GOARCH) ALL_ARCH = amd64 arm arm64 diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 81b8031..105fba3 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller-arm64:v0.3.1-preview.3 name: manager diff --git a/config/default/manager_image_patch.yaml-e b/config/default/manager_image_patch.yaml-e index 81b8031..4c24f31 100644 --- a/config/default/manager_image_patch.yaml-e +++ b/config/default/manager_image_patch.yaml-e @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller-arm:v0.3.1-preview.3 name: manager diff --git a/config/default/manager_pull_policy.yaml b/config/default/manager_pull_policy.yaml index cd7ae12..74a0879 100644 --- a/config/default/manager_pull_policy.yaml +++ b/config/default/manager_pull_policy.yaml @@ -8,4 +8,4 @@ spec: spec: containers: - name: manager - imagePullPolicy: IfNotPresent + imagePullPolicy: Always diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_consts.go b/controllers/cdk8sappproxy/cdk8sappproxy_consts.go index c0dfa34..02a0eb4 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_consts.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_consts.go @@ -18,4 +18,8 @@ const ( ) // gitPollInterval is the interval at which the cdk8sappproxy controller polls the git repository for changes. -const gitPollInterval = 1 * time.Minute +const ( + gitPollInterval = 1 * time.Minute + consecutiveErrors = 0 + maxConsecutiveErrors = 5 +) diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_controller.go b/controllers/cdk8sappproxy/cdk8sappproxy_controller.go index d37e857..5b32ecb 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_controller.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_controller.go @@ -95,14 +95,13 @@ func (r *Reconciler) getCdk8sAppProxyForPolling(ctx context.Context, proxyName t return cdk8sAppProxy, nil } -// checkIfResourceExists checks if a given resource exists on the target cluster. It uses a dynamic client to make the check. -func checkIfResourceExists(ctx context.Context, dynClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string, name string) (bool, error) { +func (r *Reconciler) checkIfResourceExists(ctx context.Context, dynClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string, name string) (bool, error) { resourceGetter := dynClient.Resource(gvr) if namespace != "" { _, err := resourceGetter.Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { - return false, nil // Resource does not exist + return false, nil } // Some other error occurred return false, errors.Wrapf(err, "failed to get namespaced resource %s/%s with GVR %s", namespace, name, gvr.String()) @@ -150,14 +149,13 @@ func (r *Reconciler) synthesizeCdk8sApp(appSourcePath string, logger logr.Logger } logger.Info("cdk8s synth successful", "outputSummary", truncateString(string(output), 200), "operation", operation) - logger.V(1).Info("cdk8s synth full output", "output", string(output), "operation", operation) + // logger.V(1).Info("cdk8s synth full output", "output", string(output), "operation", operation) return nil } func (r *Reconciler) findManifestFiles(appSourcePath string, logger logr.Logger, operation string) ([]string, error) { distPath := filepath.Join(appSourcePath, "dist") - logger.Info("Looking for manifests for deletion", "distPath", distPath, "operation", operation) var manifestFiles []string walkErr := filepath.WalkDir(distPath, func(path string, d fs.DirEntry, err error) error { @@ -238,42 +236,31 @@ func (r *Reconciler) deleteResourcesFromClusters(ctx context.Context, cdk8sAppPr // Delete resources from each cluster for _, cluster := range clusterList.Items { - if err := r.deleteResourcesFromSingleCluster(ctx, cdk8sAppProxy, cluster, parsedResources, logger); err != nil { + clusterLogger := logger.WithValues("targetCluster", cluster.Name) + + dynamicClient, err := r.getDynamicClientForCluster(ctx, cdk8sAppProxy.Namespace, cluster.Name) + if err != nil { + clusterLogger.Error(err, "Failed to get dynamic client for cluster during deletion, skipping this cluster") // Log error but continue with other clusters - logger.Error(err, "Failed to delete resources from cluster", "cluster", cluster.Name) + continue } - } - - return nil -} - -func (r *Reconciler) deleteResourcesFromSingleCluster(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, cluster clusterv1.Cluster, parsedResources []*unstructured.Unstructured, logger logr.Logger) error { - clusterLogger := logger.WithValues("targetCluster", cluster.Name) - clusterLogger.Info("Deleting resources from cluster") - dynamicClient, err := r.getDynamicClientForCluster(ctx, cdk8sAppProxy.Namespace, cluster.Name) - if err != nil { - clusterLogger.Error(err, "Failed to get dynamic client for cluster during deletion, skipping this cluster") - - return err - } - - clusterLogger.Info("Successfully created dynamic client for cluster deletion") + clusterLogger.Info("Successfully created dynamic client for cluster deletion") - for _, resource := range parsedResources { - gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) - clusterLogger.Info("Deleting resource from cluster", "GVK", resource.GroupVersionKind().String(), "Name", resource.GetName(), "Namespace", resource.GetNamespace()) + for _, resource := range parsedResources { + gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) + clusterLogger.Info("Deleting resource from cluster", "GVK", resource.GroupVersionKind().String(), "Name", resource.GetName(), "Namespace", resource.GetNamespace()) - err := dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) + err := dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) - switch { - case err != nil && !apierrors.IsNotFound(err): - clusterLogger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) - // Log but continue with other resources - case apierrors.IsNotFound(err): - clusterLogger.Info("Resource already deleted from cluster", "resourceName", resource.GetName()) - case err == nil: - clusterLogger.Info("Successfully deleted resource from cluster", "resourceName", resource.GetName()) + switch { + case err != nil && !apierrors.IsNotFound(err): + clusterLogger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) + case apierrors.IsNotFound(err): + clusterLogger.Info("Resource already deleted from cluster", "resourceName", resource.GetName()) + case err == nil: + clusterLogger.Info("Successfully deleted resource from cluster", "resourceName", resource.GetName()) + } } } @@ -305,65 +292,13 @@ func (r *Reconciler) getTargetClustersForDeletion(ctx context.Context, cdk8sAppP return clusterList, nil } -// func (r *Reconciler) deleteResourcesFromSingleCluster(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, cluster clusterv1.Cluster, parsedResources []*unstructured.Unstructured, logger logr.Logger) error { -// clusterLogger := logger.WithValues("targetCluster", cluster.Name) -// clusterLogger.Info("Deleting resources from cluster") - -// dynamicClient, err := r.getDynamicClientForCluster(ctx, cdk8sAppProxy.Namespace, cluster.Name) -// if err != nil { -// clusterLogger.Error(err, "Failed to get dynamic client for cluster during deletion, skipping this cluster") - -// return err -// } - -// clusterLogger.Info("Successfully created dynamic client for cluster deletion") - -// for _, resource := range parsedResources { -// if err := r.deleteResourceFromCluster(ctx, dynamicClient, resource, clusterLogger); err != nil { -// // Log but continue with other resources -// clusterLogger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) -// } -// } - -// return nil -// } - -// func (r *Reconciler) deleteResourceFromCluster(ctx context.Context, dynamicClient dynamic.Interface, resource *unstructured.Unstructured, logger logr.Logger) error { -// gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) -// logger.Info("Deleting resource from cluster", "GVK", resource.GroupVersionKind().String(), "Name", resource.GetName(), "Namespace", resource.GetNamespace()) - -// err := dynamicClient.Resource(gvr).Namespace(resource.GetNamespace()).Delete(ctx, resource.GetName(), metav1.DeleteOptions{}) - -// switch { -// case err != nil && !apierrors.IsNotFound(err): -// logger.Error(err, "Failed to delete resource from cluster", "resourceName", resource.GetName()) - -// return err -// case apierrors.IsNotFound(err): -// logger.Info("Resource already deleted from cluster", "resourceName", resource.GetName()) -// case err == nil: -// logger.Info("Successfully deleted resource from cluster", "resourceName", resource.GetName()) -// } - -// return nil -// } - func (r *Reconciler) finalizeDeletion(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, proxyNamespacedName types.NamespacedName, logger logr.Logger) error { logger.Info("Starting finalization process") - // Cancel any active watches for this Cdk8sAppProxy - if watchesForProxy, ok := r.ActiveWatches[proxyNamespacedName]; ok { - logger.Info("Cancelling active watches for Cdk8sAppProxy before deletion", "count", len(watchesForProxy)) - for watchKey, cancelFunc := range watchesForProxy { - logger.Info("Cancelling watch", "watchKey", watchKey) - cancelFunc() // Stop the goroutine and its associated Kubernetes watch - } - // After all watches for this proxy are canceled, remove its entry from the main map - delete(r.ActiveWatches, proxyNamespacedName) - logger.Info("Removed Cdk8sAppProxy entry from ActiveWatches map") - } else { - logger.Info("No active watches found for this Cdk8sAppProxy to cancel") - } + // Cancel any active watches for this Cdk8sAppProxy using the new WatchManager + logger.Info("Cleaning up active watches for Cdk8sAppProxy before deletion") + r.WatchManager.CleanupWatches(proxyNamespacedName) + logger.Info("Completed cleanup of active watches") // Remove finalizer logger.Info("Finished deletion logic, removing finalizer") @@ -381,7 +316,7 @@ func (r *Reconciler) finalizeDeletion(ctx context.Context, cdk8sAppProxy *addons func (r *Reconciler) getDynamicClientForCluster(ctx context.Context, secretNamespace, clusterName string) (dynamic.Interface, error) { logger := log.FromContext(ctx).WithValues("secretNamespace", secretNamespace, "clusterName", clusterName) kubeconfigSecretName := clusterName + "-kubeconfig" - logger.Info("Attempting to get Kubeconfig secret", "secretName", kubeconfigSecretName) + kubeconfigSecret := &corev1.Secret{} if err := r.Get(ctx, client.ObjectKey{Namespace: secretNamespace, Name: kubeconfigSecretName}, kubeconfigSecret); err != nil { logger.Error(err, "Failed to get Kubeconfig secret") @@ -402,7 +337,6 @@ func (r *Reconciler) getDynamicClientForCluster(ctx context.Context, secretNames return nil, errors.Wrapf(err, "failed to create REST config from kubeconfig for cluster %s", clusterName) } - logger.Info("Successfully created REST config") dynamicClient, err := dynamic.NewForConfig(restConfig) if err != nil { logger.Error(err, "Failed to create dynamic client") @@ -466,8 +400,8 @@ func getPluralFromKind(kind string) string { // SetupWithManager sets up the controller with the Manager. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { - // Initialize a watch system before setting up the controller - r.initializeWatchSystem() + // Initialize the new simplified watch system + r.WatchManager = NewResourceWatchManager(mgr.GetClient()) return ctrl.NewControllerManagedBy(mgr). For(&addonsv1alpha1.Cdk8sAppProxy{}). @@ -476,23 +410,6 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *Reconciler) initializeWatchSystem() { - // Create event handler with nil manager initially - eventHandler := NewEventHandler(r.Client, nil) - - // Create resource watcher - resourceWatcher := NewResourceWatcher(eventHandler) - - // Create the actual watch manager - watchManager := NewWatchManager(resourceWatcher) - - // Update the event handler with the real manager using the interface method - eventHandler.SetWatchManager(watchManager) - - // Set on reconciler - r.WatchManager = watchManager -} - // ClusterToCdk8sAppProxyMapper is a handler.ToRequestsFunc to be used to enqeue requests for Cdk8sAppProxyReconciler. // It maps CAPI Cluster events to Cdk8sAppProxy events. func (r *Reconciler) ClusterToCdk8sAppProxyMapper(ctx context.Context, o client.Object) []ctrl.Request { diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_git_operator.go b/controllers/cdk8sappproxy/cdk8sappproxy_git_operator.go index 6e6e073..a41f3dd 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_git_operator.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_git_operator.go @@ -99,7 +99,6 @@ func (r *Reconciler) cloneGitRepository(ctx context.Context, cdk8sAppProxy *addo // pollGitRepository periodically checks the remote git repository for changes. func (r *Reconciler) pollGitRepository(ctx context.Context, proxyName types.NamespacedName) { logger := log.FromContext(ctx).WithValues("cdk8sappproxy", proxyName.String(), "goroutine", "pollGitRepository") - logger.Info("Starting git repository polling loop") ticker := time.NewTicker(gitPollInterval) defer ticker.Stop() @@ -119,8 +118,6 @@ func (r *Reconciler) pollGitRepository(ctx context.Context, proxyName types.Name } func (r *Reconciler) pollGitRepositoryOnce(ctx context.Context, proxyName types.NamespacedName, logger logr.Logger) error { - logger.Info("Polling git repository for changes") - cdk8sAppProxy, err := r.getCdk8sAppProxyForPolling(ctx, proxyName) if err != nil { return err @@ -132,12 +129,8 @@ func (r *Reconciler) pollGitRepositoryOnce(ctx context.Context, proxyName types. return errors.New("resource not found") } - if !r.isGitRepositoryConfigured(cdk8sAppProxy, logger) { - return errors.New("git repository not configured") - } - gitSpec := cdk8sAppProxy.Spec.GitRepository - refName := r.determineGitReference(gitSpec, logger) + refName := r.determineGitReference(gitSpec) remoteCommitHash, err := r.fetchRemoteCommitHash(ctx, cdk8sAppProxy, gitSpec, refName, logger) if err != nil { @@ -173,7 +166,6 @@ func (r *Reconciler) prepareGitSourceForDeletion(ctx context.Context, cdk8sAppPr } cleanupFunc := func() { - logger.Info("Removing temporary clone directory for deletion", "tempDir", tempDir) if err := os.RemoveAll(tempDir); err != nil { logger.Error(err, "Failed to remove temporary clone directory for deletion", "tempDir", tempDir) } @@ -203,61 +195,35 @@ func (r *Reconciler) prepareGitSourceForDeletion(ctx context.Context, cdk8sAppPr } func (r *Reconciler) findRemoteCommitHash(refs []*plumbing.Reference, refName plumbing.ReferenceName, logger logr.Logger) (string, error) { - var remoteCommitHash string - foundRef := false - + // First, try to find the exact reference for _, ref := range refs { if ref.Name() == refName { - remoteCommitHash = ref.Hash().String() - foundRef = true - logger.Info("Found matching reference in LsRemote output", "refName", refName.String(), "remoteCommitHash", remoteCommitHash) + remoteCommitHash := ref.Hash().String() - break + return remoteCommitHash, nil } } + logger.Error(nil, "Specified reference not found in remote repository", "refName", refName.String()) - if !foundRef { - foundRef, remoteCommitHash = r.tryFindDefaultBranch(refs, refName, logger) - } - - if !foundRef { - logger.Info("Specified reference not found in LsRemote output.", "refName", refName.String()) - - return "", errors.New("reference not found") - } - - if remoteCommitHash == "" { - logger.Info("Remote commit hash is empty after LsRemote, skipping update check.") - - return "", errors.New("empty commit hash") - } - - return remoteCommitHash, nil + return "", errors.Errorf("reference '%s' not found in remote repository", refName.String()) } -func (r *Reconciler) tryFindDefaultBranch(refs []*plumbing.Reference, refName plumbing.ReferenceName, logger logr.Logger) (bool, string) { - if refName != plumbing.HEAD { - return false, "" - } - - logger.Info("HEAD reference not explicitly found, searching for default branches like main/master.") - defaultBranches := []plumbing.ReferenceName{ - plumbing.NewBranchReferenceName("main"), - plumbing.NewBranchReferenceName("master"), +func (r *Reconciler) determineGitReference(gitSpec *addonsv1alpha1.GitRepositorySpec) plumbing.ReferenceName { + if gitSpec.Reference == "" { + return plumbing.NewBranchReferenceName("main") } - for _, defaultBranchRef := range defaultBranches { - for _, ref := range refs { - if ref.Name() == defaultBranchRef { - remoteCommitHash := ref.Hash().String() - logger.Info("Found default branch reference", "refName", defaultBranchRef.String(), "remoteCommitHash", remoteCommitHash) + switch { + case plumbing.IsHash(gitSpec.Reference): - return true, remoteCommitHash - } - } + return plumbing.ReferenceName(gitSpec.Reference) + case strings.HasPrefix(gitSpec.Reference, "refs/"): + return plumbing.ReferenceName(gitSpec.Reference) + case strings.Contains(gitSpec.Reference, "/"): + return plumbing.ReferenceName(gitSpec.Reference) + default: + return plumbing.NewBranchReferenceName(gitSpec.Reference) } - - return false, "" } func (r *Reconciler) handleGitRepositoryChange(ctx context.Context, proxyName types.NamespacedName, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, remoteCommitHash string, logger logr.Logger) error { @@ -276,38 +242,6 @@ func (r *Reconciler) handleGitRepositoryChange(ctx context.Context, proxyName ty return r.triggerReconciliation(ctx, proxyName, logger) } -func (r *Reconciler) isGitRepositoryConfigured(cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) bool { - if cdk8sAppProxy.Spec.GitRepository == nil || cdk8sAppProxy.Spec.GitRepository.URL == "" { - logger.Info("GitRepository not configured for this Cdk8sAppProxy, stopping polling.") - - return false - } - - return true -} - -func (r *Reconciler) determineGitReference(gitSpec *addonsv1alpha1.GitRepositorySpec, logger logr.Logger) plumbing.ReferenceName { - refName := plumbing.HEAD - if gitSpec.Reference == "" { - return refName - } - - switch { - case plumbing.IsHash(gitSpec.Reference): - logger.Info("Polling a specific commit hash is not actively supported. The poller will check if the remote still has this hash, but it won't detect 'new' commits beyond this specific one. If you want to track a branch, please specify a branch name.", "reference", gitSpec.Reference) - refName = plumbing.ReferenceName(gitSpec.Reference) - case strings.HasPrefix(gitSpec.Reference, "refs/"): - refName = plumbing.ReferenceName(gitSpec.Reference) - case strings.Contains(gitSpec.Reference, "/"): - refName = plumbing.ReferenceName(gitSpec.Reference) - default: - logger.Info("Assuming Git reference is a branch name, prepending 'refs/heads/' for LsRemote.", "reference", gitSpec.Reference) - refName = plumbing.NewBranchReferenceName(gitSpec.Reference) - } - - return refName -} - func (r *Reconciler) fetchRemoteCommitHash(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, gitSpec *addonsv1alpha1.GitRepositorySpec, refName plumbing.ReferenceName, logger logr.Logger) (string, error) { logger.Info("Attempting to LsRemote", "url", gitSpec.URL, "refName", refName.String()) @@ -337,35 +271,26 @@ func (r *Reconciler) manageGitPollerLifecycle(ctx context.Context, cdk8sAppProxy if cdk8sAppProxy.Spec.GitRepository != nil && cdk8sAppProxy.Spec.GitRepository.URL != "" { r.startGitPollerIfNeeded(ctx, proxyNamespacedName, logger) } else { - r.stopGitPollerIfNotNeeded(proxyNamespacedName, logger) + r.stopGitPoller(proxyNamespacedName, logger) } } func (r *Reconciler) startGitPollerIfNeeded(ctx context.Context, proxyNamespacedName types.NamespacedName, logger logr.Logger) { - if _, pollerExists := r.activeGitPollers[proxyNamespacedName]; !pollerExists { + if _, pollerExists := r.ActiveGitPollers[proxyNamespacedName]; !pollerExists { logger.Info("Starting new git poller.") pollCtx, cancelPoll := context.WithCancel(ctx) - r.activeGitPollers[proxyNamespacedName] = cancelPoll + r.ActiveGitPollers[proxyNamespacedName] = cancelPoll go r.pollGitRepository(pollCtx, proxyNamespacedName) } else { logger.Info("Git poller already active.") } } -func (r *Reconciler) stopGitPollerIfNotNeeded(proxyNamespacedName types.NamespacedName, logger logr.Logger) { - if cancel, pollerExists := r.activeGitPollers[proxyNamespacedName]; pollerExists { - logger.Info("GitRepository is not configured, ensuring poller is stopped.") - cancel() - delete(r.activeGitPollers, proxyNamespacedName) - } -} - -// You already have this function, but let's also create a helper for explicit stopping. func (r *Reconciler) stopGitPoller(proxyNamespacedName types.NamespacedName, logger logr.Logger) { - if cancel, pollerExists := r.activeGitPollers[proxyNamespacedName]; pollerExists { + if cancel, pollerExists := r.ActiveGitPollers[proxyNamespacedName]; pollerExists { logger.Info("Stopping git poller.") cancel() - delete(r.activeGitPollers, proxyNamespacedName) + delete(r.ActiveGitPollers, proxyNamespacedName) } else { logger.Info("No active git poller found to stop.") } @@ -391,6 +316,184 @@ func (r *Reconciler) updateRemoteGitHashStatus(ctx context.Context, proxyName ty return nil } +func (r *Reconciler) prepareSource(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, proxyNamespacedName types.NamespacedName, logger logr.Logger) (string, string, func(), error) { + var appSourcePath string + var currentCommitHash string + var cleanupFunc func() + + switch { + case cdk8sAppProxy.Spec.GitRepository != nil && cdk8sAppProxy.Spec.GitRepository.URL != "": + path, hash, cleanup, err := r.prepareGitSource(ctx, cdk8sAppProxy, logger) + if err != nil { + return "", "", nil, err + } + appSourcePath = path + currentCommitHash = hash + cleanupFunc = cleanup + + // Store current commit hash in status + if currentCommitHash != "" { + cdk8sAppProxy.Status.LastRemoteGitHash = currentCommitHash + logger.Info("Updated cdk8sAppProxy.Status.LastRemoteGitHash with the latest commit hash from remote", "lastRemoteGitHash", currentCommitHash) + } + + case cdk8sAppProxy.Spec.LocalPath != "": + logger.Info("Determined source type: LocalPath", "path", cdk8sAppProxy.Spec.LocalPath) + appSourcePath = cdk8sAppProxy.Spec.LocalPath + cleanupFunc = func() {} + + // Stop any existing git poller for a local path + if cancel, ok := r.ActiveGitPollers[proxyNamespacedName]; ok { + logger.Info("GitRepository spec removed or empty, stopping existing git poller.") + cancel() + delete(r.ActiveGitPollers, proxyNamespacedName) + } + + default: + err := errors.New("no source specified") + logger.Error(err, "No source specified (neither GitRepository nor LocalPath)") + if cancel, ok := r.ActiveGitPollers[proxyNamespacedName]; ok { + logger.Info("Source spec is invalid or removed, stopping existing git poller.") + cancel() + delete(r.ActiveGitPollers, proxyNamespacedName) + } + // Use the new consolidated error handler - removeFinalizer = false for normal operations + _ = r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.SourceNotSpecifiedReason, "Neither GitRepository nor LocalPath specified", err, false) + + return "", "", nil, err + } + + return appSourcePath, currentCommitHash, cleanupFunc, nil +} + +func (r *Reconciler) prepareGitSource(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) (string, string, func(), error) { + gitSpec := cdk8sAppProxy.Spec.GitRepository + logger.Info("Determined source type: GitRepository", "url", gitSpec.URL, "reference", gitSpec.Reference, "path", gitSpec.Path) + + tempDir, err := os.MkdirTemp("", "cdk8s-git-clone-") + if err != nil { + logger.Error(err, "Failed to create temp directory for git clone") + _ = r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCloneFailedReason, "Failed to create temp dir for git clone", err, false) + + return "", "", nil, err + } + + cleanupFunc := func() { + if err := os.RemoveAll(tempDir); err != nil { + logger.Error(err, "Failed to remove temporary clone directory", "tempDir", tempDir) + } + } + + logger.Info("Created temporary directory for clone", "tempDir", tempDir) + + // Clone repository + if err := r.cloneGitRepository(ctx, cdk8sAppProxy, gitSpec, tempDir, logger, OperationNormal); err != nil { + cleanupFunc() + + return "", "", nil, err + } + + // Checkout-specific reference if specified + if err := r.checkoutGitReference(ctx, cdk8sAppProxy, gitSpec, tempDir, logger, OperationNormal); err != nil { + cleanupFunc() + + return "", "", nil, err + } + + // Determine a final app source path + appSourcePath := tempDir + if gitSpec.Path != "" { + appSourcePath = filepath.Join(tempDir, gitSpec.Path) + logger.Info("Adjusted appSourcePath for repository subpath", "subPath", gitSpec.Path, "finalPath", appSourcePath) + } + + // Get current commit hash inline + logger.Info("Attempting to retrieve current commit hash from Git repository", "repoDir", tempDir) + repo, err := git.PlainOpen(tempDir) + if err != nil { + cleanupFunc() + + return "", "", nil, r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCloneFailedReason, "Failed to open git repository: "+err.Error(), err, false) + } + + headRef, err := repo.Head() + if err != nil { + cleanupFunc() + + return "", "", nil, r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCloneFailedReason, "Failed to get HEAD reference from git repository: "+err.Error(), err, false) + } + + currentCommitHash := headRef.Hash().String() + logger.Info("Successfully retrieved current commit hash from Git repository", "commitHash", currentCommitHash) + + return appSourcePath, currentCommitHash, cleanupFunc, nil +} + +func (r *Reconciler) checkoutGitReference(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, gitSpec *addonsv1alpha1.GitRepositorySpec, tempDir string, logger logr.Logger, operation string) error { + if gitSpec.Reference == "" { + return nil + } + logger.Info("Executing git checkout with go-git", "reference", gitSpec.Reference, "dir", tempDir, "operation", operation) + + repo, err := git.PlainOpen(tempDir) + if err != nil { + logger.Error(err, "go-git PlainOpen failed", "operation", operation) + if cdk8sAppProxy != nil { + removeFinalizer := operation == OperationDeletion + + return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git PlainOpen failed during "+operation, err, removeFinalizer) + } + + return err + } + + worktree, err := repo.Worktree() + if err != nil { + logger.Error(err, "go-git Worktree failed", "operation", operation) + if cdk8sAppProxy != nil { + removeFinalizer := operation == OperationDeletion + + return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git Worktree failed during "+operation, err, removeFinalizer) + } + + return err + } + + checkoutOpts := &git.CheckoutOptions{Force: true} + if plumbing.IsHash(gitSpec.Reference) { + checkoutOpts.Hash = plumbing.NewHash(gitSpec.Reference) + } else { + revision := plumbing.Revision(gitSpec.Reference) + resolvedHash, err := repo.ResolveRevision(revision) + if err != nil { + logger.Error(err, "go-git ResolveRevision failed", "reference", gitSpec.Reference, "operation", operation) + if cdk8sAppProxy != nil { + removeFinalizer := operation == OperationDeletion + + return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git ResolveRevision failed for ref "+gitSpec.Reference+" during "+operation, err, removeFinalizer) + } + + return err + } + checkoutOpts.Hash = *resolvedHash + } + + err = worktree.Checkout(checkoutOpts) + if err != nil { + logger.Error(err, "go-git Checkout failed", "reference", gitSpec.Reference, "operation", operation) + if cdk8sAppProxy != nil { + removeFinalizer := operation == OperationDeletion + + return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git Checkout failed for ref "+gitSpec.Reference+" during "+operation, err, removeFinalizer) + } + + return err + } + logger.Info("Successfully checked out git reference with go-git", "reference", gitSpec.Reference, "operation", operation) + + return nil +} + func (gpl *gitProgressLogger) Write(p []byte) (n int, err error) { gpl.buffer = append(gpl.buffer, p...) for { diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go b/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go index 426b3f9..b83d429 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_reconciler.go @@ -2,13 +2,9 @@ package cdk8sappproxy import ( "context" - "os" - "path/filepath" "time" addonsv1alpha1 "github.com/PatrickLaabs/cluster-api-addon-provider-cdk8s/api/v1alpha1" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" "github.com/go-logr/logr" "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -34,16 +30,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu logger := log.FromContext(ctx).WithValues("cdk8sappproxy", req.NamespacedName) logger.Info("Starting Reconcile") - // Initialize activeGitPollers map if it's nil - if r.activeGitPollers == nil { - r.activeGitPollers = make(map[types.NamespacedName]context.CancelFunc) - } - // Initialize ActiveWatches map if it's nil - // Note: This was moved from reconcileNormal to ensure it's initialized before any delete or normal path. - if r.ActiveWatches == nil { - r.ActiveWatches = make(map[types.NamespacedName]map[string]context.CancelFunc) - } - cdk8sAppProxy := &addonsv1alpha1.Cdk8sAppProxy{} if err := r.Get(ctx, req.NamespacedName, cdk8sAppProxy); err != nil { if apierrors.IsNotFound(err) { @@ -122,9 +108,6 @@ func (r *Reconciler) reconcileNormal(ctx context.Context, cdk8sAppProxy *addonsv return ctrl.Result{}, err } - // Initialize active watches for this proxy - r.initializeActiveWatches(proxyNamespacedName) - // Add finalizer if needed if shouldRequeue, err := r.ensureFinalizer(ctx, cdk8sAppProxy, logger); err != nil || shouldRequeue { return ctrl.Result{Requeue: shouldRequeue}, err @@ -200,12 +183,6 @@ func (r *Reconciler) handleDeletionTriggerAnnotation(ctx context.Context, cdk8sA return forceSynthAndApplyDueToDeletion, nil } -func (r *Reconciler) initializeActiveWatches(proxyNamespacedName types.NamespacedName) { - if r.ActiveWatches[proxyNamespacedName] == nil { - r.ActiveWatches[proxyNamespacedName] = make(map[string]context.CancelFunc) - } -} - func (r *Reconciler) ensureFinalizer(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) (bool, error) { if !controllerutil.ContainsFinalizer(cdk8sAppProxy, Finalizer) { logger.Info("Adding finalizer", "finalizer", Finalizer) @@ -223,196 +200,6 @@ func (r *Reconciler) ensureFinalizer(ctx context.Context, cdk8sAppProxy *addonsv return false, nil } -func (r *Reconciler) prepareSource(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, proxyNamespacedName types.NamespacedName, logger logr.Logger) (string, string, func(), error) { - var appSourcePath string - var currentCommitHash string - var cleanupFunc func() - - switch { - case cdk8sAppProxy.Spec.GitRepository != nil && cdk8sAppProxy.Spec.GitRepository.URL != "": - path, hash, cleanup, err := r.prepareGitSource(ctx, cdk8sAppProxy, logger) - if err != nil { - return "", "", nil, err - } - appSourcePath = path - currentCommitHash = hash - cleanupFunc = cleanup - - // Store current commit hash in status - if currentCommitHash != "" { - cdk8sAppProxy.Status.LastRemoteGitHash = currentCommitHash - logger.Info("Updated cdk8sAppProxy.Status.LastRemoteGitHash with the latest commit hash from remote", "lastRemoteGitHash", currentCommitHash) - } - - case cdk8sAppProxy.Spec.LocalPath != "": - logger.Info("Determined source type: LocalPath", "path", cdk8sAppProxy.Spec.LocalPath) - appSourcePath = cdk8sAppProxy.Spec.LocalPath - cleanupFunc = func() {} - - // Stop any existing git poller for a local path - if cancel, ok := r.activeGitPollers[proxyNamespacedName]; ok { - logger.Info("GitRepository spec removed or empty, stopping existing git poller.") - cancel() - delete(r.activeGitPollers, proxyNamespacedName) - } - - default: - err := errors.New("no source specified") - logger.Error(err, "No source specified (neither GitRepository nor LocalPath)") - if cancel, ok := r.activeGitPollers[proxyNamespacedName]; ok { - logger.Info("Source spec is invalid or removed, stopping existing git poller.") - cancel() - delete(r.activeGitPollers, proxyNamespacedName) - } - // Use the new consolidated error handler - removeFinalizer = false for normal operations - _ = r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.SourceNotSpecifiedReason, "Neither GitRepository nor LocalPath specified", err, false) - - return "", "", nil, err - } - - return appSourcePath, currentCommitHash, cleanupFunc, nil -} - -func (r *Reconciler) prepareGitSource(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) (string, string, func(), error) { - gitSpec := cdk8sAppProxy.Spec.GitRepository - logger.Info("Determined source type: GitRepository", "url", gitSpec.URL, "reference", gitSpec.Reference, "path", gitSpec.Path) - - tempDir, err := os.MkdirTemp("", "cdk8s-git-clone-") - if err != nil { - logger.Error(err, "Failed to create temp directory for git clone") - _ = r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCloneFailedReason, "Failed to create temp dir for git clone", err, false) - - return "", "", nil, err - } - - cleanupFunc := func() { - logger.Info("Removing temporary clone directory", "tempDir", tempDir) - if err := os.RemoveAll(tempDir); err != nil { - logger.Error(err, "Failed to remove temporary clone directory", "tempDir", tempDir) - } - } - - logger.Info("Created temporary directory for clone", "tempDir", tempDir) - - // Clone repository - if err := r.cloneGitRepository(ctx, cdk8sAppProxy, gitSpec, tempDir, logger, OperationNormal); err != nil { - cleanupFunc() - - return "", "", nil, err - } - - // Checkout-specific reference if specified - if err := r.checkoutGitReference(ctx, cdk8sAppProxy, gitSpec, tempDir, logger, OperationNormal); err != nil { - cleanupFunc() - - return "", "", nil, err - } - - // Determine a final app source path - appSourcePath := tempDir - if gitSpec.Path != "" { - appSourcePath = filepath.Join(tempDir, gitSpec.Path) - logger.Info("Adjusted appSourcePath for repository subpath", "subPath", gitSpec.Path, "finalPath", appSourcePath) - } - - // Get current commit hash - currentCommitHash, err := r.getCurrentCommitHash(tempDir, logger) - if err != nil { - cleanupFunc() - - return "", "", nil, r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCloneFailedReason, "Failed to get commit hash: "+err.Error(), err, false) - } - - return appSourcePath, currentCommitHash, cleanupFunc, nil -} - -func (r *Reconciler) checkoutGitReference(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, gitSpec *addonsv1alpha1.GitRepositorySpec, tempDir string, logger logr.Logger, operation string) error { - if gitSpec.Reference == "" { - return nil - } - logger.Info("Executing git checkout with go-git", "reference", gitSpec.Reference, "dir", tempDir, "operation", operation) - - repo, err := git.PlainOpen(tempDir) - if err != nil { - logger.Error(err, "go-git PlainOpen failed", "operation", operation) - if cdk8sAppProxy != nil { - removeFinalizer := operation == OperationDeletion - - return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git PlainOpen failed during "+operation, err, removeFinalizer) - } - - return err - } - - worktree, err := repo.Worktree() - if err != nil { - logger.Error(err, "go-git Worktree failed", "operation", operation) - if cdk8sAppProxy != nil { - removeFinalizer := operation == OperationDeletion - - return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git Worktree failed during "+operation, err, removeFinalizer) - } - - return err - } - - checkoutOpts := &git.CheckoutOptions{Force: true} - if plumbing.IsHash(gitSpec.Reference) { - checkoutOpts.Hash = plumbing.NewHash(gitSpec.Reference) - } else { - revision := plumbing.Revision(gitSpec.Reference) - resolvedHash, err := repo.ResolveRevision(revision) - if err != nil { - logger.Error(err, "go-git ResolveRevision failed", "reference", gitSpec.Reference, "operation", operation) - if cdk8sAppProxy != nil { - removeFinalizer := operation == OperationDeletion - - return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git ResolveRevision failed for ref "+gitSpec.Reference+" during "+operation, err, removeFinalizer) - } - - return err - } - checkoutOpts.Hash = *resolvedHash - } - - err = worktree.Checkout(checkoutOpts) - if err != nil { - logger.Error(err, "go-git Checkout failed", "reference", gitSpec.Reference, "operation", operation) - if cdk8sAppProxy != nil { - removeFinalizer := operation == OperationDeletion - - return r.updateStatusWithError(ctx, cdk8sAppProxy, addonsv1alpha1.GitCheckoutFailedReason, "go-git Checkout failed for ref "+gitSpec.Reference+" during "+operation, err, removeFinalizer) - } - - return err - } - logger.Info("Successfully checked out git reference with go-git", "reference", gitSpec.Reference, "operation", operation) - - return nil -} - -func (r *Reconciler) getCurrentCommitHash(tempDir string, logger logr.Logger) (string, error) { - logger.Info("Attempting to retrieve current commit hash from Git repository", "repoDir", tempDir) - repo, err := git.PlainOpen(tempDir) - if err != nil { - logger.Error(err, "Failed to open git repository after clone/checkout") - - return "", err - } - - headRef, err := repo.Head() - if err != nil { - logger.Error(err, "Failed to get HEAD reference from git repository") - - return "", err - } - - commitHash := headRef.Hash().String() - logger.Info("Successfully retrieved current commit hash from Git repository", "commitHash", commitHash) - - return commitHash, nil -} - func (r *Reconciler) handleNoResources(ctx context.Context, cdk8sAppProxy *addonsv1alpha1.Cdk8sAppProxy, logger logr.Logger) error { logger.Info("No valid Kubernetes resources parsed from manifest files") conditions.MarkFalse(cdk8sAppProxy, addonsv1alpha1.DeploymentProgressingCondition, addonsv1alpha1.NoResourcesParsedReason, clusterv1.ConditionSeverityWarning, "No valid Kubernetes resources found in manifests") @@ -488,7 +275,7 @@ func (r *Reconciler) verifyResourcesOnClusters(ctx context.Context, cdk8sAppProx } for _, resource := range parsedResources { gvr := resource.GroupVersionKind().GroupVersion().WithResource(getPluralFromKind(resource.GetKind())) - exists, checkErr := checkIfResourceExists(ctx, dynamicClient, gvr, resource.GetNamespace(), resource.GetName()) + exists, checkErr := r.checkIfResourceExists(ctx, dynamicClient, gvr, resource.GetNamespace(), resource.GetName()) if checkErr != nil { clusterLogger.Error(checkErr, "Error checking resource existence. Assuming resource missing.", "resourceName", resource.GetName(), "GVK", gvr) foundMissingResourcesOnAnyCluster = true @@ -625,22 +412,11 @@ func (r *Reconciler) reestablishWatchesForExistingResources(ctx context.Context, for _, resource := range parsedResources { gvk := resource.GroupVersionKind() - watchKey := string(cluster.GetUID()) + "/" + resource.GetNamespace() + "/" + resource.GetName() + "/" + gvk.String() - // Check if watch already exists using ActiveWatches map - if r.ActiveWatches != nil && r.ActiveWatches[proxyNamespacedName] != nil { - if _, exists := r.ActiveWatches[proxyNamespacedName][watchKey]; exists { - logger.Info("Watch already exists, skipping re-establishment", "watchKey", watchKey, "cluster", cluster.Name) - - continue - } - } - - // Start the watch since it doesn't exist - if err := r.startResourceWatch(ctx, dynamicClient, gvk, resource.GetNamespace(), resource.GetName(), proxyNamespacedName, watchKey); err != nil { - logger.Error(err, "Failed to re-establish watch", "watchKey", watchKey, "cluster", cluster.Name) + if err := r.WatchManager.StartWatch(ctx, dynamicClient, gvk, resource.GetNamespace(), resource.GetName(), proxyNamespacedName); err != nil { + logger.Error(err, "Failed to re-establish watch", "cluster", cluster.Name, "resource", resource.GetName()) } else { - logger.Info("Re-established watch for existing resource", "watchKey", watchKey, "cluster", cluster.Name) + logger.Info("Re-established watch for existing resource", "cluster", cluster.Name, "resource", resource.GetName()) } } } @@ -728,15 +504,11 @@ func (r *Reconciler) applyResourcesToClusters(ctx context.Context, cdk8sAppProxy conditions.MarkFalse(cdk8sAppProxy, addonsv1alpha1.DeploymentProgressingCondition, addonsv1alpha1.ResourceApplyFailedReason, clusterv1.ConditionSeverityError, "Failed to apply %s %s to cluster %s: %v", gvk.Kind, resourceCopy.GetName(), cluster.Name, applyErr) } else { clusterLogger.Info("Successfully applied resource to cluster", "resourceName", resourceCopy.GetName()) - watchKey := string(cluster.GetUID()) + "/" + resourceCopy.GetNamespace() + "/" + resourceCopy.GetName() + "/" + gvk.String() - - // Stop existing watch if it exists - r.WatchManager.Stop(proxyNamespacedName, watchKey) - if err := r.startResourceWatch(ctx, dynamicClient, gvk, appliedResource.GetNamespace(), appliedResource.GetName(), proxyNamespacedName, watchKey); err != nil { - clusterLogger.Error(err, "Failed to start watch for applied resource", "watchKey", watchKey) + if err := r.WatchManager.StartWatch(ctx, dynamicClient, gvk, appliedResource.GetNamespace(), appliedResource.GetName(), proxyNamespacedName); err != nil { + clusterLogger.Error(err, "Failed to start watch for applied resource") } else { - clusterLogger.Info("Successfully started watch for applied resource", "watchKey", watchKey) + clusterLogger.Info("Successfully started watch for applied resource") } } } diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_resourcewatcher.go b/controllers/cdk8sappproxy/cdk8sappproxy_resourcewatcher.go index 4b9ebe5..ff82988 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_resourcewatcher.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_resourcewatcher.go @@ -6,7 +6,6 @@ import ( "time" addonsv1alpha1 "github.com/PatrickLaabs/cluster-api-addon-provider-cdk8s/api/v1alpha1" - "github.com/go-logr/logr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" @@ -16,102 +15,122 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -// ResourceWatcher watches resources on target clusters. -type ResourceWatcher interface { - Watch(ctx context.Context, config WatchConfig) error +// ResourceWatchManager manages resource watches on target clusters. +type ResourceWatchManager struct { + mu sync.RWMutex + watches map[types.NamespacedName]map[string]context.CancelFunc + client client.Client } -// EventHandler handles different types of watch events. -type EventHandler interface { - OnDeleted(ctx context.Context, config WatchConfig) error - OnOther(ctx context.Context, eventType watch.EventType, config WatchConfig) error - OnClosed(ctx context.Context, config WatchConfig) - OnError(ctx context.Context, err error, config WatchConfig) - - SetWatchManager(wm WatchManager) +// NewResourceWatchManager creates a new resource watch manager. +func NewResourceWatchManager(client client.Client) *ResourceWatchManager { + return &ResourceWatchManager{ + watches: make(map[types.NamespacedName]map[string]context.CancelFunc), + client: client, + } } -// WatchManager manages the lifecycle of resource watches. -type WatchManager interface { - Start(ctx context.Context, config WatchConfig) error - Stop(parentProxy types.NamespacedName, watchKey string) - Cleanup(parentProxy types.NamespacedName) -} +// StartWatch starts watching a resource for deletion. +func (m *ResourceWatchManager) StartWatch(ctx context.Context, targetClient dynamic.Interface, gvk schema.GroupVersionKind, namespace, name string, parentProxy types.NamespacedName) error { + watchKey := gvk.String() + "/" + namespace + "/" + name + + m.mu.Lock() + defer m.mu.Unlock() + + logger := log.FromContext(ctx).WithValues("watchKey", watchKey, "parentProxy", parentProxy.String()) + + // Check if already watching + if m.isActive(parentProxy, watchKey) { + logger.Info("Watch already active") -// WatchConfig encapsulates all watch configuration. -type WatchConfig struct { - TargetClient dynamic.Interface - GVK schema.GroupVersionKind - Namespace string - Name string - ParentProxy types.NamespacedName - WatchKey string + return nil + } + + // Start the watch + watchCtx, cancel := context.WithCancel(ctx) + m.store(parentProxy, watchKey, cancel) + + go func() { + defer cancel() + if err := m.watchResource(watchCtx, targetClient, gvk, namespace, name, parentProxy, watchKey); err != nil { + logger.Error(err, "Watch failed") + } + }() + + logger.Info("Started resource watch") + + return nil } -// resourceWatcher implements ResourceWatcher. -type resourceWatcher struct { - events EventHandler +// StopWatch stops a specific watch. +func (m *ResourceWatchManager) StopWatch(parentProxy types.NamespacedName, watchKey string) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.watches[parentProxy] != nil { + if cancel, ok := m.watches[parentProxy][watchKey]; ok { + cancel() + delete(m.watches[parentProxy], watchKey) + } + } } -// NewResourceWatcher creates a new resource watcher. -func NewResourceWatcher(events EventHandler) ResourceWatcher { - return &resourceWatcher{ - events: events, +// CleanupWatches stops all watches for a parent proxy. +func (m *ResourceWatchManager) CleanupWatches(parentProxy types.NamespacedName) { + m.mu.Lock() + defer m.mu.Unlock() + + if watches, ok := m.watches[parentProxy]; ok { + for _, cancel := range watches { + cancel() + } + delete(m.watches, parentProxy) } } -func (w *resourceWatcher) Watch(ctx context.Context, config WatchConfig) error { +// watchResource performs the actual watching logic. +func (m *ResourceWatchManager) watchResource(ctx context.Context, targetClient dynamic.Interface, gvk schema.GroupVersionKind, namespace, name string, parentProxy types.NamespacedName, watchKey string) error { logger := log.FromContext(ctx).WithValues( - "watchKey", config.WatchKey, - "gvk", config.GVK.String(), - "resourceNamespace", config.Namespace, - "resourceName", config.Name, - "parentProxy", config.ParentProxy.String(), + "gvk", gvk.String(), + "namespace", namespace, + "name", name, + "parentProxy", parentProxy.String(), ) - logger.Info("Starting resource watch") - watcher, err := w.createWatcher(ctx, config) + gvr := gvk.GroupVersion().WithResource(getPluralFromKind(gvk.Kind)) + + watcher, err := targetClient.Resource(gvr).Namespace(namespace).Watch(ctx, metav1.ListOptions{ + FieldSelector: "metadata.name=" + name, + }) if err != nil { - w.events.OnError(ctx, err, config) + logger.Error(err, "Failed to create watcher") return err } defer watcher.Stop() - return w.processEvents(ctx, watcher, config, logger) -} - -func (w *resourceWatcher) createWatcher(ctx context.Context, config WatchConfig) (watch.Interface, error) { - gvr := config.GVK.GroupVersion().WithResource(getPluralFromKind(config.GVK.Kind)) - - return config.TargetClient.Resource(gvr).Namespace(config.Namespace).Watch(ctx, metav1.ListOptions{ - FieldSelector: "metadata.name=" + config.Name, - }) -} - -func (w *resourceWatcher) processEvents(ctx context.Context, watcher watch.Interface, config WatchConfig, logger logr.Logger) error { for { select { case event, ok := <-watcher.ResultChan(): if !ok { - w.events.OnClosed(ctx, config) + logger.Info("Watch closed") + m.StopWatch(parentProxy, watchKey) return nil } - logger.Info("Received watch event", "type", event.Type) - if event.Type == watch.Deleted { - if err := w.events.OnDeleted(ctx, config); err != nil { - logger.Error(err, "Failed to handle deletion") + logger.Info("Resource deleted, triggering reconciliation") + if err := m.triggerReconciliation(ctx, parentProxy); err != nil { + logger.Error(err, "Failed to trigger reconciliation") } + m.StopWatch(parentProxy, watchKey) return nil } - if err := w.events.OnOther(ctx, event.Type, config); err != nil { - logger.Error(err, "Failed to handle event", "type", event.Type) - } + // For other events (Added, Modified), just continue watching + logger.V(1).Info("Received non-deletion event", "type", event.Type) case <-ctx.Done(): logger.Info("Watch cancelled") @@ -121,72 +140,10 @@ func (w *resourceWatcher) processEvents(ctx context.Context, watcher watch.Inter } } -// eventHandler implements EventHandler. -type eventHandler struct { - client client.Client - watchManager WatchManager -} - -// NewEventHandler creates a new event handler. -func NewEventHandler(client client.Client, watchManager WatchManager) EventHandler { - return &eventHandler{ - client: client, - watchManager: watchManager, - } -} - -// Add a setter method for the circular dependency. -func (h *eventHandler) SetWatchManager(wm WatchManager) { - h.watchManager = wm -} - -func (h *eventHandler) OnDeleted(ctx context.Context, config WatchConfig) error { - logger := log.FromContext(ctx).WithValues("parentProxy", config.ParentProxy.String()) - logger.Info("Resource deleted, triggering parent reconciliation") - - if err := h.triggerReconciliation(ctx, config.ParentProxy); err != nil { - logger.Error(err, "Failed to trigger reconciliation") - - return err - } - - // Only call Stop if watchManager is set - if h.watchManager != nil { - h.watchManager.Stop(config.ParentProxy, config.WatchKey) - } - - return nil -} - -// ToDo. -func (h *eventHandler) OnOther(ctx context.Context, eventType watch.EventType, config WatchConfig) error { - logger := log.FromContext(ctx) - logger.Info("Received non-deletion event", "type", eventType) - // Continue watching - reconciliation loop handles desired state - return nil -} - -func (h *eventHandler) OnClosed(ctx context.Context, config WatchConfig) { - logger := log.FromContext(ctx) - logger.Info("Watch closed") - - if h.watchManager != nil { - h.watchManager.Stop(config.ParentProxy, config.WatchKey) - } -} - -func (h *eventHandler) OnError(ctx context.Context, err error, config WatchConfig) { - logger := log.FromContext(ctx) - logger.Error(err, "Watch failed") - - if h.watchManager != nil { - h.watchManager.Stop(config.ParentProxy, config.WatchKey) - } -} - -func (h *eventHandler) triggerReconciliation(ctx context.Context, parentProxy types.NamespacedName) error { +// triggerReconciliation triggers a reconciliation by updating the parent proxy. +func (m *ResourceWatchManager) triggerReconciliation(ctx context.Context, parentProxy types.NamespacedName) error { proxy := &addonsv1alpha1.Cdk8sAppProxy{} - if err := h.client.Get(ctx, parentProxy, proxy); err != nil { + if err := m.client.Get(ctx, parentProxy, proxy); err != nil { return err } @@ -196,74 +153,10 @@ func (h *eventHandler) triggerReconciliation(ctx context.Context, parentProxy ty proxy.Annotations["cdk8s.addons.cluster.x-k8s.io/reconcile-trigger"] = metav1.Now().Format(time.RFC3339Nano) - return h.client.Update(ctx, proxy) -} - -// watchManager implements WatchManager. -type watchManager struct { - mu sync.RWMutex - watches map[types.NamespacedName]map[string]context.CancelFunc - resourceWatcher ResourceWatcher -} - -// NewWatchManager creates a new watch manager. -func NewWatchManager(resourceWatcher ResourceWatcher) WatchManager { - return &watchManager{ - watches: make(map[types.NamespacedName]map[string]context.CancelFunc), - resourceWatcher: resourceWatcher, - } -} - -func (m *watchManager) Start(ctx context.Context, config WatchConfig) error { - m.mu.Lock() - defer m.mu.Unlock() - - logger := log.FromContext(ctx) - - if m.isActive(config.ParentProxy, config.WatchKey) { - logger.Info("Watch already active", "key", config.WatchKey) - - return nil - } - - watchCtx, cancel := context.WithCancel(ctx) - m.store(config.ParentProxy, config.WatchKey, cancel) - - go func() { - defer cancel() - if err := m.resourceWatcher.Watch(watchCtx, config); err != nil { - logger.Error(err, "Watch failed", "key", config.WatchKey) - } - }() - - return nil -} - -func (m *watchManager) Stop(parentProxy types.NamespacedName, watchKey string) { - m.mu.Lock() - defer m.mu.Unlock() - - if m.watches[parentProxy] != nil { - if cancel, ok := m.watches[parentProxy][watchKey]; ok { - cancel() - delete(m.watches[parentProxy], watchKey) - } - } + return m.client.Update(ctx, proxy) } -func (m *watchManager) Cleanup(parentProxy types.NamespacedName) { - m.mu.Lock() - defer m.mu.Unlock() - - if watches, ok := m.watches[parentProxy]; ok { - for _, cancel := range watches { - cancel() - } - delete(m.watches, parentProxy) - } -} - -func (m *watchManager) isActive(parentProxy types.NamespacedName, watchKey string) bool { +func (m *ResourceWatchManager) isActive(parentProxy types.NamespacedName, watchKey string) bool { if m.watches[parentProxy] == nil { return false } @@ -272,22 +165,9 @@ func (m *watchManager) isActive(parentProxy types.NamespacedName, watchKey strin return exists } -func (m *watchManager) store(parentProxy types.NamespacedName, watchKey string, cancel context.CancelFunc) { +func (m *ResourceWatchManager) store(parentProxy types.NamespacedName, watchKey string, cancel context.CancelFunc) { if m.watches[parentProxy] == nil { m.watches[parentProxy] = make(map[string]context.CancelFunc) } m.watches[parentProxy][watchKey] = cancel } - -func (r *Reconciler) startResourceWatch(ctx context.Context, targetClient dynamic.Interface, gvk schema.GroupVersionKind, namespace, name string, parentProxy types.NamespacedName, watchKey string) error { - config := WatchConfig{ - TargetClient: targetClient, - GVK: gvk, - Namespace: namespace, - Name: name, - ParentProxy: parentProxy, - WatchKey: watchKey, - } - - return r.WatchManager.Start(ctx, config) -} diff --git a/controllers/cdk8sappproxy/cdk8sappproxy_types.go b/controllers/cdk8sappproxy/cdk8sappproxy_types.go index 5576bec..737af9a 100644 --- a/controllers/cdk8sappproxy/cdk8sappproxy_types.go +++ b/controllers/cdk8sappproxy/cdk8sappproxy_types.go @@ -22,7 +22,6 @@ type Reconciler struct { Scheme *runtime.Scheme Recorder record.EventRecorder - WatchManager WatchManager - ActiveWatches map[types.NamespacedName]map[string]context.CancelFunc - activeGitPollers map[types.NamespacedName]context.CancelFunc + WatchManager *ResourceWatchManager + ActiveGitPollers map[types.NamespacedName]context.CancelFunc } diff --git a/main.go b/main.go index f6ea53b..9ba7072 100644 --- a/main.go +++ b/main.go @@ -206,11 +206,10 @@ func main() { ctx := ctrl.SetupSignalHandler() if err = (&caapccontroller.Reconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("cdk8sappproxy-controller"), - // Initialize legacy maps for gradual migration - ActiveWatches: make(map[types.NamespacedName]map[string]context.CancelFunc), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("cdk8sappproxy-controller"), + ActiveGitPollers: make(map[types.NamespacedName]context.CancelFunc), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Cdk8sAppProxy") os.Exit(1) From ede047de57faf747a4458941b6be85d717b14685 Mon Sep 17 00:00:00 2001 From: PatrickLaabs Date: Tue, 17 Jun 2025 17:03:47 +0200 Subject: [PATCH 3/4] preparing for the next release --- Makefile | 4 ++-- config/default/manager_image_patch.yaml | 2 +- config/default/manager_image_patch.yaml-e | 2 +- config/default/manager_pull_policy.yaml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 93ffbd2..0020998 100644 --- a/Makefile +++ b/Makefile @@ -230,8 +230,8 @@ CAPI_KIND_CLUSTER_NAME ?= capi-test # It is set by Prow GIT_TAG, a git-based tag of the form vYYYYMMDD-hash, e.g., v20210120-v0.3.10-308-gc61521971 -# Next release is: v0.3.1-alpha -TAG ?= v0.3.1-preview.3 +# Next release is: v0.3.2-alpha +TAG ?= v0.3.1-alpha ARCH ?= $(shell go env GOARCH) ALL_ARCH = amd64 arm arm64 diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 105fba3..ef9fc23 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller-arm64:v0.3.1-preview.3 + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview.3 name: manager diff --git a/config/default/manager_image_patch.yaml-e b/config/default/manager_image_patch.yaml-e index 4c24f31..ef9fc23 100644 --- a/config/default/manager_image_patch.yaml-e +++ b/config/default/manager_image_patch.yaml-e @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller-arm:v0.3.1-preview.3 + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview.3 name: manager diff --git a/config/default/manager_pull_policy.yaml b/config/default/manager_pull_policy.yaml index 74a0879..cd7ae12 100644 --- a/config/default/manager_pull_policy.yaml +++ b/config/default/manager_pull_policy.yaml @@ -8,4 +8,4 @@ spec: spec: containers: - name: manager - imagePullPolicy: Always + imagePullPolicy: IfNotPresent From 5235ff2b348ae7a63e18d521ec7a94e3fad31513 Mon Sep 17 00:00:00 2001 From: PatrickLaabs Date: Tue, 17 Jun 2025 17:07:41 +0200 Subject: [PATCH 4/4] adding the latest release for the controller --- config/default/manager_image_patch.yaml | 2 +- config/default/manager_image_patch.yaml-e | 2 +- config/default/manager_pull_policy.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index ef9fc23..6c496f9 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview.3 + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-alpha name: manager diff --git a/config/default/manager_image_patch.yaml-e b/config/default/manager_image_patch.yaml-e index ef9fc23..6c7a6c5 100644 --- a/config/default/manager_image_patch.yaml-e +++ b/config/default/manager_image_patch.yaml-e @@ -7,5 +7,5 @@ spec: template: spec: containers: - - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller:v0.3.1-preview.3 + - image: ghcr.io/patricklaabs/cluster-api-addon-provider-cdk8s/cluster-api-cdk8s-controller-arm64:v0.3.1-alpha name: manager diff --git a/config/default/manager_pull_policy.yaml b/config/default/manager_pull_policy.yaml index cd7ae12..74a0879 100644 --- a/config/default/manager_pull_policy.yaml +++ b/config/default/manager_pull_policy.yaml @@ -8,4 +8,4 @@ spec: spec: containers: - name: manager - imagePullPolicy: IfNotPresent + imagePullPolicy: Always