From c566c5510ad8208598d896a02cdaba90d9e3fc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radek=20Ma=C5=88=C3=A1k?= Date: Tue, 16 Jun 2026 15:05:57 +0200 Subject: [PATCH] Treat missing cloud config sources as transient. Keep brief source ConfigMap outages on the retry path so CCCMO does not immediately degrade during recovery, while preserving terminal behavior for real bad-key misconfigurations. --- .../cloud_config_sync_controller.go | 2 ++ .../cloud_config_sync_controller_test.go | 30 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pkg/controllers/cloud_config_sync_controller.go b/pkg/controllers/cloud_config_sync_controller.go index 4ad41e972..df1c9dd36 100644 --- a/pkg/controllers/cloud_config_sync_controller.go +++ b/pkg/controllers/cloud_config_sync_controller.go @@ -187,6 +187,8 @@ func (r *CloudConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) klog.Infof("Initializing minimal config for platform %s", platformType) minimalConfig := getMinimalConfigForPlatform(platformType) sourceCM.Data = map[string]string{defaultConfigKey: minimalConfig} + } else { + return ctrl.Result{}, fmt.Errorf("cloud-config source configmap %s/%s not found", openshiftUnmanagedCMKey.Namespace, openshiftUnmanagedCMKey.Name) } } else if err != nil { klog.Errorf("unable to get cloud-config for sync: %v", err) diff --git a/pkg/controllers/cloud_config_sync_controller_test.go b/pkg/controllers/cloud_config_sync_controller_test.go index a9e370ef9..8d55138ae 100644 --- a/pkg/controllers/cloud_config_sync_controller_test.go +++ b/pkg/controllers/cloud_config_sync_controller_test.go @@ -548,6 +548,31 @@ var _ = Describe("Cloud config sync reconciler", func() { Expect(degradedCond.Status).To(Equal(configv1.ConditionTrue)) }) + It("should treat a missing source configmap as transient until it reappears", func() { + Expect(cl.Delete(ctx, makeInfraCloudConfig(configv1.AWSPlatformType))).To(Succeed()) + + infraResource := makeInfrastructureResource(configv1.AWSPlatformType) + Expect(cl.Create(ctx, infraResource)).To(Succeed()) + + infraResource.Status = makeInfraStatus(infraResource.Spec.PlatformSpec.Type) + Expect(cl.Status().Update(ctx, infraResource.DeepCopy())).To(Succeed()) + + _, err := reconciler.Reconcile(context.TODO(), ctrl.Request{}) + Expect(err).To(HaveOccurred()) + + co := &configv1.ClusterOperator{} + Expect(cl.Get(ctx, client.ObjectKey{Name: clusterOperatorName}, co)).To(MatchError(apierrors.IsNotFound, "IsNotFound")) + + Expect(cl.Create(ctx, makeInfraCloudConfig(configv1.AWSPlatformType))).To(Succeed()) + + _, err = reconciler.Reconcile(context.TODO(), ctrl.Request{}) + Expect(err).NotTo(HaveOccurred()) + + Expect(cl.Get(ctx, client.ObjectKey{Name: clusterOperatorName}, co)).To(Succeed()) + Expect(v1helpers.IsStatusConditionTrue(co.Status.Conditions, cloudConfigControllerDegradedCondition)).To(BeFalse()) + Expect(v1helpers.IsStatusConditionTrue(co.Status.Conditions, cloudConfigControllerAvailableCondition)).To(BeTrue()) + }) + It("should continue with reconcile when feature gates are available", func() { reconciler.FeatureGateAccess = featuregates.NewHardcodedFeatureGateAccessForTesting( []configv1.FeatureGateName{"CloudControllerManagerWebhook", "ChocobombVanilla", "ChocobombStrawberry"}, @@ -604,9 +629,8 @@ var _ = Describe("Cloud config sync reconciler", func() { Expect(cl.Status().Update(ctx, infraResource.DeepCopy())).To(Succeed()) _, err := reconciler.Reconcile(context.TODO(), ctrl.Request{}) Expect(err).To(BeNil()) - allCMs := &corev1.ConfigMapList{} - Expect(cl.List(ctx, allCMs, &client.ListOptions{Namespace: targetNamespaceName})).To(Succeed()) - Expect(len(allCMs.Items)).To(BeZero()) + Expect(cl.Get(ctx, client.ObjectKey{Namespace: targetNamespaceName, Name: syncedCloudConfigMapName}, &corev1.ConfigMap{})). + To(MatchError(apierrors.IsNotFound, "IsNotFound")) }) })