Skip to content

Commit f8ed170

Browse files
committed
Add missing conditions in both the top-level and glanceAPI controller
Most of the code ofter returns ctrl.Result{}, err in case there's something wrong with a specific component that is supposed to be ready before move forward with the reconcile loop. However, an aspect of this evaluation is the missing update of the condition for that particular component. This commit fixes it and introduces the missing updates during the reconcile loop. Signed-off-by: Francesco Pantano <[email protected]>
1 parent c6441c1 commit f8ed170

File tree

5 files changed

+96
-17
lines changed

5 files changed

+96
-17
lines changed

controllers/glance_controller.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,18 @@ func (r *GlanceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res
156156
instance.Status.Conditions = condition.Conditions{}
157157
}
158158

159-
// initialize conditions used later as Status=Unknown
159+
// initialize conditions used later as Status=Unknown, except the ReadyCondition
160+
// that should be False when we start
160161
cl := condition.CreateList(
162+
// Mark ReadyCondition as False from the beginning
163+
condition.FalseCondition(condition.ReadyCondition, condition.InitReason, condition.SeverityInfo, condition.ReadyInitMessage),
161164
condition.UnknownCondition(condition.DBReadyCondition, condition.InitReason, condition.DBReadyInitMessage),
162165
condition.UnknownCondition(condition.DBSyncReadyCondition, condition.InitReason, condition.DBSyncReadyInitMessage),
163166
condition.UnknownCondition(condition.MemcachedReadyCondition, condition.InitReason, condition.MemcachedReadyInitMessage),
164167
condition.UnknownCondition(condition.InputReadyCondition, condition.InitReason, condition.InputReadyInitMessage),
165168
condition.UnknownCondition(condition.ServiceConfigReadyCondition, condition.InitReason, condition.ServiceConfigReadyInitMessage),
166169
condition.UnknownCondition(glancev1.GlanceAPIReadyCondition, condition.InitReason, glancev1.GlanceAPIReadyInitMessage),
167170
condition.UnknownCondition(condition.KeystoneServiceReadyCondition, condition.InitReason, ""),
168-
condition.UnknownCondition(condition.NetworkAttachmentsReadyCondition, condition.InitReason, condition.NetworkAttachmentsReadyInitMessage),
169171
// service account, role, rolebinding conditions
170172
condition.UnknownCondition(condition.ServiceAccountReadyCondition, condition.InitReason, condition.ServiceAccountReadyInitMessage),
171173
condition.UnknownCondition(condition.RoleReadyCondition, condition.InitReason, condition.RoleReadyInitMessage),

controllers/glanceapi_controller.go

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,12 @@ func (r *GlanceAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
147147
//
148148
// initialize status
149149
//
150-
instance.Status.Conditions = condition.Conditions{}
150+
// initialize status if Conditions is nil, but do not reset if it
151+
// already exists
152+
isNewInstance := instance.Status.Conditions == nil
153+
if isNewInstance {
154+
instance.Status.Conditions = condition.Conditions{}
155+
}
151156
// initialize conditions used later as Status=Unknown
152157
cl := condition.CreateList(
153158
condition.UnknownCondition(glancev1.CinderCondition, condition.InitReason, glancev1.CinderInitMessage),
@@ -163,6 +168,10 @@ func (r *GlanceAPIReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
163168
)
164169

165170
instance.Status.Conditions.Init(&cl)
171+
if isNewInstance {
172+
// Register overall status immediately to have an early feedback e.g. in the cli
173+
return ctrl.Result{}, nil
174+
}
166175

167176
if instance.Status.Hash == nil {
168177
instance.Status.Hash = map[string]string{}
@@ -471,6 +480,8 @@ func (r *GlanceAPIReconciler) reconcileInit(
471480
serviceLabels,
472481
)
473482
if err != nil {
483+
// The ExposeServiceReadyCondition is already marked as False
484+
// within the GetHeadlessService function: we can return
474485
return ctrlResult, err
475486
}
476487

@@ -485,6 +496,12 @@ func (r *GlanceAPIReconciler) reconcileInit(
485496
apiEndpoints[string(endpointType)], err = svc.GetAPIEndpoint(
486497
svcOverride.EndpointURL, data.Protocol, data.Path)
487498
if err != nil {
499+
instance.Status.Conditions.MarkFalse(
500+
condition.ExposeServiceReadyCondition,
501+
condition.ErrorReason,
502+
condition.SeverityWarning,
503+
condition.ExposeServiceReadyErrorMessage,
504+
err.Error())
488505
return ctrl.Result{}, err
489506
}
490507
}
@@ -503,9 +520,14 @@ func (r *GlanceAPIReconciler) reconcileInit(
503520
// Create/Patch the KeystoneEndpoints
504521
ctrlResult, err := r.ensureKeystoneEndpoints(ctx, helper, instance, serviceLabels)
505522
if err != nil {
523+
instance.Status.Conditions.MarkFalse(
524+
condition.KeystoneEndpointReadyCondition,
525+
condition.ErrorReason,
526+
condition.SeverityWarning,
527+
"Creating KeyStoneEndpoint CR %s",
528+
err.Error())
506529
return ctrlResult, err
507530
}
508-
509531
r.Log.Info(fmt.Sprintf("Reconciled Service '%s' init successfully", instance.Name))
510532
return ctrl.Result{}, nil
511533
}
@@ -566,6 +588,12 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
566588
availableBackends := glancev1.GetEnabledBackends(instance.Spec.CustomServiceConfig)
567589
_, hashChanged, err := r.createHashOfBackendConfig(instance, availableBackends)
568590
if err != nil {
591+
instance.Status.Conditions.Set(condition.FalseCondition(
592+
condition.InputReadyCondition,
593+
condition.ErrorReason,
594+
condition.SeverityWarning,
595+
condition.InputReadyErrorMessage,
596+
err.Error()))
569597
return ctrl.Result{}, err
570598
}
571599
// Update the current StateFulSet (by recreating it) only when a backend is
@@ -642,9 +670,15 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
642670
err.Error()))
643671
return ctrlResult, err
644672
} else if (ctrlResult != ctrl.Result{}) {
673+
// Marking the condition as Unknown because we are not returining
674+
// an err, but comparing the ctrlResult: this represents an in
675+
// progress operation rather than something that failed
676+
instance.Status.Conditions.MarkUnknown(
677+
condition.TLSInputReadyCondition,
678+
condition.InitReason,
679+
condition.InputReadyInitMessage)
645680
return ctrlResult, nil
646681
}
647-
648682
if hash != "" {
649683
configVars[tls.CABundleKey] = env.SetValue(hash)
650684
}
@@ -661,6 +695,13 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
661695
err.Error()))
662696
return ctrlResult, err
663697
} else if (ctrlResult != ctrl.Result{}) {
698+
// Marking the condition as Unknown because we are not returining
699+
// an err, but comparing the ctrlResult: this represents an in
700+
// progress operation rather than something that failed
701+
instance.Status.Conditions.MarkUnknown(
702+
condition.TLSInputReadyCondition,
703+
condition.InitReason,
704+
condition.InputReadyInitMessage)
664705
return ctrlResult, nil
665706
}
666707
configVars[tls.TLSHashName] = env.SetValue(certsHash)
@@ -680,10 +721,6 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
680721
condition.ServiceConfigReadyErrorMessage,
681722
err.Error()))
682723
return ctrl.Result{}, err
683-
} else if hashChanged {
684-
// Hash changed and instance status should be updated (which will be done by main defer func),
685-
// so we need to return and reconcile again
686-
return ctrl.Result{}, nil
687724
}
688725
instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage)
689726
// Create Secrets - end
@@ -695,6 +732,13 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
695732
// networks to attach to
696733
serviceAnnotations, ctrlResult, err = ensureNAD(ctx, &instance.Status.Conditions, instance.Spec.NetworkAttachments, helper)
697734
if err != nil {
735+
instance.Status.Conditions.MarkFalse(
736+
condition.NetworkAttachmentsReadyCondition,
737+
condition.ErrorReason,
738+
condition.SeverityWarning,
739+
condition.NetworkAttachmentsReadyErrorMessage,
740+
err,
741+
)
698742
return ctrlResult, err
699743
}
700744

