diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index b763cb60a..5af25e0b9 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -93,6 +93,8 @@ const ( ReasonResolutionUnknown = "ResolutionUnknown" ReasonSuccess = "Success" ReasonDeprecated = "Deprecated" + ReasonDeleteFailed = "DeleteFailed" + ReasonDeleting = "Deleting" ) func init() { @@ -116,6 +118,8 @@ func init() { ReasonInvalidSpec, ReasonSuccess, ReasonDeprecated, + ReasonDeleteFailed, + ReasonDeleting, ) } diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 98067824d..2b2fcc743 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -99,6 +99,28 @@ func setInstalledStatusConditionFailed(conditions *[]metav1.Condition, message s }) } +// setInstalledStatusConditionDeleting sets the installed status condition to unknown for deletes in progress. +func setInstalledStatusConditionDeleting(conditions *[]metav1.Condition, message string, generation int64) { + apimeta.SetStatusCondition(conditions, metav1.Condition{ + Type: ocv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: ocv1alpha1.ReasonDeleting, + Message: message, + ObservedGeneration: generation, + }) +} + +// setInstalledStatusConditionDeleteFailed sets the installed status condition to unknown for failed deletes. +func setInstalledStatusConditionDeleteFailed(conditions *[]metav1.Condition, message string, generation int64) { + apimeta.SetStatusCondition(conditions, metav1.Condition{ + Type: ocv1alpha1.TypeInstalled, + Status: metav1.ConditionUnknown, + Reason: ocv1alpha1.ReasonDeleteFailed, + Message: message, + ObservedGeneration: generation, + }) +} + // setDEprecationStatusesUnknown sets the deprecation status conditions to unknown. func setDeprecationStatusesUnknown(conditions *[]metav1.Condition, message string, generation int64) { conditionTypes := []string{ diff --git a/internal/controllers/extension_controller.go b/internal/controllers/extension_controller.go index 04310164c..17a4f23d9 100644 --- a/internal/controllers/extension_controller.go +++ b/internal/controllers/extension_controller.go @@ -207,7 +207,7 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext return ctrl.Result{}, err } - mapAppStatusToInstalledCondition(existingTypedApp, ext, bundle.Image) + MapAppStatusToCondition(existingTypedApp, ext, bundle.Image) SetDeprecationStatusInExtension(ext, bundle) return ctrl.Result{}, nil @@ -231,30 +231,63 @@ func (r *ExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// TODO: follow up with mapping of all the available App statuses: https://github.com/carvel-dev/kapp-controller/blob/855063edee53315811a13ee8d5df1431ba258ede/pkg/apis/kappctrl/v1alpha1/status.go#L28-L35 -// mapAppStatusToInstalledCondition currently maps only the installed condition. -func mapAppStatusToInstalledCondition(existingApp *kappctrlv1alpha1.App, ext *ocv1alpha1.Extension, bundleImage string) { - appReady := findStatusCondition(existingApp.Status.GenericStatus.Conditions, kappctrlv1alpha1.ReconcileSucceeded) - if appReady == nil { - ext.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&ext.Status.Conditions, "install status unknown", ext.Generation) +// mapAppStatusToCondition maps the reconciling/deleting App conditions to the installed/deleting conditions on the Extension. +func MapAppStatusToCondition(existingApp *kappctrlv1alpha1.App, ext *ocv1alpha1.Extension, bundleImage string) { + if ext == nil { return } + var message string + ext.Status.InstalledBundleResource = "" - if appReady.Status != corev1.ConditionTrue { - ext.Status.InstalledBundleResource = "" - setInstalledStatusConditionFailed( - &ext.Status.Conditions, - appReady.Message, - ext.GetGeneration(), - ) - return + if existingApp != nil { + message = existingApp.Status.FriendlyDescription + if strings.Contains(message, "Error (see .status.usefulErrorMessage for details)") || len(message) == 0 { + message = existingApp.Status.UsefulErrorMessage + } + + orderedAppStatuses := []kappctrlv1alpha1.ConditionType{ + kappctrlv1alpha1.DeleteFailed, + kappctrlv1alpha1.Deleting, + kappctrlv1alpha1.ReconcileSucceeded, + kappctrlv1alpha1.ReconcileFailed, + kappctrlv1alpha1.Reconciling, + } + appStatusMapFn := map[kappctrlv1alpha1.ConditionType]func(*[]metav1.Condition, string, int64){ + kappctrlv1alpha1.DeleteFailed: setInstalledStatusConditionDeleteFailed, + kappctrlv1alpha1.Deleting: setInstalledStatusConditionDeleting, + kappctrlv1alpha1.ReconcileSucceeded: setInstalledStatusConditionSuccess, + kappctrlv1alpha1.ReconcileFailed: setInstalledStatusConditionFailed, + kappctrlv1alpha1.Reconciling: setInstalledStatusConditionUnknown, + } + for _, cond := range orderedAppStatuses { + if c := findStatusCondition(existingApp.Status.GenericStatus.Conditions, cond); c != nil && c.Status == corev1.ConditionTrue { + if len(message) == 0 { + message = c.Message + } + if c.Type == kappctrlv1alpha1.ReconcileSucceeded { + ext.Status.InstalledBundleResource = bundleImage + } + appStatusMapFn[cond](&ext.Status.Conditions, message, ext.Generation) + return + } + } + } + if len(message) == 0 { + message = "install status unknown" } + setInstalledStatusConditionUnknown(&ext.Status.Conditions, message, ext.Generation) + return +} - // InstalledBundleResource this should be converted into a slice as App allows fetching - // from multiple sources. - ext.Status.InstalledBundleResource = bundleImage - setInstalledStatusConditionSuccess(&ext.Status.Conditions, appReady.Message, ext.Generation) +// findStatusCondition finds the conditionType in conditions. +// TODO: suggest using upstream conditions to Carvel. +func findStatusCondition(conditions []kappctrlv1alpha1.Condition, conditionType kappctrlv1alpha1.ConditionType) *kappctrlv1alpha1.Condition { + for i := range conditions { + if conditions[i].Type == conditionType { + return &conditions[i] + } + } + return nil } // setDeprecationStatus will set the appropriate deprecation statuses for a Extension @@ -339,17 +372,6 @@ func SetDeprecationStatusInExtension(ext *ocv1alpha1.Extension, bundle *catalogm } } -// findStatusCondition finds the conditionType in conditions. -// TODO: suggest using upstream conditions to Carvel. -func findStatusCondition(conditions []kappctrlv1alpha1.Condition, conditionType kappctrlv1alpha1.ConditionType) *kappctrlv1alpha1.Condition { - for i := range conditions { - if conditions[i].Type == conditionType { - return &conditions[i] - } - } - return nil -} - func (r *ExtensionReconciler) ensureApp(ctx context.Context, desiredApp *unstructured.Unstructured) error { existingApp, err := r.existingAppUnstructured(ctx, desiredApp.GetName(), desiredApp.GetNamespace()) if client.IgnoreNotFound(err) != nil {