Skip to content

Commit 55085aa

Browse files
committed
add Progressing condition
Signed-off-by: Ankita Thomas <ankithom@redhat.com>
1 parent be9f4a3 commit 55085aa

File tree

6 files changed

+114
-415
lines changed

6 files changed

+114
-415
lines changed

api/v1alpha1/clusterextension_types.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,16 @@ type ClusterExtensionSpec struct {
7575

7676
const (
7777
// TODO(user): add more Types, here and into init()
78+
// TypeInstalled indicates whether the install for the bundle
79+
// referenced in the status was completed.
80+
// It does not indicate whether the App created by the bundle is healthy.
7881
TypeInstalled = "Installed"
7982
TypeResolved = "Resolved"
83+
// TypeProgressing indicates whether operator-controller is
84+
// reconciling, installing, updating or deleting an extension.
85+
TypeProgressing = "Progressing"
8086
// TypeDeprecated is a rollup condition that is present when
81-
// any of the deprecated conditions are present.
87+
// any of the deprecated conditions are present for the resolved bundle.
8288
TypeDeprecated = "Deprecated"
8389
TypePackageDeprecated = "PackageDeprecated"
8490
TypeChannelDeprecated = "ChannelDeprecated"
@@ -93,8 +99,8 @@ const (
9399
ReasonResolutionUnknown = "ResolutionUnknown"
94100
ReasonSuccess = "Success"
95101
ReasonDeprecated = "Deprecated"
96-
ReasonDeleteFailed = "DeleteFailed"
97-
ReasonDeleting = "Deleting"
102+
ReasonProgressing = "Progressing"
103+
ReasonFailed = "Failed"
98104
)
99105

100106
func init() {
@@ -118,9 +124,13 @@ func init() {
118124
ReasonInvalidSpec,
119125
ReasonSuccess,
120126
ReasonDeprecated,
121-
ReasonDeleteFailed,
122-
ReasonDeleting,
127+
ReasonProgressing,
128+
ReasonFailed,
123129
)
130+
131+
conditionsets.ExtensionConditionTypes = []string{
132+
TypeProgressing,
133+
}
124134
}
125135

126136
// ClusterExtensionStatus defines the observed state of ClusterExtension

api/v1alpha1/clusterextension_types_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func TestClusterExtensionTypeRegistration(t *testing.T) {
2222
}
2323

2424
for _, tt := range types {
25-
if !slices.Contains(conditionsets.ConditionTypes, tt) {
25+
if !slices.Contains(conditionsets.ConditionTypes, tt) && !slices.Contains(conditionsets.ExtensionConditionTypes, tt) {
2626
t.Errorf("append Type%s to conditionsets.ConditionTypes in this package's init function", tt)
2727
}
2828
}

internal/conditionsets/conditionsets.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ package conditionsets
2222
// NOTE: These are populated by init() in api/v1alpha1/clusterextension_types.go
2323
var ConditionTypes []string
2424
var ConditionReasons []string
25+
26+
// This is the set of Extension exclusive condition Types.
27+
var ExtensionConditionTypes []string

internal/controllers/common_controller.go

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,6 @@ func setResolvedStatusConditionFailed(conditions *[]metav1.Condition, message st
6565
})
6666
}
6767

68-
// setResolvedStatusConditionUnknown sets the resolved status condition to unknown.
69-
func setResolvedStatusConditionUnknown(conditions *[]metav1.Condition, message string, generation int64) {
70-
apimeta.SetStatusCondition(conditions, metav1.Condition{
71-
Type: ocv1alpha1.TypeResolved,
72-
Status: metav1.ConditionUnknown,
73-
Reason: ocv1alpha1.ReasonResolutionUnknown,
74-
Message: message,
75-
ObservedGeneration: generation,
76-
})
77-
}
78-
7968
// setInstalledStatusConditionSuccess sets the installed status condition to success.
8069
func setInstalledStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) {
8170
apimeta.SetStatusCondition(conditions, metav1.Condition{
@@ -98,29 +87,7 @@ func setInstalledStatusConditionFailed(conditions *[]metav1.Condition, message s
9887
})
9988
}
10089

101-
// setInstalledStatusConditionDeleting sets the installed status condition to unknown for deletes in progress.
102-
func setInstalledStatusConditionDeleting(conditions *[]metav1.Condition, message string, generation int64) {
103-
apimeta.SetStatusCondition(conditions, metav1.Condition{
104-
Type: ocv1alpha1.TypeInstalled,
105-
Status: metav1.ConditionUnknown,
106-
Reason: ocv1alpha1.ReasonDeleting,
107-
Message: message,
108-
ObservedGeneration: generation,
109-
})
110-
}
111-
112-
// setInstalledStatusConditionDeleteFailed sets the installed status condition to unknown for failed deletes.
113-
func setInstalledStatusConditionDeleteFailed(conditions *[]metav1.Condition, message string, generation int64) {
114-
apimeta.SetStatusCondition(conditions, metav1.Condition{
115-
Type: ocv1alpha1.TypeInstalled,
116-
Status: metav1.ConditionUnknown,
117-
Reason: ocv1alpha1.ReasonDeleteFailed,
118-
Message: message,
119-
ObservedGeneration: generation,
120-
})
121-
}
122-
123-
// setDEprecationStatusesUnknown sets the deprecation status conditions to unknown.
90+
// setDeprecationStatusesUnknown sets the deprecation status conditions to unknown.
12491
func setDeprecationStatusesUnknown(conditions *[]metav1.Condition, message string, generation int64) {
12592
conditionTypes := []string{
12693
ocv1alpha1.TypeDeprecated,
@@ -139,3 +106,36 @@ func setDeprecationStatusesUnknown(conditions *[]metav1.Condition, message strin
139106
})
140107
}
141108
}
109+
110+
// setProgressingStatusConditionSuccess sets the progressing status condition to false for a successful install or upgrade.
111+
func setProgressingStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) {
112+
apimeta.SetStatusCondition(conditions, metav1.Condition{
113+
Type: ocv1alpha1.TypeProgressing,
114+
Status: metav1.ConditionFalse,
115+
Reason: ocv1alpha1.ReasonSuccess,
116+
Message: message,
117+
ObservedGeneration: generation,
118+
})
119+
}
120+
121+
// setProgressingStatusConditionFailed sets the progressing status condition to False for a failed install or upgrade.
122+
func setProgressingStatusConditionFailed(conditions *[]metav1.Condition, message string, generation int64) {
123+
apimeta.SetStatusCondition(conditions, metav1.Condition{
124+
Type: ocv1alpha1.TypeProgressing,
125+
Status: metav1.ConditionFalse,
126+
Reason: ocv1alpha1.ReasonFailed,
127+
Message: message,
128+
ObservedGeneration: generation,
129+
})
130+
}
131+
132+
// setProgressingStatusConditionProgressing sets the progressing status condition to true for an app being reconciled.
133+
func setProgressingStatusConditionProgressing(conditions *[]metav1.Condition, message string, generation int64) {
134+
apimeta.SetStatusCondition(conditions, metav1.Condition{
135+
Type: ocv1alpha1.TypeProgressing,
136+
Status: metav1.ConditionTrue,
137+
Reason: ocv1alpha1.ReasonProgressing,
138+
Message: message,
139+
ObservedGeneration: generation,
140+
})
141+
}

