Skip to content

Commit 4c5ecb3

Browse files
Merge pull request #1830 from bertinatto/try-apply-workload
API-1835: Migrate WorkloadController to SSA
2 parents c31b2ce + ed9ab11 commit 4c5ecb3

File tree

2 files changed

+108
-83
lines changed

2 files changed

+108
-83
lines changed

pkg/operator/apiserver/controller/workload/workload.go

Lines changed: 100 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import (
2020

2121
operatorv1 "github.com/openshift/api/operator/v1"
2222
openshiftconfigclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
23+
applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1"
2324
"github.com/openshift/library-go/pkg/apps/deployment"
2425
"github.com/openshift/library-go/pkg/controller/factory"
2526
"github.com/openshift/library-go/pkg/operator/events"
26-
"github.com/openshift/library-go/pkg/operator/resource/resourcemerge"
2727
"github.com/openshift/library-go/pkg/operator/status"
2828
"github.com/openshift/library-go/pkg/operator/v1helpers"
2929
)
@@ -49,6 +49,7 @@ type Delegate interface {
4949
// Callers must provide a sync function for delegation. It should bring the desired workload into operation.
5050
// The returned state along with errors will be converted into conditions and persisted in the status field.
5151
type Controller struct {
52+
controllerInstanceName string
5253
// conditionsPrefix an optional prefix that will be used as operator's condition type field for example APIServerDeploymentDegraded where APIServer indicates the prefix
5354
conditionsPrefix string
5455
operatorNamespace string
@@ -71,13 +72,13 @@ type Controller struct {
7172

7273
// NewController creates a brand new Controller instance.
7374
//
74-
// the "name" param will be used to set conditions in the status field. It will be suffixed with "WorkloadController",
75+
// the "instanceName" param will be used to set conditions in the status field. It will be suffixed with "WorkloadController",
7576
// so it can end up in the condition in the form of "OAuthAPIWorkloadControllerDeploymentAvailable"
7677
//
7778
// the "operatorNamespace" is used to set "version-mapping" in the correct namespace
7879
//
7980
// the "targetNamespace" represent the namespace for the managed resource (DaemonSet)
80-
func NewController(name, operatorNamespace, targetNamespace, targetOperandVersion, operandNamePrefix, conditionsPrefix string,
81+
func NewController(instanceName, operatorNamespace, targetNamespace, targetOperandVersion, operandNamePrefix, conditionsPrefix string,
8182
operatorClient v1helpers.OperatorClient,
8283
kubeClient kubernetes.Interface,
8384
podLister corev1listers.PodLister,
@@ -89,6 +90,7 @@ func NewController(name, operatorNamespace, targetNamespace, targetOperandVersio
8990
versionRecorder status.VersionGetter,
9091
) factory.Controller {
9192
controllerRef := &Controller{
93+
controllerInstanceName: factory.ControllerInstanceName(instanceName, "Workload"),
9294
operatorNamespace: operatorNamespace,
9395
targetNamespace: targetNamespace,
9496
targetOperandVersion: targetOperandVersion,
@@ -100,7 +102,7 @@ func NewController(name, operatorNamespace, targetNamespace, targetOperandVersio
100102
delegate: delegate,
101103
openshiftClusterConfigClient: openshiftClusterConfigClient,
102104
versionRecorder: versionRecorder,
103-
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), name),
105+
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), instanceName),
104106
}
105107

106108
c := factory.New()
@@ -111,7 +113,7 @@ func NewController(name, operatorNamespace, targetNamespace, targetOperandVersio
111113
return c.WithSync(controllerRef.sync).
112114
WithInformers(informers...).
113115
ToController(
114-
fmt.Sprintf("%sWorkloadController", name), // don't change what is passed here unless you also remove the old FooDegraded condition
116+
fmt.Sprintf("%sWorkloadController", controllerRef.controllerInstanceName),
115117
eventRecorder,
116118
)
117119
}
@@ -161,40 +163,40 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
161163
errs = []error{}
162164
}
163165

164-
deploymentAvailableCondition := operatorv1.OperatorCondition{
165-
Type: fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeAvailable),
166-
Status: operatorv1.ConditionTrue,
167-
}
166+
deploymentAvailableCondition := applyoperatorv1.OperatorCondition().
167+
WithType(fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeAvailable))
168168

169-
workloadDegradedCondition := operatorv1.OperatorCondition{
170-
Type: fmt.Sprintf("%sWorkloadDegraded", c.conditionsPrefix),
171-
Status: operatorv1.ConditionFalse,
172-
}
169+
workloadDegradedCondition := applyoperatorv1.OperatorCondition().
170+
WithType(fmt.Sprintf("%sWorkloadDegraded", c.conditionsPrefix))
173171

