diff --git a/Makefile b/Makefile index fc536085..63b5eb17 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ KUBECTL := $(TOOLS_BIN_DIR)/kubectl GOVULNCHECK_VERSION := "v1.1.4" GOLANGCI_LINT_VERSION := "v2.13.2" -CLUSTERCTL_VERSION := v1.14.2 +CLUSTERCTL_VERSION := v1.14.3 KIND_VERSION := v0.32.0 KUSTOMIZE_VER := v5.8.0 diff --git a/controllers/handlers_utils.go b/controllers/handlers_utils.go index 9b35d464..c87c7c08 100644 --- a/controllers/handlers_utils.go +++ b/controllers/handlers_utils.go @@ -1206,16 +1206,24 @@ func processDeployedGVKs(ctx context.Context, isMgmtCluster bool, remoteConfig * continue } - // Only rely on the clustersummary annotation as a protective signal: skip a - // resource here when it is explicitly annotated for a *different* ClusterSummary - // (relevant when this ClusterSummary's remote cluster is the self-managed - // management cluster, and another ClusterSummary deployed there via + // The management cluster pass only removes what this very ClusterSummary deployed + // there via deploymentType: Local, so the resource must carry the clustersummary + // annotation of this ClusterSummary. That annotation has always been set on Local + // deploys. A resource without it was not deployed locally by this ClusterSummary: + // for instance, the management cluster can itself be a managed cluster of another + // Sveltos instance, which deployed resources owned by a (Cluster)Profile with the + // very same name. Those must never be removed here. + // + // The managed cluster pass is lenient: the annotation is only a protective signal. + // Skip a resource here when it is explicitly annotated for a *different* + // ClusterSummary (relevant when this ClusterSummary's remote cluster is the + // self-managed management cluster, and another ClusterSummary deployed there via // deploymentType: Local). A resource missing the annotation entirely predates it // being set on every deploy, and must still fall through to the normal // canDelete/isResourceOwner checks below, or upgraded ClusterSummaries would never // detect their own pre-existing resources as stale again. var skipAnnotationKey, skipAnnotationValue string - if _, ok := r.GetAnnotations()[clusterSummaryAnnotation]; ok { + if _, ok := r.GetAnnotations()[clusterSummaryAnnotation]; ok || isMgmtCluster { skipAnnotationKey = clusterSummaryAnnotation skipAnnotationValue = ownClusterSummaryValue } diff --git a/controllers/handlers_utils_test.go b/controllers/handlers_utils_test.go index a50b6796..3e8a79bd 100644 --- a/controllers/handlers_utils_test.go +++ b/controllers/handlers_utils_test.go @@ -1365,6 +1365,189 @@ var _ = Describe("HandlersUtils", func() { }, timeout, pollingInterval).Should(BeTrue()) }) + It(`undeployStaleResources in the management cluster only removes resources annotated for this ClusterSummary`, func() { + // The management cluster can itself be a managed cluster of another Sveltos instance. That + // instance deployed resources owned by a (Cluster)Profile with the same name as the one this + // ClusterSummary belongs to, but without the clustersummary annotation (for instance deployed + // by a release that did not set it on managed cluster deploys). The management cluster pass + // (isMgmtCluster=true) must not consider those stale: this ClusterSummary never deployed them. + newClusterRole := func(annotations map[string]string) *rbacv1.ClusterRole { + annotations[deployer.ReferenceKindAnnotation] = string(libsveltosv1beta1.ConfigMapReferencedResourceKind) + annotations[deployer.ReferenceNamespaceAnnotation] = randomString() + annotations[deployer.ReferenceNameAnnotation] = randomString() + + return &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: randomString(), + Labels: map[string]string{ + deployer.ReasonLabel: string(libsveltosv1beta1.FeatureResources), + }, + Annotations: annotations, + }, + } + } + + // Deployed by this ClusterSummary via deploymentType: Local + ownClusterRole := newClusterRole(map[string]string{ + controllers.ClusterSummaryAnnotation: controllers.GetClusterSummaryAnnotationValue(clusterSummary), + controllers.DeploymentTypeAnnotation: string(configv1beta1.DeploymentTypeLocal), + }) + // Deployed by a different ClusterSummary + otherClusterRole := newClusterRole(map[string]string{ + controllers.ClusterSummaryAnnotation: randomString(), + }) + // No clustersummary annotation at all + unannotatedClusterRole := newClusterRole(map[string]string{}) + + clusterRoles := []*rbacv1.ClusterRole{ownClusterRole, otherClusterRole, unannotatedClusterRole} + for _, clusterRole := range clusterRoles { + Expect(testEnv.Create(context.TODO(), clusterRole)).To(Succeed()) + Expect(waitForObject(ctx, testEnv.Client, clusterRole)).To(Succeed()) + } + + currentClusterProfile := &configv1beta1.ClusterProfile{} + Expect(testEnv.Get(context.TODO(), + types.NamespacedName{Name: clusterProfile.Name}, + currentClusterProfile)).To(Succeed()) + for _, clusterRole := range clusterRoles { + addOwnerReference(context.TODO(), testEnv.Client, clusterRole, currentClusterProfile) + } + + currentClusterSummary := &configv1beta1.ClusterSummary{} + Expect(testEnv.Get(context.TODO(), + types.NamespacedName{Namespace: clusterSummary.Namespace, Name: clusterSummary.Name}, + currentClusterSummary)).To(Succeed()) + currentClusterSummary.Status.FeatureSummaries = []configv1beta1.FeatureSummary{ + { + FeatureID: libsveltosv1beta1.FeatureResources, + Status: libsveltosv1beta1.FeatureStatusProvisioned, + }, + } + currentClusterSummary.Status.DeployedGVKs = []libsveltosv1beta1.FeatureDeploymentInfo{ + { + FeatureID: libsveltosv1beta1.FeatureResources, + DeployedGroupVersionKind: []string{ + testClusterRoleKindV1, + }, + }, + } + Expect(testEnv.Status().Update(context.TODO(), currentClusterSummary)).To(Succeed()) + + deployedGKVs := controllers.GetDeployedGroupVersionKinds(currentClusterSummary, libsveltosv1beta1.FeatureResources) + Expect(deployedGKVs).ToNot(BeEmpty()) + + // None of the ClusterRoles is in currentPolicies (nil): all are candidates for deletion + // were it not for the clustersummary annotation scoping. + _, err := controllers.UndeployStaleResources(context.TODO(), true, testEnv.Config, testEnv.Client, + libsveltosv1beta1.FeatureResources, currentClusterSummary, deployedGKVs, nil, + textlogger.NewLogger(textlogger.NewConfig())) + Expect(err).To(BeNil()) + + // What this ClusterSummary deployed locally and is not referenced anymore is removed + Eventually(func() bool { + currentClusterRole := &rbacv1.ClusterRole{} + err = testEnv.Get(context.TODO(), types.NamespacedName{Name: ownClusterRole.Name}, currentClusterRole) + return err != nil && apierrors.IsNotFound(err) + }, timeout, pollingInterval).Should(BeTrue()) + + // Anything not annotated for this ClusterSummary is never touched + Consistently(func() error { + for _, clusterRole := range []*rbacv1.ClusterRole{otherClusterRole, unannotatedClusterRole} { + currentClusterRole := &rbacv1.ClusterRole{} + if err := testEnv.Get(context.TODO(), types.NamespacedName{Name: clusterRole.Name}, + currentClusterRole); err != nil { + + return err + } + } + return nil + }, timeout, pollingInterval).Should(BeNil()) + }) + + It(`undeployStaleResources in the management cluster does not remove resources deployed by a parent Sveltos instance`, func() { + // The management cluster can itself be a managed cluster of a parent Sveltos instance. The parent + // instance deployed a resource in this cluster (so deploymenttype is Remote and clustersummary is + // the identity of this cluster as seen by the parent). The (Cluster)Profile in the parent has the + // same name as the one this ClusterSummary belongs to, so the owner annotations match as well. + // The management cluster pass (isMgmtCluster=true) must not consider that resource stale, even + // though it has the reason label and is not part of currentPolicies. + newClusterRole := func(clusterSummaryValue, deploymentType string) *rbacv1.ClusterRole { + return &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: randomString(), + Labels: map[string]string{ + deployer.ReasonLabel: string(libsveltosv1beta1.FeatureResources), + }, + Annotations: map[string]string{ + deployer.ReferenceKindAnnotation: string(libsveltosv1beta1.ConfigMapReferencedResourceKind), + deployer.ReferenceNamespaceAnnotation: randomString(), + deployer.ReferenceNameAnnotation: randomString(), + deployer.OwnerKind: configv1beta1.ClusterProfileKind, + deployer.OwnerName: clusterProfile.Name, + controllers.ClusterSummaryAnnotation: clusterSummaryValue, + controllers.DeploymentTypeAnnotation: deploymentType, + }, + }, + } + } + + // Deployed by this ClusterSummary via deploymentType: Local + ownClusterRole := newClusterRole(controllers.GetClusterSummaryAnnotationValue(clusterSummary), + string(configv1beta1.DeploymentTypeLocal)) + // Deployed by the parent Sveltos instance + parentClusterRole := newClusterRole(fmt.Sprintf("capi-%s-%s", randomString(), randomString()), + string(configv1beta1.DeploymentTypeRemote)) + + clusterRoles := []*rbacv1.ClusterRole{ownClusterRole, parentClusterRole} + for _, clusterRole := range clusterRoles { + Expect(testEnv.Create(context.TODO(), clusterRole)).To(Succeed()) + Expect(waitForObject(ctx, testEnv.Client, clusterRole)).To(Succeed()) + } + + currentClusterSummary := &configv1beta1.ClusterSummary{} + Expect(testEnv.Get(context.TODO(), + types.NamespacedName{Namespace: clusterSummary.Namespace, Name: clusterSummary.Name}, + currentClusterSummary)).To(Succeed()) + currentClusterSummary.Status.FeatureSummaries = []configv1beta1.FeatureSummary{ + { + FeatureID: libsveltosv1beta1.FeatureResources, + Status: libsveltosv1beta1.FeatureStatusProvisioned, + }, + } + currentClusterSummary.Status.DeployedGVKs = []libsveltosv1beta1.FeatureDeploymentInfo{ + { + FeatureID: libsveltosv1beta1.FeatureResources, + DeployedGroupVersionKind: []string{ + testClusterRoleKindV1, + }, + }, + } + Expect(testEnv.Status().Update(context.TODO(), currentClusterSummary)).To(Succeed()) + + deployedGKVs := controllers.GetDeployedGroupVersionKinds(currentClusterSummary, libsveltosv1beta1.FeatureResources) + Expect(deployedGKVs).ToNot(BeEmpty()) + + // Neither ClusterRole is in currentPolicies (nil): both are candidates for deletion + // were it not for the deploymenttype and clustersummary annotations scoping. + _, err := controllers.UndeployStaleResources(context.TODO(), true, testEnv.Config, testEnv.Client, + libsveltosv1beta1.FeatureResources, currentClusterSummary, deployedGKVs, nil, + textlogger.NewLogger(textlogger.NewConfig())) + Expect(err).To(BeNil()) + + // What this ClusterSummary deployed locally and is not referenced anymore is removed + Eventually(func() bool { + currentClusterRole := &rbacv1.ClusterRole{} + err = testEnv.Get(context.TODO(), types.NamespacedName{Name: ownClusterRole.Name}, currentClusterRole) + return err != nil && apierrors.IsNotFound(err) + }, timeout, pollingInterval).Should(BeTrue()) + + // What the parent Sveltos instance deployed is never touched + Consistently(func() error { + currentClusterRole := &rbacv1.ClusterRole{} + return testEnv.Get(context.TODO(), types.NamespacedName{Name: parentClusterRole.Name}, currentClusterRole) + }, timeout, pollingInterval).Should(BeNil()) + }) + It("addExtraLabels adds extra labels on unstructured", func() { u := &unstructured.Unstructured{} extraLabels := map[string]string{ diff --git a/go.mod b/go.mod index 54f79701..f961ffa0 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.5.0 github.com/TwiN/go-color v1.4.1 github.com/dariubs/percent v1.0.0 - github.com/docker/cli v29.8.2+incompatible + github.com/docker/cli v29.9.0+incompatible github.com/fluxcd/pkg/apis/meta v1.32.0 github.com/fluxcd/pkg/http/fetch v0.26.0 github.com/fluxcd/pkg/tar v1.2.0 @@ -21,7 +21,7 @@ require ( github.com/opencontainers/image-spec v1.1.1 github.com/pkg/errors v0.9.1 github.com/projectsveltos/libsveltos v1.16.1-0.20261007133830-d623a1556939 - github.com/prometheus/client_golang v1.24.1 + github.com/prometheus/client_golang v1.25.0 github.com/sigstore/cosign/v3 v3.1.3 github.com/sigstore/sigstore v1.11.0 github.com/spf13/pflag v1.0.10 @@ -37,8 +37,8 @@ require ( k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 oras.land/oras-go/v2 v2.6.2 - sigs.k8s.io/cluster-api v1.14.2 - sigs.k8s.io/cluster-api/api v1.14.2 + sigs.k8s.io/cluster-api v1.14.3 + sigs.k8s.io/cluster-api/api v1.14.3 sigs.k8s.io/controller-runtime v0.25.2 sigs.k8s.io/kustomize/api v0.21.2 sigs.k8s.io/kustomize/kyaml v0.21.2 @@ -190,7 +190,7 @@ require ( github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.19.2 // indirect + github.com/klauspost/compress v1.20.1 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect @@ -225,9 +225,9 @@ require ( github.com/projectsveltos/lua-utils/glua-runes v0.0.0-20251212200258-2b3cdcb7c0f5 // indirect github.com/projectsveltos/lua-utils/glua-sprig v0.0.0-20251212200258-2b3cdcb7c0f5 // indirect github.com/projectsveltos/lua-utils/glua-strings v0.0.0-20251212200258-2b3cdcb7c0f5 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.70.1 // indirect - github.com/prometheus/procfs v0.21.1 // indirect + github.com/prometheus/client_model v0.6.3 // indirect + github.com/prometheus/common v0.72.0 // indirect + github.com/prometheus/procfs v0.22.0 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 // indirect github.com/rubenv/sql-migrate v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index d0aacd59..00d966cb 100644 --- a/go.sum +++ b/go.sum @@ -248,8 +248,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.8.2+incompatible h1:2zgdFuoFst2T80oS42vhKGxkd0JRo305eIEKTsxR7pQ= -github.com/docker/cli v29.8.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.9.0+incompatible h1:Cw9x50ioSGgGwXJ4eO+hIBry/w3LLbyF8GPAEKz1fnE= +github.com/docker/cli v29.9.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-events v0.0.0-20250808211157-605354379745 h1:yOn6Ze6IbYI/KAw2lw/83ELYvZh6hvsygTVkD0dzMC4= @@ -532,8 +532,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= -github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= -github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ= +github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -653,17 +653,17 @@ github.com/projectsveltos/lua-utils/glua-sprig v0.0.0-20251212200258-2b3cdcb7c0f github.com/projectsveltos/lua-utils/glua-sprig v0.0.0-20251212200258-2b3cdcb7c0f5/go.mod h1:esi+znTJzieo7M60Ytx56vJZbWJ8WuJfVyRvOCOWs64= github.com/projectsveltos/lua-utils/glua-strings v0.0.0-20251212200258-2b3cdcb7c0f5 h1:ifNj1y4pqhSSDL0B5XfCPTnFy9ZjGTzStuOJu1jE9xs= github.com/projectsveltos/lua-utils/glua-strings v0.0.0-20251212200258-2b3cdcb7c0f5/go.mod h1:P/l817Avvelnzyb9YyMmnDYG3OabOyK8KuWF8s35kj0= -github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= -github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= +github.com/prometheus/client_golang v1.25.0 h1:cDtW1TcFsMi1RJc8khwCKB4VhzwV04aKqbYtNOXuiK0= +github.com/prometheus/client_golang v1.25.0/go.mod h1:j2X/4/kaQHLPnmyEU8zjmkndo1T317VpCIQEml8LTKk= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= -github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= +github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= +github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= +github.com/prometheus/common v0.72.0 h1:tAYsE+sPJxIncDAobm4H5aQjmox9ZxEIIqPbiffa8G4= +github.com/prometheus/common v0.72.0/go.mod h1:77NWqAQ2tXT7BIK40qjJdw5Acrsrg1TlHAnsQi3i6mk= github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos= github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= -github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= -github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= +github.com/prometheus/procfs v0.22.0 h1:6q9+/JL9IKAPbCmBrv9n5O5Ty3NKnciV5X7YGw0oics= +github.com/prometheus/procfs v0.22.0/go.mod h1:CvmFr/GVhIjIvWJZW3tgkODBQMRIf0EyWMQLHCHab58= github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3 h1:1/BDligzCa40GTllkDnY3Y5DTHuKCONbB2JcRyIfl20= github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3/go.mod h1:3dZmcLn3Qw6FLlWASn1g4y+YO9ycEFUOM+bhBmzLVKQ= github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 h1:kuvuJL/+MZIEdvtb/kTBRiRgYaOmx1l+lYJyVdrRUOs= @@ -1111,10 +1111,10 @@ oras.land/oras-go/v2 v2.6.2 h1:N04RXngAp1LJKTG6ifz3xHPipasEkWr+hFmInja5YKo= oras.land/oras-go/v2 v2.6.2/go.mod h1:PlTtg4JTDJkDe8yVHpM2wz7/YDc00GVas+i4jAW2TZ4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.36.0 h1:/YpDJ4vReG7ZmzSpBGxduXgywWkJU9zHubgJG03MT+Y= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.36.0/go.mod h1:tJo1aepTXyR+8Xs3sUsGBDk4Ub2AM5dPAPKJx0mpm5c= -sigs.k8s.io/cluster-api v1.14.2 h1:o3GFNaeNFAOEEMpDPfhCK+2CjmV6gtQ8yLEcUwCg7bA= -sigs.k8s.io/cluster-api v1.14.2/go.mod h1:QGMq7dEyUsceFGBLIrnPDVmX2J316yfY20Woq1S9GuU= -sigs.k8s.io/cluster-api/api v1.14.2 h1:B0XWZGFrb4+R1pH2nApdR9d4tcQGIhpEquRxxf36fHg= -sigs.k8s.io/cluster-api/api v1.14.2/go.mod h1:LJ/5hdS9tqTBi9YqyLRnd1t53Sn/cXsa7rEVaY0bBms= +sigs.k8s.io/cluster-api v1.14.3 h1:xc2krMm7SfUV7/sVo3XcHwNz33fkYupy+iSD/W/TA/Q= +sigs.k8s.io/cluster-api v1.14.3/go.mod h1:NB3GmqTA/hlMaGDv01gt36/5k0DJ+ixGTJFHUe5Ib3Q= +sigs.k8s.io/cluster-api/api v1.14.3 h1:lTjHLULtlJHRXvroECdeiM2fXZOsvu93y+LYRjm/5II= +sigs.k8s.io/cluster-api/api v1.14.3/go.mod h1:LJ/5hdS9tqTBi9YqyLRnd1t53Sn/cXsa7rEVaY0bBms= sigs.k8s.io/controller-runtime v0.25.2 h1:bEkK3PVOIVK9X8QWLGVhJgmFc++47vfT6wakSzAOLsQ= sigs.k8s.io/controller-runtime v0.25.2/go.mod h1:4QqLdT6z/L6Olj8JJCtvztid4/fnIiYsfaTFScegctc= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=