44 "context"
55 "errors"
66 "fmt"
7+ "strings"
8+
79 appsv1 "k8s.io/api/apps/v1"
810 corev1 "k8s.io/api/core/v1"
911 apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -14,7 +16,6 @@ import (
1416 corev1listers "k8s.io/client-go/listers/core/v1"
1517 "k8s.io/client-go/tools/cache"
1618 "k8s.io/client-go/util/workqueue"
17- "strings"
1819
1920 operatorv1 "github.com/openshift/api/operator/v1"
2021 openshiftconfigclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
@@ -41,6 +42,12 @@ type Delegate interface {
4142 // operator will be degraded, not available and not progressing
4243 // returned errors (if any) will be added to the Message field
4344 PreconditionFulfilled (ctx context.Context ) (bool , error )
45+
46+ // WorkloadDeleted indicates whether the delegate workload has been deleted or not. It returns a bool
47+ // flag to indicate this, a string representing the workload's name and an error. When true, this
48+ // controller will remove any fields from the operator status that the controller is managing, and
49+ // will also remove the version field.
50+ WorkloadDeleted (ctx context.Context ) (bool , string , error )
4451}
4552
4653// Controller is a generic workload controller that deals with Deployment resource.
@@ -133,6 +140,20 @@ func (c *Controller) sync(ctx context.Context, controllerContext factory.SyncCon
133140 return c .updateOperatorStatus (ctx , operatorStatus , nil , false , false , nil )
134141 }
135142
143+ if deleted , operandName , err := c .delegate .WorkloadDeleted (ctx ); err != nil {
144+ return c .updateOperatorStatus (ctx , operatorStatus , nil , false , false , []error {err })
145+ } else if deleted {
146+ // Server-Side-Apply with an empty operator status for the specific field manager will effectively
147+ // remove any conditions and generations owned by it, because the respective API fields have 'map'
148+ // as the list type where field managers can be list element-specific
149+ if err := c .operatorClient .ApplyOperatorStatus (ctx , c .controllerInstanceName , applyoperatorv1 .OperatorStatus ()); err != nil {
150+ return err
151+ }
152+
153+ c .versionRecorder .UnsetVersion (c .constructOperandNameFor (operandName ))
154+ return nil
155+ }
156+
136157 workload , operatorConfigAtHighestGeneration , errs := c .delegate .Sync (ctx , controllerContext )
137158
138159 return c .updateOperatorStatus (ctx , operatorStatus , workload , operatorConfigAtHighestGeneration , true , errs )
@@ -341,11 +362,7 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
341362 // which should immediately result in a deployment generation diff, which should cause this block to be skipped until it is ready.
342363 workloadHasAllPodsUpdated := workload .Status .UpdatedReplicas == desiredReplicas
343364 if workloadAtHighestGeneration && workloadHasAllPodsAvailable && workloadHasAllPodsUpdated && operatorConfigAtHighestGeneration {
344- operandName := workload .Name
345- if len (c .operandNamePrefix ) > 0 {
346- operandName = fmt .Sprintf ("%s-%s" , c .operandNamePrefix , workload .Name )
347- }
348- c .versionRecorder .SetVersion (operandName , c .targetOperandVersion )
365+ c .versionRecorder .SetVersion (c .constructOperandNameFor (workload .Name ), c .targetOperandVersion )
349366 }
350367
351368 if len (errs ) > 0 {
@@ -354,6 +371,14 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
354371 return nil
355372}
356373
374+ func (c * Controller ) constructOperandNameFor (name string ) string {
375+ if len (c .operandNamePrefix ) > 0 {
376+ return fmt .Sprintf ("%s-%s" , c .operandNamePrefix , name )
377+ }
378+
379+ return name
380+ }
381+
357382// hasDeploymentProgressed returns true if the deployment reports NewReplicaSetAvailable
358383// via the DeploymentProgressing condition
359384func hasDeploymentProgressed (status appsv1.DeploymentStatus ) bool {
0 commit comments