174-
deploymentDegradedCondition := operatorv1.OperatorCondition{
175-
Type: fmt.Sprintf("%sDeploymentDegraded", c.conditionsPrefix),
176-
Status: operatorv1.ConditionFalse,
177-
}
172+
deploymentDegradedCondition := applyoperatorv1.OperatorCondition().
173+
WithType(fmt.Sprintf("%sDeploymentDegraded", c.conditionsPrefix))
178174

179-
deploymentProgressingCondition := operatorv1.OperatorCondition{
180-
Type: fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeProgressing),
181-
Status: operatorv1.ConditionFalse,
175+
deploymentProgressingCondition := applyoperatorv1.OperatorCondition().
176+
WithType(fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeProgressing))
177+
178+
status := applyoperatorv1.OperatorStatus()
179+
if workload != nil {
180+
// The Hash field is not required since the LastGeneration field is enough to uniquely identify a Deployment's desired state
181+
status = status.WithGenerations(applyoperatorv1.GenerationStatus().
182+
WithGroup("apps").
183+
WithResource("deployments").
184+
WithNamespace(workload.Namespace).
185+
WithName(workload.Name).
186+
WithLastGeneration(workload.Generation),
187+
)
182188
}
183189

184-
// only set updateGenerationFn to update the observed generation if everything is available
185-
var updateGenerationFn func(newStatus *operatorv1.OperatorStatus) error
186190
defer func() {
187-
updates := []v1helpers.UpdateStatusFunc{
188-
v1helpers.UpdateConditionFn(deploymentAvailableCondition),
189-
v1helpers.UpdateConditionFn(deploymentDegradedCondition),
190-
v1helpers.UpdateConditionFn(deploymentProgressingCondition),
191-
v1helpers.UpdateConditionFn(workloadDegradedCondition),
192-
}
193-
if updateGenerationFn != nil {
194-
updates = append(updates, updateGenerationFn)
195-
}
196-
if _, _, updateError := v1helpers.UpdateStatus(ctx, c.operatorClient, updates...); updateError != nil {
197-
err = updateError
191+
status = status.WithConditions(
192+
deploymentAvailableCondition,
193+
deploymentDegradedCondition,
194+
deploymentProgressingCondition,
195+
workloadDegradedCondition,
196+
)
197+
198+
if applyError := c.operatorClient.ApplyOperatorStatus(ctx, c.controllerInstanceName, status); applyError != nil {
199+
err = applyError
198200
}
199201
}()
200202

@@ -209,15 +211,23 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
209211

210212
// we are degraded, not available and we are not progressing
211213

212-
deploymentDegradedCondition.Status = operatorv1.ConditionTrue
213-
deploymentDegradedCondition.Reason = "PreconditionNotFulfilled"
214-
deploymentDegradedCondition.Message = message
214+
deploymentDegradedCondition = deploymentDegradedCondition.
215+
WithStatus(operatorv1.ConditionTrue).
216+
WithReason("PreconditionNotFulfilled").
217+
WithMessage(message)
215218

216-
deploymentAvailableCondition.Status = operatorv1.ConditionFalse
217-
deploymentAvailableCondition.Reason = "PreconditionNotFulfilled"
219+
deploymentAvailableCondition = deploymentAvailableCondition.
220+
WithStatus(operatorv1.ConditionFalse).
221+
WithReason("PreconditionNotFulfilled")
218222

219-
deploymentProgressingCondition.Status = operatorv1.ConditionFalse
220-
deploymentProgressingCondition.Reason = "PreconditionNotFulfilled"
223+
deploymentProgressingCondition = deploymentProgressingCondition.
224+
WithStatus(operatorv1.ConditionFalse).
225+
WithReason("PreconditionNotFulfilled")
226+
227+
workloadDegradedCondition = workloadDegradedCondition.
228+
WithStatus(operatorv1.ConditionTrue).
229+
WithReason("PreconditionNotFulfilled").
230+
WithMessage(message)
221231

222232
return kerrors.NewAggregate(errs)
223233
}
@@ -227,37 +237,49 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
227237
for _, err := range errs {
228238
message = message + err.Error() + "\n"
229239
}
230-
workloadDegradedCondition.Status = operatorv1.ConditionTrue
231-
workloadDegradedCondition.Reason = "SyncError"
232-
workloadDegradedCondition.Message = message
240+
workloadDegradedCondition = workloadDegradedCondition.
241+
WithStatus(operatorv1.ConditionTrue).
242+
WithReason("SyncError").
243+
WithMessage(message)
244+
} else if workload == nil {
245+
workloadDegradedCondition = workloadDegradedCondition.
246+
WithStatus(operatorv1.ConditionTrue).
247+
WithReason("NoDeployment").
248+
WithMessage(fmt.Sprintf("deployment/%s: could not be retrieved", c.targetNamespace))
233249
} else {
234-
workloadDegradedCondition.Status = operatorv1.ConditionFalse
250+
workloadDegradedCondition = workloadDegradedCondition.
251+
WithStatus(operatorv1.ConditionFalse)
235252
}
236253

