From 139a87d69cc6ffe0538185b3aabff8f90c23fa3c Mon Sep 17 00:00:00 2001 From: Ankita Thomas Date: Mon, 25 Mar 2024 10:20:40 -0400 Subject: [PATCH] add Progressing condition Signed-off-by: Ankita Thomas --- api/v1alpha1/clusterextension_types.go | 22 +- api/v1alpha1/clusterextension_types_test.go | 2 +- internal/conditionsets/conditionsets.go | 3 + internal/controllers/common_controller.go | 62 ++-- internal/controllers/extension_controller.go | 83 ++--- .../controllers/extension_controller_test.go | 306 ------------------ 6 files changed, 87 insertions(+), 391 deletions(-) diff --git a/api/v1alpha1/clusterextension_types.go b/api/v1alpha1/clusterextension_types.go index 5af25e0b9..cfc8fc09d 100644 --- a/api/v1alpha1/clusterextension_types.go +++ b/api/v1alpha1/clusterextension_types.go @@ -75,10 +75,16 @@ type ClusterExtensionSpec struct { const ( // TODO(user): add more Types, here and into init() + // TypeInstalled indicates whether the install for the bundle + // referenced in the status was completed. + // It does not indicate whether the App created by the bundle is healthy. TypeInstalled = "Installed" TypeResolved = "Resolved" + // TypeProgressing indicates whether operator-controller is + // reconciling, installing, updating or deleting an extension. + TypeProgressing = "Progressing" // TypeDeprecated is a rollup condition that is present when - // any of the deprecated conditions are present. + // any of the deprecated conditions are present for the resolved bundle. TypeDeprecated = "Deprecated" TypePackageDeprecated = "PackageDeprecated" TypeChannelDeprecated = "ChannelDeprecated" @@ -95,6 +101,11 @@ const ( ReasonDeprecated = "Deprecated" ReasonDeleteFailed = "DeleteFailed" ReasonDeleting = "Deleting" + ReasonUnknown = "Unknown" + ReasonPending = "Pending" + ReasonReconcileFailed = "ReconcileFailed" + ReasonProgressing = "Progressing" + ReasonFailed = "Failed" ) func init() { @@ -120,7 +131,16 @@ func init() { ReasonDeprecated, ReasonDeleteFailed, ReasonDeleting, + ReasonUnknown, + ReasonPending, + ReasonReconcileFailed, + ReasonProgressing, + ReasonFailed, ) + + conditionsets.ExtensionConditionTypes = []string{ + TypeProgressing, + } } // ClusterExtensionStatus defines the observed state of ClusterExtension diff --git a/api/v1alpha1/clusterextension_types_test.go b/api/v1alpha1/clusterextension_types_test.go index ce151e3b8..e5dfb5fbb 100644 --- a/api/v1alpha1/clusterextension_types_test.go +++ b/api/v1alpha1/clusterextension_types_test.go @@ -22,7 +22,7 @@ func TestClusterExtensionTypeRegistration(t *testing.T) { } for _, tt := range types { - if !slices.Contains(conditionsets.ConditionTypes, tt) { + if !slices.Contains(conditionsets.ConditionTypes, tt) && !slices.Contains(conditionsets.ExtensionConditionTypes, tt) { t.Errorf("append Type%s to conditionsets.ConditionTypes in this package's init function", tt) } } diff --git a/internal/conditionsets/conditionsets.go b/internal/conditionsets/conditionsets.go index fa4087148..27b08e20a 100644 --- a/internal/conditionsets/conditionsets.go +++ b/internal/conditionsets/conditionsets.go @@ -22,3 +22,6 @@ package conditionsets // NOTE: These are populated by init() in api/v1alpha1/clusterextension_types.go var ConditionTypes []string var ConditionReasons []string + +// This is the set of Extension exclusive condition Types. +var ExtensionConditionTypes []string diff --git a/internal/controllers/common_controller.go b/internal/controllers/common_controller.go index 2b2fcc743..71f601cfd 100644 --- a/internal/controllers/common_controller.go +++ b/internal/controllers/common_controller.go @@ -44,17 +44,6 @@ func setResolvedStatusConditionSuccess(conditions *[]metav1.Condition, message s }) } -// setInstalledStatusConditionUnknown sets the installed status condition to unknown. -func setInstalledStatusConditionUnknown(conditions *[]metav1.Condition, message string, generation int64) { - apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: message, - ObservedGeneration: generation, - }) -} - // setResolvedStatusConditionFailed sets the resolved status condition to failed. func setResolvedStatusConditionFailed(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ @@ -66,17 +55,6 @@ func setResolvedStatusConditionFailed(conditions *[]metav1.Condition, message st }) } -// setResolvedStatusConditionUnknown sets the resolved status condition to unknown. -func setResolvedStatusConditionUnknown(conditions *[]metav1.Condition, message string, generation int64) { - apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: ocv1alpha1.TypeResolved, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonResolutionUnknown, - Message: message, - ObservedGeneration: generation, - }) -} - // setInstalledStatusConditionSuccess sets the installed status condition to success. func setInstalledStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ @@ -99,29 +77,51 @@ 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) { +// setInstalledStatusConditionUnknown sets the installed status condition to unknown. +func setInstalledStatusConditionUnknown(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ Type: ocv1alpha1.TypeInstalled, Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonDeleting, + Reason: ocv1alpha1.ReasonInstallationStatusUnknown, Message: message, ObservedGeneration: generation, }) } -// setInstalledStatusConditionDeleteFailed sets the installed status condition to unknown for failed deletes. -func setInstalledStatusConditionDeleteFailed(conditions *[]metav1.Condition, message string, generation int64) { +// setProgressingStatusConditionResolutionFailed sets the progressing status condition to false for failed bundle resolutions. +func setProgressingStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) { apimeta.SetStatusCondition(conditions, metav1.Condition{ - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonDeleteFailed, + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionFalse, + Reason: ocv1alpha1.ReasonSuccess, + Message: message, + ObservedGeneration: generation, + }) +} + +// setProgressingStatusConditionUnsupportedOrInvalidBundle sets the progressing status condition to false for unsupported/invalid bundles. +func setProgressingStatusConditionFailed(conditions *[]metav1.Condition, message string, generation int64) { + apimeta.SetStatusCondition(conditions, metav1.Condition{ + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionTrue, + Reason: ocv1alpha1.ReasonFailed, + Message: message, + ObservedGeneration: generation, + }) +} + +// setProgressingStatusConditionReconciling sets the progressing status condition to true for an app being reconciled. +func setProgressingStatusConditionProgressing(conditions *[]metav1.Condition, message string, generation int64) { + apimeta.SetStatusCondition(conditions, metav1.Condition{ + Type: ocv1alpha1.TypeProgressing, + Status: metav1.ConditionTrue, + Reason: ocv1alpha1.ReasonProgressing, Message: message, ObservedGeneration: generation, }) } -// setDEprecationStatusesUnknown sets the deprecation status conditions to unknown. +// setDeprecationStatusesUnknown sets the deprecation status conditions to unknown. func setDeprecationStatusesUnknown(conditions *[]metav1.Condition, message string, generation int64) { conditionTypes := []string{ ocv1alpha1.TypeDeprecated, diff --git a/internal/controllers/extension_controller.go b/internal/controllers/extension_controller.go index 815ee2d49..6933b0b41 100644 --- a/internal/controllers/extension_controller.go +++ b/internal/controllers/extension_controller.go @@ -129,10 +129,8 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext // hasn't been attempted yet, due to the spec being invalid. ext.Status.InstalledBundleResource = "" setInstalledStatusConditionUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) - // Set the TypeResolved condition to Unknown to indicate that the resolution - // hasn't been attempted yet, due to the spec being invalid. ext.Status.ResolvedBundleResource = "" - setResolvedStatusConditionUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) + setResolvedStatusConditionFailed(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) setDeprecationStatusesUnknown(&ext.Status.Conditions, "extension feature is disabled", ext.GetGeneration()) return ctrl.Result{}, nil @@ -147,10 +145,9 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext if !r.HasKappApis { ext.Status.InstalledBundleResource = "" - setInstalledStatusConditionFailed(&ext.Status.Conditions, errkappAPIUnavailable.Error(), ext.GetGeneration()) - + setInstalledStatusConditionUnknown(&ext.Status.Conditions, errkappAPIUnavailable.Error(), ext.GetGeneration()) ext.Status.ResolvedBundleResource = "" - setResolvedStatusConditionUnknown(&ext.Status.Conditions, "kapp apis are unavailable", ext.GetGeneration()) + setResolvedStatusConditionFailed(&ext.Status.Conditions, "kapp apis are unavailable", ext.GetGeneration()) setDeprecationStatusesUnknown(&ext.Status.Conditions, "kapp apis are unavailable", ext.GetGeneration()) return ctrl.Result{}, errkappAPIUnavailable @@ -159,33 +156,27 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext // TODO: Improve the resolution logic. bundle, err := r.resolve(ctx, *ext) if err != nil { - ext.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&ext.Status.Conditions, "installation has not been attempted as resolution failed", ext.GetGeneration()) ext.Status.ResolvedBundleResource = "" - setResolvedStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) - setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as resolution failed", ext.GetGeneration()) + setResolvedStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("resolution failed: %v", err), ext.GetGeneration()) return ctrl.Result{}, err } // Now we can set the Resolved Condition, and the resolvedBundleSource field to the bundle.Image value. ext.Status.ResolvedBundleResource = bundle.Image - setResolvedStatusConditionSuccess(&ext.Status.Conditions, fmt.Sprintf("resolved to %q", bundle.Image), ext.GetGeneration()) + + // Populate the deprecation status using the resolved bundle + SetDeprecationStatusInExtension(ext, bundle) mediaType, err := bundle.MediaType() if err != nil { - setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) - setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration()) + setProgressingStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("failed to read bundle mediaType: %v", err), ext.GetGeneration()) return ctrl.Result{}, err } // TODO: this needs to include the registryV1 bundle option. As of this PR, this only supports direct // installation of a set of manifests. if mediaType != catalogmetadata.MediaTypePlain { - // Set the TypeInstalled condition to Unknown to indicate that the resolution - // hasn't been attempted yet, due to the spec being invalid. - ext.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&ext.Status.Conditions, fmt.Sprintf("bundle type %s not supported currently", mediaType), ext.GetGeneration()) - setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration()) + setProgressingStatusConditionFailed(&ext.Status.Conditions, fmt.Sprintf("bundle type %s not supported currently", mediaType), ext.GetGeneration()) return ctrl.Result{}, nil } @@ -194,6 +185,7 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext // originally Reason: ocv1alpha1.ReasonInstallationFailed ext.Status.InstalledBundleResource = "" setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) + setProgressingStatusConditionFailed(&ext.Status.Conditions, "app install failed", ext.GetGeneration()) return ctrl.Result{}, err } @@ -202,13 +194,16 @@ func (r *ExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alpha1.Ext if err := runtime.DefaultUnstructuredConverter.FromUnstructured(app.UnstructuredContent(), existingTypedApp); err != nil { // originally Reason: ocv1alpha1.ReasonInstallationStatusUnknown ext.Status.InstalledBundleResource = "" - setInstalledStatusConditionUnknown(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) - setDeprecationStatusesUnknown(&ext.Status.Conditions, "deprecation checks have not been attempted as installation has failed", ext.GetGeneration()) + setInstalledStatusConditionFailed(&ext.Status.Conditions, err.Error(), ext.GetGeneration()) + setProgressingStatusConditionFailed(&ext.Status.Conditions, "app install failed", ext.GetGeneration()) return ctrl.Result{}, err } - MapAppStatusToCondition(existingTypedApp, ext, bundle.Image) - SetDeprecationStatusInExtension(ext, bundle) + ext.Status.InstalledBundleResource = bundle.Image + setInstalledStatusConditionSuccess(&ext.Status.Conditions, "app install succeeded", ext.GetGeneration()) + + // TODO: add conditions to determine extension health + mapAppStatusToCondition(existingTypedApp, ext) return ctrl.Result{}, nil } @@ -232,52 +227,36 @@ func (r *ExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error { } // 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 - } - message := "install status unknown" - ext.Status.InstalledBundleResource = "" - - if existingApp == nil { - setInstalledStatusConditionUnknown(&ext.Status.Conditions, message, ext.Generation) +func mapAppStatusToCondition(existingApp *kappctrlv1alpha1.App, ext *ocv1alpha1.Extension) { + // Note: App.Status.Inspect errors are never surfaced to App conditions, so are currently ignored when determining App status. + if ext == nil || existingApp == nil { return } - message = existingApp.Status.FriendlyDescription - if strings.Contains(message, "Error (see .status.usefulErrorMessage for details)") || len(message) == 0 { + message := existingApp.Status.FriendlyDescription + if len(message) == 0 || strings.Contains(message, "Error (see .status.usefulErrorMessage for details)") { 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, + kappctrlv1alpha1.Deleting: setProgressingStatusConditionProgressing, + kappctrlv1alpha1.Reconciling: setProgressingStatusConditionProgressing, + kappctrlv1alpha1.DeleteFailed: setProgressingStatusConditionFailed, + kappctrlv1alpha1.ReconcileFailed: setProgressingStatusConditionFailed, + kappctrlv1alpha1.ReconcileSucceeded: setProgressingStatusConditionSuccess, } - for _, cond := range orderedAppStatuses { + for cond := range appStatusMapFn { 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) + appStatusMapFn[cond](&ext.Status.Conditions, fmt.Sprintf("App %s: %s", c.Type, message), ext.Generation) return } } if len(message) == 0 { - message = "install status unknown" + message = "waiting for app" } - setInstalledStatusConditionUnknown(&ext.Status.Conditions, message, ext.Generation) + setProgressingStatusConditionProgressing(&ext.Status.Conditions, message, ext.Generation) } // findStatusCondition finds the conditionType in conditions. diff --git a/internal/controllers/extension_controller_test.go b/internal/controllers/extension_controller_test.go index a23c82dd9..0d59995a9 100644 --- a/internal/controllers/extension_controller_test.go +++ b/internal/controllers/extension_controller_test.go @@ -7,8 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - kappctrlv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/kappctrl/v1alpha1" - corev1 "k8s.io/api/core/v1" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -16,8 +14,6 @@ import ( featuregatetesting "k8s.io/component-base/featuregate/testing" ctrl "sigs.k8s.io/controller-runtime" - "github.com/operator-framework/operator-controller/internal/controllers" - ocv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" "github.com/operator-framework/operator-controller/internal/conditionsets" "github.com/operator-framework/operator-controller/pkg/features" @@ -114,305 +110,3 @@ func verifyExtensionConditionsInvariants(t *testing.T, ext *ocv1alpha1.Extension require.Equal(t, ext.GetGeneration(), cond.ObservedGeneration) } } - -func TestMapAppCondtitionToStatus(t *testing.T) { - testCases := []struct { - name string - app *kappctrlv1alpha1.App - ext *ocv1alpha1.Extension - bundleImage string - expected *ocv1alpha1.Extension - }{ - { - name: "preserve existing conditions on extension while reconciling", - app: &kappctrlv1alpha1.App{ - Status: kappctrlv1alpha1.AppStatus{ - GenericStatus: kappctrlv1alpha1.GenericStatus{ - // ObservedGeneration: 0, - FriendlyDescription: "Paused/Cancelled", - }, - }, - }, - ext: &ocv1alpha1.Extension{ - ObjectMeta: metav1.ObjectMeta{ - Generation: 1, - }, - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, - Status: metav1.ConditionFalse, - ObservedGeneration: 1, - }, - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: "install status unknown", - ObservedGeneration: 1, - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - ObjectMeta: metav1.ObjectMeta{ - Generation: 1, - }, - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeDeprecated, - Reason: ocv1alpha1.ReasonDeprecated, - Status: metav1.ConditionFalse, - ObservedGeneration: 1, - }, - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: "Paused/Cancelled", - ObservedGeneration: 1, - }, - }, - }, - }, - }, - - { - name: "update installedBundleResource on successful reconcile", - bundleImage: "test-bundle", - app: &kappctrlv1alpha1.App{ - Status: kappctrlv1alpha1.AppStatus{ - GenericStatus: kappctrlv1alpha1.GenericStatus{ - Conditions: []kappctrlv1alpha1.Condition{{ - Type: kappctrlv1alpha1.ReconcileSucceeded, - Status: corev1.ConditionTrue, - // Reason: "", - Message: "Reconcile Succeeded", - }}, - }, - }, - }, - ext: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - InstalledBundleResource: "test-bundle", - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSuccess, - Message: "Reconcile Succeeded", - }, - }, - }, - }, - }, - { - name: "remove installedBundleResource when not successful reconcile", - bundleImage: "test-bundle", - app: &kappctrlv1alpha1.App{ - Status: kappctrlv1alpha1.AppStatus{ - GenericStatus: kappctrlv1alpha1.GenericStatus{ - Conditions: []kappctrlv1alpha1.Condition{{ - Type: kappctrlv1alpha1.Reconciling, - Status: corev1.ConditionTrue, - Message: "Reconciling", - }}, - FriendlyDescription: "Reconciling", - }, - }, - }, - ext: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - InstalledBundleResource: "test-bundle", - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSuccess, - Message: "Success", - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: "Reconciling", - }, - }, - }, - }, - }, - { - name: "show UsefulErrorMessage when referenced by FriendlyErrorMessage", - app: &kappctrlv1alpha1.App{ - Status: kappctrlv1alpha1.AppStatus{ - GenericStatus: kappctrlv1alpha1.GenericStatus{ - Conditions: []kappctrlv1alpha1.Condition{{ - Type: kappctrlv1alpha1.ReconcileFailed, - Status: corev1.ConditionTrue, - Message: "Reconcile Failed", - }}, - FriendlyDescription: "Reconcile Error (see .status.usefulErrorMessage for details)", - UsefulErrorMessage: "Deployment Error: Exit Status 1", - }, - }, - }, - ext: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - InstalledBundleResource: "test-bundle", - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionTrue, - Reason: ocv1alpha1.ReasonSuccess, - Message: "Success", - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionFalse, - Reason: ocv1alpha1.ReasonInstallationFailed, - Message: "Deployment Error: Exit Status 1", - }, - }, - }, - }, - }, - { - name: "show FriendlyErrorMessage when present", - app: &kappctrlv1alpha1.App{ - Status: kappctrlv1alpha1.AppStatus{ - GenericStatus: kappctrlv1alpha1.GenericStatus{ - Conditions: []kappctrlv1alpha1.Condition{{ - Type: kappctrlv1alpha1.DeleteFailed, - Status: corev1.ConditionTrue, - Message: "Delete Failed", - }}, - FriendlyDescription: "Delete Error: Timed out", - UsefulErrorMessage: "Timed out after 5m", - }, - }, - }, - ext: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonDeleting, - Message: "Deleting", - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonDeleteFailed, - Message: "Delete Error: Timed out", - }, - }, - }, - }, - }, - { - name: "update status without message when none exist on App", - app: &kappctrlv1alpha1.App{ - Status: kappctrlv1alpha1.AppStatus{ - GenericStatus: kappctrlv1alpha1.GenericStatus{ - Conditions: []kappctrlv1alpha1.Condition{{ - Type: kappctrlv1alpha1.Deleting, - Status: corev1.ConditionTrue, - }}, - }, - }, - }, - ext: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: "Reconciling", - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonDeleting, - }, - }, - }, - }, - }, - { - name: "set default installed condition for empty app status", - app: &kappctrlv1alpha1.App{}, - ext: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: "Reconciling", - }, - }, - }, - }, - expected: &ocv1alpha1.Extension{ - Status: ocv1alpha1.ExtensionStatus{ - Conditions: []metav1.Condition{ - { - Type: ocv1alpha1.TypeInstalled, - Status: metav1.ConditionUnknown, - Reason: ocv1alpha1.ReasonInstallationStatusUnknown, - Message: "install status unknown", - }, - }, - }, - }, - }, - } - - for _, tt := range testCases { - controllers.MapAppStatusToCondition(tt.app, tt.ext, tt.bundleImage) - for i := range tt.ext.Status.Conditions { - //unset transition time for comparison - tt.ext.Status.Conditions[i].LastTransitionTime = metav1.Time{} - } - assert.Equal(t, tt.expected, tt.ext, tt.name) - } -}