4
4
"context"
5
5
"errors"
6
6
"fmt"
7
+ "strings"
8
+
7
9
appsv1 "k8s.io/api/apps/v1"
8
10
corev1 "k8s.io/api/core/v1"
9
11
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -14,7 +16,6 @@ import (
14
16
corev1listers "k8s.io/client-go/listers/core/v1"
15
17
"k8s.io/client-go/tools/cache"
16
18
"k8s.io/client-go/util/workqueue"
17
- "strings"
18
19
19
20
operatorv1 "github.com/openshift/api/operator/v1"
20
21
openshiftconfigclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
@@ -41,6 +42,12 @@ type Delegate interface {
41
42
// operator will be degraded, not available and not progressing
42
43
// returned errors (if any) will be added to the Message field
43
44
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 )
44
51
}
45
52
46
53
// 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
133
140
return c .updateOperatorStatus (ctx , operatorStatus , nil , false , false , nil )
134
141
}
135
142
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
+
136
157
workload , operatorConfigAtHighestGeneration , errs := c .delegate .Sync (ctx , controllerContext )
137
158
138
159
return c .updateOperatorStatus (ctx , operatorStatus , workload , operatorConfigAtHighestGeneration , true , errs )
@@ -341,11 +362,7 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
341
362
// which should immediately result in a deployment generation diff, which should cause this block to be skipped until it is ready.
342
363
workloadHasAllPodsUpdated := workload .Status .UpdatedReplicas == desiredReplicas
343
364
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 )
349
366
}
350
367
351
368
if len (errs ) > 0 {
@@ -354,6 +371,14 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
354
371
return nil
355
372
}
356
373
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
+
357
382
// hasDeploymentProgressed returns true if the deployment reports NewReplicaSetAvailable
358
383
// via the DeploymentProgressing condition
359
384
func hasDeploymentProgressed (status appsv1.DeploymentStatus ) bool {
0 commit comments