237254
if workload == nil {
238255
message := fmt.Sprintf("deployment/%s: could not be retrieved", c.targetNamespace)
239-
deploymentAvailableCondition.Status = operatorv1.ConditionFalse
240-
deploymentAvailableCondition.Reason = "NoDeployment"
241-
deploymentAvailableCondition.Message = message
256+
deploymentAvailableCondition = deploymentAvailableCondition.
257+
WithStatus(operatorv1.ConditionFalse).
258+
WithReason("NoDeployment").
259+
WithMessage(message)
242260

243-
deploymentProgressingCondition.Status = operatorv1.ConditionTrue
244-
deploymentProgressingCondition.Reason = "NoDeployment"
245-
deploymentProgressingCondition.Message = message
261+
deploymentProgressingCondition = deploymentProgressingCondition.
262+
WithStatus(operatorv1.ConditionTrue).
263+
WithReason("NoDeployment").
264+
WithMessage(message)
246265

247-
deploymentDegradedCondition.Status = operatorv1.ConditionTrue
248-
deploymentDegradedCondition.Reason = "NoDeployment"
249-
deploymentDegradedCondition.Message = message
266+
deploymentDegradedCondition = deploymentDegradedCondition.
267+
WithStatus(operatorv1.ConditionTrue).
268+
WithReason("NoDeployment").
269+
WithMessage(message)
250270

251271
return kerrors.NewAggregate(errs)
252272
}
253273

254274
if workload.Status.AvailableReplicas == 0 {
255-
deploymentAvailableCondition.Status = operatorv1.ConditionFalse
256-
deploymentAvailableCondition.Reason = "NoPod"
257-
deploymentAvailableCondition.Message = fmt.Sprintf("no %s.%s pods available on any node.", workload.Name, c.targetNamespace)
275+
deploymentAvailableCondition = deploymentAvailableCondition.
276+
WithStatus(operatorv1.ConditionFalse).
277+
WithReason("NoPod").
278+
WithMessage(fmt.Sprintf("no %s.%s pods available on any node.", workload.Name, c.targetNamespace))
258279
} else {
259-
deploymentAvailableCondition.Status = operatorv1.ConditionTrue
260-
deploymentAvailableCondition.Reason = "AsExpected"
280+
deploymentAvailableCondition = deploymentAvailableCondition.
281+
WithStatus(operatorv1.ConditionTrue).
282+
WithReason("AsExpected")
261283
}
262284

263285
desiredReplicas := int32(1)
@@ -268,18 +290,21 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
268290
// If the workload is up to date, then we are no longer progressing
269291
workloadAtHighestGeneration := workload.ObjectMeta.Generation == workload.Status.ObservedGeneration
270292
workloadIsBeingUpdated := workload.Status.UpdatedReplicas < desiredReplicas
271-
workloadIsBeingUpdatedTooLong, err := isUpdatingTooLong(previousStatus, deploymentProgressingCondition.Type)
293+
workloadIsBeingUpdatedTooLong, err := isUpdatingTooLong(previousStatus, *deploymentProgressingCondition.Type)
272294
if !workloadAtHighestGeneration {
273-
deploymentProgressingCondition.Status = operatorv1.ConditionTrue
274-
deploymentProgressingCondition.Reason = "NewGeneration"
275-
deploymentProgressingCondition.Message = fmt.Sprintf("deployment/%s.%s: observed generation is %d, desired generation is %d.", workload.Name, c.targetNamespace, workload.Status.ObservedGeneration, workload.ObjectMeta.Generation)
295+
deploymentProgressingCondition = deploymentProgressingCondition.
296+
WithStatus(operatorv1.ConditionTrue).
297+
WithReason("NewGeneration").
298+
WithMessage(fmt.Sprintf("deployment/%s.%s: observed generation is %d, desired generation is %d.", workload.Name, c.targetNamespace, workload.Status.ObservedGeneration, workload.ObjectMeta.Generation))
276299
} else if workloadIsBeingUpdated {
277-
deploymentProgressingCondition.Status = operatorv1.ConditionTrue
278-
deploymentProgressingCondition.Reason = "PodsUpdating"
279-
deploymentProgressingCondition.Message = fmt.Sprintf("deployment/%s.%s: %d/%d pods have been updated to the latest generation", workload.Name, c.targetNamespace, workload.Status.UpdatedReplicas, desiredReplicas)
300+
deploymentProgressingCondition = deploymentProgressingCondition.
301+
WithStatus(operatorv1.ConditionTrue).
302+
WithReason("PodsUpdating").
303+
WithMessage(fmt.Sprintf("deployment/%s.%s: %d/%d pods have been updated to the latest generation", workload.Name, c.targetNamespace, workload.Status.UpdatedReplicas, desiredReplicas))
280304
} else {
281-
deploymentProgressingCondition.Status = operatorv1.ConditionFalse
282-
deploymentProgressingCondition.Reason = "AsExpected"
305+
deploymentProgressingCondition = deploymentProgressingCondition.
306+
WithStatus(operatorv1.ConditionFalse).
307+
WithReason("AsExpected")
283308
}
284309

