Skip to content

Commit 3a9d623

Browse files
committed
Properly set Available and Progressing conditions
The Pod Identity Webhook controller will now report Available: False when there are no pods available on the deployment, and Progressing: True when not all pods match the latest deployment spec (motivating factor: when the image on the deployment spec has been updated)
1 parent af37b11 commit 3a9d623

8 files changed

Lines changed: 156 additions & 76 deletions

File tree

pkg/operator/cleanup/status.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cleanup
22

33
import (
4+
"context"
5+
46
log "github.com/sirupsen/logrus"
57

68
configv1 "github.com/openshift/api/config/v1"
@@ -9,7 +11,7 @@ import (
911

1012
var _ status.Handler = &ReconcileStaleCredentialsRequest{}
1113

12-
func (r *ReconcileStaleCredentialsRequest) GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
14+
func (r *ReconcileStaleCredentialsRequest) GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
1315
return []configv1.ClusterOperatorStatusCondition{}, nil
1416
}
1517

pkg/operator/credentialsrequest/credentialsrequest_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,9 +493,9 @@ type ReconcileSecretMissingLabel struct {
493493
mutatingClient corev1client.SecretsGetter
494494
}
495495

496-
func (r *ReconcileSecretMissingLabel) GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
496+
func (r *ReconcileSecretMissingLabel) GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
497497
var secrets corev1.SecretList
498-
if err := r.cachedClient.List(context.TODO(), &secrets); err != nil {
498+
if err := r.cachedClient.List(ctx, &secrets); err != nil {
499499
return nil, err
500500
}
501501
var missing int

pkg/operator/credentialsrequest/status.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ const (
2929

3030
var _ status.Handler = &ReconcileCredentialsRequest{}
3131

32-
func (r *ReconcileCredentialsRequest) GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
33-
_, credRequests, mode, err := r.getOperatorState(logger)
32+
func (r *ReconcileCredentialsRequest) GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
33+
_, credRequests, mode, err := r.getOperatorState(ctx, logger)
3434
if err != nil {
3535
return []configv1.ClusterOperatorStatusCondition{}, fmt.Errorf("failed to get operator state: %v", err)
3636
}
@@ -43,7 +43,7 @@ func (r *ReconcileCredentialsRequest) GetConditions(logger log.FieldLogger) ([]c
4343
}
4444

4545
func (r *ReconcileCredentialsRequest) GetRelatedObjects(logger log.FieldLogger) ([]configv1.ObjectReference, error) {
46-
_, credRequests, _, err := r.getOperatorState(logger)
46+
_, credRequests, _, err := r.getOperatorState(context.TODO(), logger)
4747
if err != nil {
4848
return []configv1.ObjectReference{}, fmt.Errorf("failed to get operator state: %v", err)
4949
}
@@ -56,10 +56,10 @@ func (r *ReconcileCredentialsRequest) Name() string {
5656

5757
// getOperatorState gets and returns the resources necessary to compute the
5858
// operator's current state.
59-
func (r *ReconcileCredentialsRequest) getOperatorState(logger log.FieldLogger) (*corev1.Namespace, []minterv1.CredentialsRequest, operatorv1.CloudCredentialsMode, error) {
59+
func (r *ReconcileCredentialsRequest) getOperatorState(ctx context.Context, logger log.FieldLogger) (*corev1.Namespace, []minterv1.CredentialsRequest, operatorv1.CloudCredentialsMode, error) {
6060

6161
ns := &corev1.Namespace{}
62-
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: minterv1.CloudCredOperatorNamespace}, ns); err != nil {
62+
if err := r.Client.Get(ctx, types.NamespacedName{Name: minterv1.CloudCredOperatorNamespace}, ns); err != nil {
6363
if apierrors.IsNotFound(err) {
6464
return nil, nil, operatorv1.CloudCredentialsModeDefault, nil
6565
}
@@ -72,7 +72,7 @@ func (r *ReconcileCredentialsRequest) getOperatorState(logger log.FieldLogger) (
7272
// central list to live. Other credentials requests in other namespaces will not affect status,
7373
// but they will still work fine.
7474
credRequestList := &minterv1.CredentialsRequestList{}
75-
err := r.Client.List(context.TODO(), credRequestList, client.InNamespace(minterv1.CloudCredOperatorNamespace))
75+
err := r.Client.List(ctx, credRequestList, client.InNamespace(minterv1.CloudCredOperatorNamespace))
7676
if err != nil {
7777
return nil, nil, operatorv1.CloudCredentialsModeDefault, fmt.Errorf(
7878
"failed to list CredentialsRequests: %v", err)

pkg/operator/loglevel/status.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package loglevel
22

33
import (
4+
"context"
5+
46
log "github.com/sirupsen/logrus"
57

68
configv1 "github.com/openshift/api/config/v1"
@@ -9,7 +11,7 @@ import (
911

1012
var _ status.Handler = &ReconcileCloudCredentialConfig{}
1113

12-
func (r *ReconcileCloudCredentialConfig) GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
14+
func (r *ReconcileCloudCredentialConfig) GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
1315
return []configv1.ClusterOperatorStatusCondition{}, nil
1416
}
1517

pkg/operator/podidentity/podidentitywebhook_controller.go

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ import (
3737
)
3838

3939
const (
40-
controllerName = "pod-identity"
41-
deploymentName = "cloud-credential-operator"
42-
operatorNamespace = "openshift-cloud-credential-operator"
43-
retryInterval = 10 * time.Second
44-
reasonStaticResourceReconcileFailed = "StaticResourceReconcileFailed"
45-
pdb = "v4.1.0/common/poddisruptionbudget.yaml"
40+
controllerName = "pod-identity"
41+
deploymentName = "cloud-credential-operator"
42+
operatorNamespace = "openshift-cloud-credential-operator"
43+
retryInterval = 10 * time.Second
44+
reasonStaticResourceReconcileFailed = "StaticResourceReconcileFailed"
45+
reasonPodIdentityWebhookPodsStillUpdating = "PodIdentityWebhookPodsStillUpdating"
46+
reasonPodIdentityWebhookPodsNotAvailable = "PodIdentityWebhookPodsNotAvailable"
47+
pdb = "v4.1.0/common/poddisruptionbudget.yaml"
4648
)
4749

4850
var (
@@ -384,10 +386,75 @@ func (r *staticResourceReconciler) ReconcileResources(ctx context.Context) error
384386
return nil
385387
}
386388

389+
type webhookPodStatus struct {
390+
desiredReplicas int32
391+
availableReplicas int32
392+
updatedReplicas int32
393+
}
394+
395+
func (wps *webhookPodStatus) Available() bool {
396+
return wps.availableReplicas > 0
397+
}
398+
399+
func (wps *webhookPodStatus) Progressing() bool {
400+
return wps.updatedReplicas != wps.desiredReplicas
401+
}
402+
403+
func (r *staticResourceReconciler) CheckPodStatus(ctx context.Context) (*webhookPodStatus, error) {
404+
deployment := &appsv1.Deployment{}
405+
err := r.client.Get(ctx, client.ObjectKey{
406+
Name: "pod-identity-webhook",
407+
Namespace: "openshift-cloud-credential-operator",
408+
}, deployment)
409+
if err != nil {
410+
return nil, err
411+
}
412+
413+
if deployment.Spec.Replicas == nil {
414+
return &webhookPodStatus{}, nil
415+
}
416+
417+
deploymentStatus := &webhookPodStatus{
418+
desiredReplicas: *deployment.Spec.Replicas,
419+
availableReplicas: deployment.Status.AvailableReplicas,
420+
updatedReplicas: deployment.Status.UpdatedReplicas,
421+
}
422+
423+
return deploymentStatus, nil
424+
}
425+
387426
var _ status.Handler = &staticResourceReconciler{}
388427

389-
func (r *staticResourceReconciler) GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
390-
return r.conditions, nil
428+
func (r *staticResourceReconciler) GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
429+
podStatus, err := r.CheckPodStatus(ctx)
430+
if err != nil {
431+
logger.WithError(err).Errorf("checking pod identity webhook status failed")
432+
return nil, err
433+
}
434+
435+
conditions := r.conditions
436+
if !podStatus.Available() {
437+
conditions = append(conditions, configv1.ClusterOperatorStatusCondition{
438+
Type: configv1.OperatorAvailable,
439+
Status: configv1.ConditionFalse,
440+
Reason: reasonPodIdentityWebhookPodsNotAvailable,
441+
Message: "No pod identity webhook pods are available\n",
442+
})
443+
}
444+
if podStatus.Progressing() {
445+
conditions = append(conditions, configv1.ClusterOperatorStatusCondition{
446+
Type: configv1.OperatorProgressing,
447+
Status: configv1.ConditionTrue,
448+
Reason: reasonPodIdentityWebhookPodsStillUpdating,
449+
Message: fmt.Sprintf(
450+
"Waiting for pod identity webhook deployment rollout to finish: %d out of %d new replica(s) have been updated...\n",
451+
podStatus.updatedReplicas,
452+
podStatus.desiredReplicas,
453+
),
454+
})
455+
}
456+
457+
return conditions, nil
391458
}
392459

393460
func (r *staticResourceReconciler) GetRelatedObjects(logger log.FieldLogger) ([]configv1.ObjectReference, error) {

pkg/operator/secretannotator/status/status.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package status
22

33
import (
4+
"context"
45
"fmt"
56

67
log "github.com/sirupsen/logrus"
@@ -28,7 +29,7 @@ func NewSecretStatusHandler(kubeClient client.Client) *SecretStatusHandler {
2829

2930
var _ status.Handler = &SecretStatusHandler{}
3031

31-
func (s *SecretStatusHandler) GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
32+
func (s *SecretStatusHandler) GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
3233
conditions := []configv1.ClusterOperatorStatusCondition{}
3334

3435
mode, conflict, err := utils.GetOperatorConfiguration(s.kubeClient, logger)

pkg/operator/status/status_controller.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const (
4444
// Handler produces conditions and related objects to be reflected
4545
// in the cloud-credential-operator ClusterOperatorStatus
4646
type Handler interface {
47-
GetConditions(logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error)
47+
GetConditions(ctx context.Context, logger log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error)
4848
GetRelatedObjects(logger log.FieldLogger) ([]configv1.ObjectReference, error)
4949
Name() string
5050
}
@@ -179,7 +179,7 @@ func (r *ReconcileStatus) Reconcile(ctx context.Context, request reconcile.Reque
179179
metrics.MetricControllerReconcileTime.WithLabelValues(controllerName).Observe(dur.Seconds())
180180
}()
181181

182-
err := syncStatus(r.Client, r.Logger)
182+
err := syncStatus(ctx, r.Client, r.Logger)
183183
return reconcile.Result{
184184
RequeueAfter: defaultRequeuePeriod,
185185
}, err
@@ -188,7 +188,7 @@ func (r *ReconcileStatus) Reconcile(ctx context.Context, request reconcile.Reque
188188
// syncStatus is written in a way so that if we expose this function it would allow
189189
// external controllers to trigger a static sync. But for now we will make this an internal
190190
// function until the need arises to expose it.
191-
func syncStatus(kubeClient client.Client, logger log.FieldLogger) error {
191+
func syncStatus(ctx context.Context, kubeClient client.Client, logger log.FieldLogger) error {
192192
log.Info("reconciling clusteroperator status")
193193

194194
co := &configv1.ClusterOperator{}
@@ -209,7 +209,7 @@ func syncStatus(kubeClient client.Client, logger log.FieldLogger) error {
209209
conditions := []configv1.ClusterOperatorStatusCondition{}
210210
relatedObjects := []configv1.ObjectReference{}
211211
for handlerName, handler := range statusHandlers {
212-
handlerConditions, err := handler.GetConditions(logger)
212+
handlerConditions, err := handler.GetConditions(ctx, logger)
213213
logger.WithFields(log.Fields{
214214
"handlerconditions": handlerConditions,
215215
"statushandler": handlerName,
@@ -261,6 +261,7 @@ func syncStatus(kubeClient client.Client, logger log.FieldLogger) error {
261261
progressing, _ := findClusterOperatorCondition(co.Status.Conditions, configv1.OperatorProgressing)
262262
// We know this should be there.
263263
progressing.LastTransitionTime = metav1.Now()
264+
progressing.Status = configv1.ConditionTrue
264265
}
265266

266267
// ClusterOperator should already exist (from the manifest payload), but recreate it if needed

pkg/operator/status/status_controller_test.go

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,14 @@ func TestConditionsEqual(t *testing.T) {
165165
}
166166

167167
for _, tc := range testCases {
168-
actual := conditionEqual(tc.a, tc.b)
169-
if actual != tc.expected {
170-
t.Fatalf("%q: expected %v, got %v", tc.description,
171-
tc.expected, actual)
172-
}
168+
t.Run(tc.description, func(t *testing.T) {
169+
170+
actual := conditionEqual(tc.a, tc.b)
171+
if actual != tc.expected {
172+
t.Fatalf("%q: expected %v, got %v", tc.description,
173+
tc.expected, actual)
174+
}
175+
})
173176
}
174177
}
175178

@@ -293,43 +296,45 @@ func TestConditions(t *testing.T) {
293296
}
294297

295298
for _, test := range tests {
299+
t.Run(test.name, func(t *testing.T) {
300+
301+
basicClusterOperator := testBasicClusterOperator()
302+
// Make sure we have a clean ClusterOperator and CCO config for each test run
303+
objects := []runtime.Object{
304+
basicClusterOperator,
305+
testOperatorConfig(""),
306+
}
307+
308+
fakeClient := fake.NewClientBuilder().
309+
WithStatusSubresource(basicClusterOperator).
310+
WithRuntimeObjects(objects...).Build()
311+
312+
r := &ReconcileStatus{
313+
Client: fakeClient,
314+
Logger: log.WithField("controller", controllerName),
315+
platform: configv1.AWSPlatformType,
316+
}
296317

297-
basicClusterOperator := testBasicClusterOperator()
298-
// Make sure we have a clean ClusterOperator and CCO config for each test run
299-
objects := []runtime.Object{
300-
basicClusterOperator,
301-
testOperatorConfig(""),
302-
}
303-
304-
fakeClient := fake.NewClientBuilder().
305-
WithStatusSubresource(basicClusterOperator).
306-
WithRuntimeObjects(objects...).Build()
307-
308-
r := &ReconcileStatus{
309-
Client: fakeClient,
310-
Logger: log.WithField("controller", controllerName),
311-
platform: configv1.AWSPlatformType,
312-
}
313-
314-
for _, handler := range test.statusHandlers {
315-
AddHandler(handler.Name(), handler)
316-
}
317-
defer clearHandlers()
318-
319-
_, err := r.Reconcile(context.TODO(), reconcile.Request{})
320-
321-
require.NoError(t, err, "unexpected error")
322-
323-
for _, condition := range test.expectedConditions {
324-
co := getClusterOperator(fakeClient)
325-
assert.NotNil(t, co)
326-
foundCondition, _ := findClusterOperatorCondition(co.Status.Conditions, condition.conditionType)
327-
require.NotNil(t, foundCondition)
328-
assert.Equal(t, string(condition.status), string(foundCondition.Status), "condition %s had unexpected status", condition.conditionType)
329-
if condition.reason != "" {
330-
assert.Exactly(t, condition.reason, foundCondition.Reason)
318+
for _, handler := range test.statusHandlers {
319+
AddHandler(handler.Name(), handler)
331320
}
332-
}
321+
defer clearHandlers()
322+
323+
_, err := r.Reconcile(context.TODO(), reconcile.Request{})
324+
325+
require.NoError(t, err, "unexpected error")
326+
327+
for _, condition := range test.expectedConditions {
328+
co := getClusterOperator(fakeClient)
329+
assert.NotNil(t, co)
330+
foundCondition, _ := findClusterOperatorCondition(co.Status.Conditions, condition.conditionType)
331+
require.NotNil(t, foundCondition)
332+
assert.Equal(t, string(condition.status), string(foundCondition.Status), "condition %s had unexpected status", condition.conditionType)
333+
if condition.reason != "" {
334+
assert.Exactly(t, condition.reason, foundCondition.Reason)
335+
}
336+
}
337+
})
333338
}
334339
}
335340

@@ -438,21 +443,23 @@ func TestSortedStatus(t *testing.T) {
438443
}
439444

440445
for _, test := range tests {
441-
sortStatusArrays(&test.status)
442-
assert.ElementsMatchf(t, test.status.Conditions, test.expected.Conditions, "conditions = %v, want %v", test.status.Conditions, test.expected.Conditions)
443-
assert.ElementsMatchf(t, test.status.RelatedObjects, test.expected.RelatedObjects, "conditions = %v, want %v", test.status.RelatedObjects, test.expected.RelatedObjects)
446+
t.Run(test.name, func(t *testing.T) {
447+
sortStatusArrays(&test.status)
448+
assert.ElementsMatchf(t, test.status.Conditions, test.expected.Conditions, "conditions = %v, want %v", test.status.Conditions, test.expected.Conditions)
449+
assert.ElementsMatchf(t, test.status.RelatedObjects, test.expected.RelatedObjects, "conditions = %v, want %v", test.status.RelatedObjects, test.expected.RelatedObjects)
444450

445-
for i, expected := range test.expected.Conditions {
446-
assert.Equal(t, expected, test.status.Conditions[i])
447-
}
451+
for i, expected := range test.expected.Conditions {
452+
assert.Equal(t, expected, test.status.Conditions[i])
453+
}
448454

449-
for i, expected := range test.expected.RelatedObjects {
450-
assert.Equal(t, expected, test.expected.RelatedObjects[i])
451-
}
455+
for i, expected := range test.expected.RelatedObjects {
456+
assert.Equal(t, expected, test.expected.RelatedObjects[i])
457+
}
452458

453-
if !reflect.DeepEqual(test.status, test.expected) {
454-
t.Error("status' are not equal")
455-
}
459+
if !reflect.DeepEqual(test.status, test.expected) {
460+
t.Error("status' are not equal")
461+
}
462+
})
456463
}
457464
}
458465

@@ -534,7 +541,7 @@ func newHandler(name string, conditions []configv1.ClusterOperatorStatusConditio
534541
}
535542
}
536543

537-
func (h *miniHandler) GetConditions(log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
544+
func (h *miniHandler) GetConditions(context.Context, log.FieldLogger) ([]configv1.ClusterOperatorStatusCondition, error) {
538545
return h.conditions, nil
539546
}
540547

0 commit comments

Comments
 (0)