internal/controllers/extension_controller.go

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,14 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext
127127
if !features.OperatorControllerFeatureGate.Enabled(features.EnableExtensionAPI) {
128128
l.Info("extension feature is gated", "name", ext.GetName(), "namespace", ext.GetNamespace())
129129

130-
// Set the TypeInstalled condition to Unknown to indicate that the resolution
130+
// Set the TypeInstalled condition to Failed to indicate that the resolution
131131
// hasn't been attempted yet, due to the spec being invalid.
132132
ext.Status.InstalledBundle = nil
133-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration())
134-
// Set the TypeResolved condition to Unknown to indicate that the resolution
133+
setInstalledStatusConditionFailed(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration())
134+
// Set the TypeResolved condition to Failed to indicate that the resolution
135135
// hasn't been attempted yet, due to the spec being invalid.
136136
ext.Status.ResolvedBundle = nil
137-
setResolvedStatusConditionUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration())
137+
setResolvedStatusConditionFailed(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration())
138138

139139
setDeprecationStatusesUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration())
140140
return ctrl.Result{}, nil
@@ -150,8 +150,10 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext
150150
// TODO: Improve the resolution logic.
151151
bundle, err := r.resolve(ctx, *ext)
152152
if err != nil {
153-
ext.Status.InstalledBundle = nil
154-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, "installation has not been attempted as resolution failed", ext.GetGeneration())
153+
if c := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled); c == nil {
154+
ext.Status.InstalledBundle = nil
155+
setInstalledStatusConditionFailed(&ext.Status.Conditions, "installation has not been attempted as resolution failed", ext.GetGeneration())
156+
}
155157
ext.Status.ResolvedBundle = nil
156158
setResolvedStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
157159
setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as resolution failed", ext.GetGeneration())
@@ -162,35 +164,47 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext
162164
ext.Status.ResolvedBundle = bundleMetadataFor(bundle)
163165
setResolvedStatusConditionSuccess(&ext.Status.Conditions, fmt.Sprintf("resolved to %q", bundle.Image), ext.GetGeneration())
164166

