@@ -123,6 +123,17 @@ func (r *GlanceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res
123123 return ctrl.Result {}, err
124124 }
125125
126+ // initialize status if Conditions is nil, but do not reset if it already
127+ // exists
128+ isNewInstance := instance .Status .Conditions == nil
129+ if isNewInstance {
130+ instance .Status .Conditions = condition.Conditions {}
131+ }
132+
133+ // Save a copy of the condtions so that we can restore the LastTransitionTime
134+ // when a condition's state doesn't change.
135+ savedConditions := instance .Status .Conditions .DeepCopy ()
136+
126137 // Always patch the instance status when exiting this function so we can persist any changes.
127138 defer func () {
128139 // update the Ready condition based on the sub conditions
@@ -137,46 +148,40 @@ func (r *GlanceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res
137148 instance .Status .Conditions .Set (
138149 instance .Status .Conditions .Mirror (condition .ReadyCondition ))
139150 }
151+ condition .RestoreLastTransitionTimes (& instance .Status .Conditions , savedConditions )
140152 err := helper .PatchInstance (ctx , instance )
141153 if err != nil {
142154 _err = err
143155 return
144156 }
145157 }()
146158
147- // If we're not deleting this and the service object doesn't have our finalizer, add it.
148- if instance .DeletionTimestamp .IsZero () && controllerutil .AddFinalizer (instance , helper .GetFinalizer ()) {
149- return ctrl.Result {}, nil
150- }
151-
152- //
153- // initialize status
154- //
155- if instance .Status .Conditions == nil {
156- instance .Status .Conditions = condition.Conditions {}
157- // initialize conditions used later as Status=Unknown
158- cl := condition .CreateList (
159- condition .UnknownCondition (condition .DBReadyCondition , condition .InitReason , condition .DBReadyInitMessage ),
160- condition .UnknownCondition (condition .DBSyncReadyCondition , condition .InitReason , condition .DBSyncReadyInitMessage ),
161- condition .UnknownCondition (condition .MemcachedReadyCondition , condition .InitReason , condition .MemcachedReadyInitMessage ),
162- condition .UnknownCondition (condition .InputReadyCondition , condition .InitReason , condition .InputReadyInitMessage ),
163- condition .UnknownCondition (condition .ServiceConfigReadyCondition , condition .InitReason , condition .ServiceConfigReadyInitMessage ),
164- condition .UnknownCondition (glancev1 .GlanceAPIReadyCondition , condition .InitReason , glancev1 .GlanceAPIReadyInitMessage ),
165- // right now we have no dedicated KeystoneServiceReadyInitMessage
166- condition .UnknownCondition (condition .KeystoneServiceReadyCondition , condition .InitReason , "" ),
167- condition .UnknownCondition (condition .NetworkAttachmentsReadyCondition , condition .InitReason , condition .NetworkAttachmentsReadyInitMessage ),
168- // service account, role, rolebinding conditions
169- condition .UnknownCondition (condition .ServiceAccountReadyCondition , condition .InitReason , condition .ServiceAccountReadyInitMessage ),
170- condition .UnknownCondition (condition .RoleReadyCondition , condition .InitReason , condition .RoleReadyInitMessage ),
171- condition .UnknownCondition (condition .RoleBindingReadyCondition , condition .InitReason , condition .RoleBindingReadyInitMessage ),
172- condition .UnknownCondition (condition .CronJobReadyCondition , condition .InitReason , condition .CronJobReadyInitMessage ),
173- )
174-
175- instance .Status .Conditions .Init (& cl )
159+ // initialize conditions used later as Status=Unknown, except the ReadyCondition
160+ // that should be False when we start
161+ cl := condition .CreateList (
162+ // Mark ReadyCondition as False from the beginning
163+ condition .FalseCondition (condition .ReadyCondition , condition .InitReason , condition .SeverityInfo , condition .ReadyInitMessage ),
164+ condition .UnknownCondition (condition .DBReadyCondition , condition .InitReason , condition .DBReadyInitMessage ),
165+ condition .UnknownCondition (condition .DBSyncReadyCondition , condition .InitReason , condition .DBSyncReadyInitMessage ),
166+ condition .UnknownCondition (condition .MemcachedReadyCondition , condition .InitReason , condition .MemcachedReadyInitMessage ),
167+ condition .UnknownCondition (condition .InputReadyCondition , condition .InitReason , condition .InputReadyInitMessage ),
168+ condition .UnknownCondition (condition .ServiceConfigReadyCondition , condition .InitReason , condition .ServiceConfigReadyInitMessage ),
169+ condition .UnknownCondition (glancev1 .GlanceAPIReadyCondition , condition .InitReason , glancev1 .GlanceAPIReadyInitMessage ),
170+ condition .UnknownCondition (condition .KeystoneServiceReadyCondition , condition .InitReason , "" ),
171+ // service account, role, rolebinding conditions
172+ condition .UnknownCondition (condition .ServiceAccountReadyCondition , condition .InitReason , condition .ServiceAccountReadyInitMessage ),
173+ condition .UnknownCondition (condition .RoleReadyCondition , condition .InitReason , condition .RoleReadyInitMessage ),
174+ condition .UnknownCondition (condition .RoleBindingReadyCondition , condition .InitReason , condition .RoleBindingReadyInitMessage ),
175+ condition .UnknownCondition (condition .CronJobReadyCondition , condition .InitReason , condition .CronJobReadyInitMessage ),
176+ )
177+ instance .Status .Conditions .Init (& cl )
176178
179+ // If we're not deleting this and the service object doesn't have our finalizer, add it.
180+ if instance .DeletionTimestamp .IsZero () && controllerutil .AddFinalizer (instance , helper .GetFinalizer ()) || isNewInstance {
177181 // Register overall status immediately to have an early feedback e.g. in the cli
178182 return ctrl.Result {}, nil
179183 }
184+
180185 if instance .Status .Hash == nil {
181186 instance .Status .Hash = map [string ]string {}
182187 }
@@ -479,12 +484,7 @@ func (r *GlanceReconciler) reconcileInit(
479484 r .Log .Info (fmt .Sprintf ("Service '%s' - Job %s hash added - %s" , instance .Name , jobDef .Name , instance .Status .Hash [glancev1 .DbSyncHash ]))
480485 }
481486 instance .Status .Conditions .MarkTrue (condition .DBSyncReadyCondition , condition .DBSyncReadyMessage )
482-
483- // when job passed, mark NetworkAttachmentsReadyCondition ready
484- instance .Status .Conditions .MarkTrue (condition .NetworkAttachmentsReadyCondition , condition .NetworkAttachmentsReadyMessage )
485-
486487 // run Glance db sync - end
487-
488488 r .Log .Info (fmt .Sprintf ("Reconciled Service '%s' init successfully" , instance .Name ))
489489 return ctrl.Result {}, nil
490490}
@@ -699,8 +699,14 @@ func (r *GlanceReconciler) reconcileNormal(ctx context.Context, instance *glance
699699 return ctrlResult , err
700700 }
701701 instance .Status .Conditions .MarkTrue (condition .CronJobReadyCondition , condition .CronJobReadyMessage )
702-
703702 // create CronJob - end
703+
704+ // We reached the end of the Reconcile, update the Ready condition based on
705+ // the sub conditions
706+ if instance .Status .Conditions .AllSubConditionIsTrue () {
707+ instance .Status .Conditions .MarkTrue (
708+ condition .ReadyCondition , condition .ReadyMessage )
709+ }
704710 return ctrl.Result {}, nil
705711}
706712
@@ -873,6 +879,10 @@ func (r *GlanceReconciler) apiDeploymentCreateOrUpdate(
873879 apiSpec .GlanceAPITemplate .StorageRequest = instance .Spec .StorageRequest
874880 apiSpec .GlanceAPITemplate .StorageClass = instance .Spec .StorageClass
875881 apiSpec .MemcachedInstance = memcached .Name
882+ // Make sure to inject the ContainerImage passed by the OpenStackVersions
883+ // resource to all the underlying instances and rollout a new StatefulSet
884+ // if it has been changed
885+ apiSpec .GlanceAPITemplate .ContainerImage = instance .Spec .ContainerImage
876886
877887 // We select which glanceAPI should register the keystoneEndpoint by using
878888 // an API selector defined in the main glance CR; if it matches with the
0 commit comments