diff --git a/pkg/operator/resourcesynccontroller/resourcesync_controller.go b/pkg/operator/resourcesynccontroller/resourcesync_controller.go index cbfc2dd5f4..79bf651159 100644 --- a/pkg/operator/resourcesynccontroller/resourcesync_controller.go +++ b/pkg/operator/resourcesynccontroller/resourcesync_controller.go @@ -45,6 +45,8 @@ type ResourceSyncController struct { kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces operatorConfigClient v1helpers.OperatorClient + degradedConditionType string + runFn func(ctx context.Context, workers int) syncCtx factory.SyncContext } @@ -69,6 +71,7 @@ func NewResourceSyncController( secretSyncRules: syncRules{}, kubeInformersForNamespaces: kubeInformersForNamespaces, knownNamespaces: kubeInformersForNamespaces.Namespaces(), + degradedConditionType: condition.ResourceSyncControllerDegradedConditionType, configMapGetter: v1helpers.CachedConfigMapGetter(configMapsGetter, kubeInformersForNamespaces), secretGetter: v1helpers.CachedSecretGetter(secretsGetter, kubeInformersForNamespaces), @@ -109,6 +112,14 @@ func (c *ResourceSyncController) Name() string { return c.controllerInstanceName } +// WithConditionPrefix sets a prefix for the controller's condition types. +// This is useful when multiple operators reuse the same controller but would like to have +// distinct conditions. +func (c *ResourceSyncController) WithConditionPrefix(prefix string) *ResourceSyncController { + c.degradedConditionType = fmt.Sprintf("%s%s", prefix, c.degradedConditionType) + return c +} + func (c *ResourceSyncController) SyncConfigMap(destination, source ResourceLocation) error { return c.syncConfigMap(destination, source, alwaysFulfilledPreconditions) } @@ -262,7 +273,7 @@ func (c *ResourceSyncController) Sync(ctx context.Context, syncCtx factory.SyncC if len(errors) > 0 { condition := applyoperatorv1.OperatorStatus(). WithConditions(applyoperatorv1.OperatorCondition(). - WithType(condition.ResourceSyncControllerDegradedConditionType). + WithType(c.degradedConditionType). WithStatus(operatorv1.ConditionTrue). WithReason("Error"). WithMessage(v1helpers.NewMultiLineAggregate(errors).Error())) @@ -275,7 +286,7 @@ func (c *ResourceSyncController) Sync(ctx context.Context, syncCtx factory.SyncC condition := applyoperatorv1.OperatorStatus(). WithConditions(applyoperatorv1.OperatorCondition(). - WithType(condition.ResourceSyncControllerDegradedConditionType). + WithType(c.degradedConditionType). WithStatus(operatorv1.ConditionFalse)) updateErr := c.operatorConfigClient.ApplyOperatorStatus(ctx, c.controllerInstanceName, condition) if updateErr != nil { diff --git a/pkg/operator/resourcesynccontroller/resourcesync_controller_test.go b/pkg/operator/resourcesynccontroller/resourcesync_controller_test.go index 925faf56dc..0ceef4d34c 100644 --- a/pkg/operator/resourcesynccontroller/resourcesync_controller_test.go +++ b/pkg/operator/resourcesynccontroller/resourcesync_controller_test.go @@ -2,29 +2,28 @@ package resourcesynccontroller import ( "context" - clocktesting "k8s.io/utils/clock/testing" + "fmt" "net/http" "net/http/httptest" "sync" "testing" "time" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - ktesting "k8s.io/client-go/testing" - - "github.com/openshift/library-go/pkg/operator/events/eventstesting" - "github.com/openshift/library-go/pkg/operator/v1helpers" - corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" + ktesting "k8s.io/client-go/testing" + clocktesting "k8s.io/utils/clock/testing" operatorv1 "github.com/openshift/api/operator/v1" - + "github.com/openshift/library-go/pkg/operator/condition" "github.com/openshift/library-go/pkg/operator/events" + "github.com/openshift/library-go/pkg/operator/events/eventstesting" + "github.com/openshift/library-go/pkg/operator/v1helpers" ) func TestSyncSecret(t *testing.T) { @@ -368,3 +367,75 @@ func TestServeHTTP(t *testing.T) { t.Errorf("Expected:%+v\n Got: %+v\n", expected, response) } } + +func TestWithConditionPrefix(t *testing.T) { + tests := []struct { + name string + customConditionPrefix string + expectedCondition string + }{ + { + name: "Without ConditionPrefix", + expectedCondition: condition.ResourceSyncControllerDegradedConditionType, + }, + { + name: "WithConditionPrefix", + customConditionPrefix: "RecoveryPod", + expectedCondition: fmt.Sprintf("RecoveryPod%s", condition.ResourceSyncControllerDegradedConditionType), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + informerFactory := informers.NewSharedInformerFactoryWithOptions(nil, 1*time.Minute, informers.WithNamespace("foo")) + fakeStaticPodOperatorClient := v1helpers.NewFakeOperatorClient( + &operatorv1.OperatorSpec{ + ManagementState: operatorv1.Managed, + }, + &operatorv1.OperatorStatus{}, + nil, + ) + eventRecorder := eventstesting.NewTestingEventRecorder(t) + + c := NewResourceSyncController( + "testing-instance", + fakeStaticPodOperatorClient, + v1helpers.NewFakeKubeInformersForNamespaces(map[string]informers.SharedInformerFactory{ + "foo": informerFactory, + }), + nil, + nil, + eventRecorder, + ) + if len(test.customConditionPrefix) > 0 { + c.WithConditionPrefix(test.customConditionPrefix) + } + + if err := c.Sync(context.TODO(), c.syncCtx); err != nil { + t.Fatal(err) + } + _, status, _, err := fakeStaticPodOperatorClient.GetOperatorState() + if err != nil { + t.Fatal(err) + } + if !v1helpers.IsOperatorConditionFalse(status.Conditions, test.expectedCondition) { + t.Errorf("expected resource sync condition: %q to NOT be degraded", test.expectedCondition) + } + + if err = c.SyncSecretConditionally(ResourceLocation{Namespace: "foo", Name: "secret"}, ResourceLocation{Namespace: "foo", Name: "secret2"}, func() (bool, error) { + return false, fmt.Errorf("nasty err") + }); err != nil { + t.Fatal(err) + } + if err = c.Sync(context.TODO(), c.syncCtx); err != nil { + t.Fatal(err) + } + _, status, _, err = fakeStaticPodOperatorClient.GetOperatorState() + if err != nil { + t.Fatal(err) + } + if !v1helpers.IsOperatorConditionTrue(status.Conditions, test.expectedCondition) { + t.Errorf("expected resource sync condition: %q to be degraded", test.expectedCondition) + } + }) + } +}