167+
// Populate the deprecation status using the resolved bundle
168+
SetDeprecationStatusInExtension(ext, bundle)
169+
165170
mediaType, err := bundle.MediaType()
166171
if err != nil {
167-
setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
168-
setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration())
172+
if c := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled); c == nil {
173+
ext.Status.InstalledBundle = nil
174+
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("failed to read bundle mediaType: %v", err), ext.GetGeneration())
175+
}
176+
setProgressingStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("failed to read bundle mediaType: %v", err), ext.GetGeneration())
169177
return ctrl.Result{}, err
170178
}
171179

172180
// TODO: this needs to include the registryV1 bundle option. As of this PR, this only supports direct
173181
// installation of a set of manifests.
174182
if mediaType != catalogmetadata.MediaTypePlain {
175-
// Set the TypeInstalled condition to Unknown to indicate that the resolution
176-
// hasn't been attempted yet, due to the spec being invalid.
177-
ext.Status.InstalledBundle = nil
178-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, fmt.Sprintf("bundle type %s not supported currently", mediaType), ext.GetGeneration())
179-
setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration())
183+
if c := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled); c == nil {
184+
// Set the TypeInstalled condition to Failed to indicate that the resolution
185+
// hasn't been attempted yet, due to the spec being invalid.
186+
ext.Status.InstalledBundle = nil
187+
setInstalledStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("bundle type %s not supported currently", mediaType), ext.GetGeneration())
188+
}
189+
setProgressingStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("bundle type %s not supported currently", mediaType), ext.GetGeneration())
180190
return ctrl.Result{}, nil
181191
}
182192

183193
app, err := r.GenerateExpectedApp(*ext, bundle)
184194
if err != nil {
185-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
186-
setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration())
195+
if c := apimeta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled); c == nil {
196+
ext.Status.InstalledBundle = nil
197+
setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
198+
}
199+
setProgressingStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
187200
return ctrl.Result{}, err
188201
}
189202

190203
if err := r.ensureApp(ctx, app); err != nil {
191204
// originally Reason: ocv1alpha1.ReasonInstallationFailed
192205
ext.Status.InstalledBundle = nil
193206
setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
207+
setProgressingStatusConditionProgressing(&ext.Status.Conditions, "installation failed", ext.GetGeneration())
194208
return ctrl.Result{}, err
195209
}
196210

@@ -199,14 +213,16 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext
199213
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(app.UnstructuredContent(), existingTypedApp); err != nil {
200214
// originally Reason: ocv1alpha1.ReasonInstallationStatusUnknown
201215
ext.Status.InstalledBundle = nil
202-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
203-
setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration())
216+
setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration())
217+
setProgressingStatusConditionProgressing(&ext.Status.Conditions, "installation failed", ext.GetGeneration())
204218
return ctrl.Result{}, err
205219
}
206220

207-
version, _ := bundle.Version()
208-
MapAppStatusToCondition(existingTypedApp, ext, &ocv1alpha1.BundleMetadata{Name: bundle.Name, Version: fmt.Sprintf("%v", version)})
209-
SetDeprecationStatusInExtension(ext, bundle)
221+
ext.Status.InstalledBundle = bundleMetadataFor(bundle)
222+
setInstalledStatusConditionSuccess(&ext.Status.Conditions, fmt.Sprintf("successfully installed %v", ext.Status.InstalledBundle), ext.GetGeneration())
223+
224+
// TODO: add conditions to determine extension health
225+
mapAppStatusToCondition(existingTypedApp, ext)
210226