@@ -764,6 +808,13 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
764808
// verify if network attachment matches expectations
765809
networkReady, networkAttachmentStatus, err := nad.VerifyNetworkStatusFromAnnotation(ctx, helper, instance.Spec.NetworkAttachments, GetServiceLabels(instance), instance.Status.ReadyCount)
766810
if err != nil {
811+
err = fmt.Errorf("verifying API NetworkAttachments (%s) %w", instance.Spec.NetworkAttachments, err)
812+
instance.Status.Conditions.MarkFalse(
813+
condition.NetworkAttachmentsReadyCondition,
814+
condition.ErrorReason,
815+
condition.SeverityWarning,
816+
condition.NetworkAttachmentsReadyErrorMessage,
817+
err)
767818
return ctrl.Result{}, err
768819
}
769820

@@ -830,8 +881,11 @@ func (r *GlanceAPIReconciler) reconcileNormal(ctx context.Context, instance *gla
830881
}
831882
}
832883

833-
// Mark the CronJobReadyCondition as True because there's nothing to do here
834-
instance.Status.Conditions.MarkTrue(condition.CronJobReadyCondition, condition.CronJobReadyMessage)
884+
// If we reach this point, we can mark the CronJobReadyCondition as True
885+
instance.Status.Conditions.MarkTrue(
886+
condition.CronJobReadyCondition,
887+
condition.CronJobReadyMessage,
888+
)
835889