285310
// During a rollout the default maxSurge (25%) will allow the available
@@ -288,17 +313,19 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
288313
workloadHasAllPodsAvailable := workload.Status.AvailableReplicas >= desiredReplicas
289314
if !workloadHasAllPodsAvailable && (!workloadIsBeingUpdated || workloadIsBeingUpdatedTooLong) {
290315
numNonAvailablePods := desiredReplicas - workload.Status.AvailableReplicas
291-
deploymentDegradedCondition.Status = operatorv1.ConditionTrue
292-
deploymentDegradedCondition.Reason = "UnavailablePod"
316+
deploymentDegradedCondition = deploymentDegradedCondition.
317+
WithStatus(operatorv1.ConditionTrue).
318+
WithReason("UnavailablePod")
293319
podContainersStatus, err := deployment.PodContainersStatus(workload, c.podsLister)
294320
if err != nil {
295321
podContainersStatus = []string{fmt.Sprintf("failed to get pod containers details: %v", err)}
296322
}
297-
deploymentDegradedCondition.Message = fmt.Sprintf("%v of %v requested instances are unavailable for %s.%s (%s)", numNonAvailablePods, desiredReplicas, workload.Name, c.targetNamespace,
298-
strings.Join(podContainersStatus, ", "))
323+
deploymentDegradedCondition = deploymentDegradedCondition.
324+
WithMessage(fmt.Sprintf("%v of %v requested instances are unavailable for %s.%s (%s)", numNonAvailablePods, desiredReplicas, workload.Name, c.targetNamespace, strings.Join(podContainersStatus, ", ")))
299325
} else {
300-
deploymentDegradedCondition.Status = operatorv1.ConditionFalse
301-
deploymentDegradedCondition.Reason = "AsExpected"
326+
deploymentDegradedCondition = deploymentDegradedCondition.
327+
WithStatus(operatorv1.ConditionFalse).
328+
WithReason("AsExpected")
302329
}
303330

304331
// if the deployment is all available and at the expected generation, then update the version to the latest
@@ -313,12 +340,6 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o
313340
c.versionRecorder.SetVersion(operandName, c.targetOperandVersion)
314341
}
315342

316-
// set updateGenerationFn so that it is invoked in defer
317-
updateGenerationFn = func(newStatus *operatorv1.OperatorStatus) error {
318-
resourcemerge.SetDeploymentGeneration(&newStatus.Generations, workload)
319-
return nil
320-
}
321-
322343
if len(errs) > 0 {
323344
return kerrors.NewAggregate(errs)
324345
}

pkg/operator/apiserver/controller/workload/workload_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ func TestUpdateOperatorStatus(t *testing.T) {
7171
Message: "deployment/: could not be retrieved",
7272
},
7373
{
74-
Type: fmt.Sprintf("%sWorkloadDegraded", defaultControllerName),
75-
Status: operatorv1.ConditionFalse,
74+
Type: fmt.Sprintf("%sWorkloadDegraded", defaultControllerName),
75+
Status: operatorv1.ConditionTrue,
76+
Reason: "NoDeployment",
77+
Message: "deployment/: could not be retrieved",
7678
},
7779
{
7880
Type: fmt.Sprintf("%sDeploymentDegraded", defaultControllerName),
@@ -441,8 +443,10 @@ func TestUpdateOperatorStatus(t *testing.T) {
441443
Message: "",
442444
},
443445
{
444-
Type: fmt.Sprintf("%sWorkloadDegraded", defaultControllerName),
445-
Status: operatorv1.ConditionFalse,
446+
Type: fmt.Sprintf("%sWorkloadDegraded", defaultControllerName),
447+
Status: operatorv1.ConditionTrue,
448+
Reason: "PreconditionNotFulfilled",
449+
Message: "the operator didn't specify what preconditions are missing",
446450
},
447451
}
448452
return areCondidtionsEqual(expectedConditions, actualStatus.Conditions)

0 commit comments

Comments
 (0)