211227
return ctrl.Result{}, nil
212228
}
@@ -230,63 +246,36 @@ func (r *ExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error {
230246
}
231247

232248
// mapAppStatusToCondition maps the reconciling/deleting App conditions to the installed/deleting conditions on the Extension.
233-
func MapAppStatusToCondition(existingApp *kappctrlv1alpha1.App, ext *ocv1alpha1.Extension, bundle *ocv1alpha1.BundleMetadata) {
234-
if ext == nil {
235-
return
236-
}
237-
message := "install status unknown"
238-
ext.Status.InstalledBundle = nil
239-
240-
if existingApp == nil {
241-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, message, ext.Generation)
249+
func mapAppStatusToCondition(existingApp *kappctrlv1alpha1.App, ext *ocv1alpha1.Extension) {
250+
// Note: App.Status.Inspect errors are never surfaced to App conditions, so are currently ignored when determining App status.
251+
if ext == nil || existingApp == nil {
242252
return
243253
}
244-
message = existingApp.Status.FriendlyDescription
245-
if strings.Contains(message, "Error (see .status.usefulErrorMessage for details)") || len(message) == 0 {
254+
message := existingApp.Status.FriendlyDescription
255+
if len(message) == 0 || strings.Contains(message, "Error (see .status.usefulErrorMessage for details)") {
246256
message = existingApp.Status.UsefulErrorMessage
247257
}
248258

249-
orderedAppStatuses := []kappctrlv1alpha1.ConditionType{
250-
kappctrlv1alpha1.DeleteFailed,
251-
kappctrlv1alpha1.Deleting,
252-
kappctrlv1alpha1.ReconcileSucceeded,
253-
kappctrlv1alpha1.ReconcileFailed,
254-
kappctrlv1alpha1.Reconciling,
255-
}
256259
appStatusMapFn := map[kappctrlv1alpha1.ConditionType]func(*[]metav1.Condition, string, int64){
257-
kappctrlv1alpha1.DeleteFailed: setInstalledStatusConditionDeleteFailed,
258-
kappctrlv1alpha1.Deleting: setInstalledStatusConditionDeleting,
259-
kappctrlv1alpha1.ReconcileSucceeded: setInstalledStatusConditionSuccess,
260-
kappctrlv1alpha1.ReconcileFailed: setInstalledStatusConditionFailed,
261-
kappctrlv1alpha1.Reconciling: setInstalledStatusConditionUnknown,
260+
kappctrlv1alpha1.Deleting: setProgressingStatusConditionProgressing,
261+
kappctrlv1alpha1.Reconciling: setProgressingStatusConditionProgressing,
262+
kappctrlv1alpha1.DeleteFailed: setProgressingStatusConditionFailed,
263+
kappctrlv1alpha1.ReconcileFailed: setProgressingStatusConditionFailed,
264+
kappctrlv1alpha1.ReconcileSucceeded: setProgressingStatusConditionSuccess,
262265
}
263-
for _, cond := range orderedAppStatuses {
266+
for cond := range appStatusMapFn {
264267
if c := findStatusCondition(existingApp.Status.GenericStatus.Conditions, cond); c != nil && c.Status == corev1.ConditionTrue {
265268
if len(message) == 0 {
266269
message = c.Message
267270
}
268-
if c.Type == kappctrlv1alpha1.ReconcileSucceeded {
269-
ext.Status.InstalledBundle = bundle
270-
}
271-
appStatusMapFn[cond](&ext.Status.Conditions, message, ext.Generation)
271+
appStatusMapFn[cond](&ext.Status.Conditions, fmt.Sprintf("App %s: %s", c.Type, message), ext.Generation)
272272
return
273273
}
274274
}
275275
if len(message) == 0 {
276-
message = "install status unknown"
277-
}
278-
setInstalledStatusConditionUnknown(&ext.Status.Conditions, message, ext.Generation)
279-
}
280-
281-
// findStatusCondition finds the conditionType in conditions.
282-
// TODO: suggest using upstream conditions to Carvel.
283-
func findStatusCondition(conditions []kappctrlv1alpha1.Condition, conditionType kappctrlv1alpha1.ConditionType) *kappctrlv1alpha1.Condition {
284-
for i := range conditions {
285-
if conditions[i].Type == conditionType {
286-
return &conditions[i]
287-
}
276+
message = "waiting for app"
288277
}
289-
return nil
278+
setProgressingStatusConditionProgressing(&ext.Status.Conditions, message, ext.Generation)
290279
}
291280

292281
// setDeprecationStatus will set the appropriate deprecation statuses for a Extension
@@ -371,6 +360,17 @@ func SetDeprecationStatusInExtension(ext *ocv1alpha1.Extension, bundle *catalogm
371360
}
372361
}
373362

363+
// findStatusCondition finds the conditionType in conditions.
364+
// TODO: suggest using upstream conditions to Carvel.
365+
func findStatusCondition(conditions []kappctrlv1alpha1.Condition, conditionType kappctrlv1alpha1.ConditionType) *kappctrlv1alpha1.Condition {
366+
for i := range conditions {
367+
if conditions[i].Type == conditionType {
368+
return &conditions[i]
369+
}
370+
}
371+
return nil
372+
}
373+
374374
func (r *ExtensionReconciler) ensureApp(ctx context.Context, desiredApp *unstructured.Unstructured) error {
375375
existingApp, err := r.existingAppUnstructured(ctx, desiredApp.GetName(), desiredApp.GetNamespace())
376376
if client.IgnoreNotFound(err) != nil {

0 commit comments

Comments
 (0)