836890
// create ImageCache cronJobs - end
837891
r.Log.Info(fmt.Sprintf("Reconciled Service '%s' successfully", instance.Name))
@@ -1044,13 +1098,11 @@ func (r *GlanceAPIReconciler) ensureKeystoneEndpoints(
10441098
}
10451099
return ctrlResult, nil
10461100
}
1047-
10481101
// Build the keystoneEndpoints Spec
10491102
ksEndpointSpec := keystonev1.KeystoneEndpointSpec{
10501103
ServiceName: glance.ServiceName,
10511104
Endpoints: instance.Status.APIEndpoints,
10521105
}
1053-
10541106
ksSvc := keystonev1.NewKeystoneEndpoint(
10551107
instance.Name,
10561108
instance.Namespace,
@@ -1062,14 +1114,12 @@ func (r *GlanceAPIReconciler) ensureKeystoneEndpoints(
10621114
if err != nil {
10631115
return ctrlResult, err
10641116
}
1065-
10661117
// mirror the Status, Reason, Severity and Message of the latest keystoneendpoint condition
10671118
// into a local condition with the type condition.KeystoneEndpointReadyCondition
10681119
c := ksSvc.GetConditions().Mirror(condition.KeystoneEndpointReadyCondition)
10691120
if c != nil {
10701121
instance.Status.Conditions.Set(c)
10711122
}
1072-
10731123
return ctrlResult, nil
10741124
}
10751125

docs/dev/design-decisions.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,30 @@ instance that should be registered in Keystone.
416416
An update to the `keystoneEndpoint` parameter results in a reconciliation execution
417417
that switches the two affected instances (the one registered in keystone and the
418418
one proposed by the new update).
419+
420+
## How Conditions are managed
421+
422+
Conditions represent a critical aspect for reconciliation because they allow:
423+
424+
1. to evaluate the status of a particular component validating a set of conditions
425+
2. give a feedback to the end user about the current state of the Deployment
426+
3. identify the status of the underlying GlanceAPIs and report their healhy to
427+
the upper level CR
428+
429+
The document [docs/conditions]() describes the meaning of the k8s-operators
430+
shared conditions.
431+
Conditions are re-evaluated when a new Reconciliation loop starts, and if one of
432+
them is different from what has been previously registered, an update is performed,
433+
and it allows to give immediate feedback to the user.
434+
Conditions are initially marked as `Unknown`, while the general `ReadyCondition`
435+
is marked as `False` because we assume that the Deployment is in progress but
436+
not `Ready` at the same time, until the entire set of conditions are evaluated.
437+
Conditions can be seen as a checklist or steps within the reconciliation loop,
438+
and while the loop proceed to the end, each of them is evaluated.
439+
If a particular component with an associated condition is not Ready, an `error`
440+
is returned from the operator, and its failing condition is marked as `False`
441+
with an appropriate error message.
442+
If the end of the loop is reached, it means we passed through all the steps and
443+
the Conditions are marked to `True`: it is possible, at this point, to mark the
444+
overall `ReadyCondition` to `Status=True` as well and `Mirror` the result to the
445+
top-level CR.

test/functional/glance_controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ var _ = Describe("Glance controller", func() {
5353
It("initializes the status fields", func() {
5454
Eventually(func(g Gomega) {
5555
glance := GetGlance(glanceName)
56-
g.Expect(glance.Status.Conditions).To(HaveLen(13))
56+
g.Expect(glance.Status.Conditions).To(HaveLen(12))
5757
g.Expect(glance.Status.DatabaseHostname).To(Equal(""))
5858
g.Expect(glance.Status.APIEndpoints).To(BeEmpty())
5959
}, timeout, interval).Should(Succeed())

test/functional/sample_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ var _ = Describe("Samples", func() {
6161
Eventually(func(g Gomega) {
6262
name := CreateGlanceFromSample("glance_v1beta1_glance.yaml", glanceTest.Instance)
6363
glance := GetGlance(name)
64-
g.Expect(glance.Status.Conditions).To(HaveLen(13))
64+
g.Expect(glance.Status.Conditions).To(HaveLen(12))
6565
g.Expect(glance.Status.DatabaseHostname).To(Equal(""))
6666
g.Expect(glance.Status.APIEndpoints).To(BeEmpty())
6767
}, timeout, interval).Should(Succeed())

0 commit comments

Comments
 (0)