diff --git a/pkg/query/configuration/fluxobject_test.go b/pkg/query/configuration/fluxobject_test.go index ae7bce63b2..2b2a8f3155 100644 --- a/pkg/query/configuration/fluxobject_test.go +++ b/pkg/query/configuration/fluxobject_test.go @@ -6,15 +6,15 @@ import ( "github.com/fluxcd/helm-controller/api/v2beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) func TestStatusAndMessage(t *testing.T) { - tests := []struct { name string desiredStatus ObjectStatus desiredMessage string - obj FluxObject + obj client.Object }{ { name: "HelmRelease with Ready condition", diff --git a/pkg/query/configuration/objectkind.go b/pkg/query/configuration/objectkind.go index 69f00abdb1..fb57960ea9 100644 --- a/pkg/query/configuration/objectkind.go +++ b/pkg/query/configuration/objectkind.go @@ -66,9 +66,12 @@ type ObjectKind struct { type ObjectStatus string const ( - Success ObjectStatus = "Success" - Failed ObjectStatus = "Failed" - NoStatus ObjectStatus = "-" + Success ObjectStatus = "Success" + Failed ObjectStatus = "Failed" + Reconciling ObjectStatus = "Reconciling" + Suspended ObjectStatus = "Suspended" + PendingAction ObjectStatus = "PendingAction" + NoStatus ObjectStatus = "-" ) func (ok ObjectKind) String() string { @@ -101,11 +104,6 @@ func (o ObjectKind) Validate() error { return nil } -type FluxObject interface { - client.Object - GetConditions() []metav1.Condition -} - var ( HelmReleaseObjectKind = ObjectKind{ Gvk: helmv2beta1.GroupVersion.WithKind(helmv2beta1.HelmReleaseKind), @@ -348,15 +346,29 @@ func defaultFluxObjectStatusFunc(obj client.Object) ObjectStatus { return Failed } + if fo.GetSuspended() { + return Suspended + } + for _, c := range fo.GetConditions() { if ObjectStatus(c.Type) == NoStatus { return NoStatus } + if c.Type == "Ready" || c.Type == "Available" { if c.Status == "True" { return Success } + if c.Status == "Unknown" { + if c.Reason == "Progressing" { + return Reconciling + } + if c.Reason == "TerraformPlannedWithChanges" { + return PendingAction + } + } + return Failed } } @@ -380,35 +392,86 @@ func defaultFluxObjectMessageFunc(obj client.Object) string { return "" } -type AutomatedClusterDiscoveryAdaptor struct { +type FluxObject interface { client.Object + GetConditions() []metav1.Condition + GetSuspended() bool } -func (a *AutomatedClusterDiscoveryAdaptor) GetConditions() []metav1.Condition { - acd := a.Object.(*clusterreflectorv1alpha1.AutomatedClusterDiscovery) - return acd.Status.Conditions +type SuspendableAdapter struct { + client.Object } -func ToFluxObject(obj client.Object) (FluxObject, error) { - switch t := obj.(type) { +func (o *SuspendableAdapter) GetConditions() []metav1.Condition { + switch t := o.Object.(type) { case *helmv2beta1.HelmRelease: - return t, nil + return t.GetConditions() case *kustomizev1.Kustomization: - return t, nil + return t.GetConditions() case *sourcev1beta2.HelmRepository: - return t, nil + return t.GetConditions() case *sourcev1beta2.HelmChart: - return t, nil + return t.GetConditions() case *sourcev1beta2.Bucket: - return t, nil + return t.GetConditions() case *sourcev1.GitRepository: - return t, nil + return t.GetConditions() case *sourcev1beta2.OCIRepository: - return t, nil + return t.GetConditions() case *gitopssets.GitOpsSet: - return t, nil + return t.GetConditions() + default: + return nil + } +} + +func (a *SuspendableAdapter) GetSuspended() bool { + switch t := a.Object.(type) { + case *helmv2beta1.HelmRelease: + return t.Spec.Suspend + case *kustomizev1.Kustomization: + return t.Spec.Suspend + case *sourcev1beta2.HelmRepository: + return t.Spec.Suspend + case *sourcev1beta2.HelmChart: + return t.Spec.Suspend + case *sourcev1beta2.Bucket: + return t.Spec.Suspend + case *sourcev1.GitRepository: + return t.Spec.Suspend + case *sourcev1beta2.OCIRepository: + return t.Spec.Suspend + case *gitopssets.GitOpsSet: + return t.Spec.Suspend + case *clusterreflectorv1alpha1.AutomatedClusterDiscovery: + return t.Spec.Suspend + default: + return false + } +} + +type AutomatedClusterDiscoveryAdapter struct { + client.Object +} + +func (a *AutomatedClusterDiscoveryAdapter) GetConditions() []metav1.Condition { + acd := a.Object.(*clusterreflectorv1alpha1.AutomatedClusterDiscovery) + return acd.Status.Conditions +} + +func ToFluxObject(obj client.Object) (FluxObject, error) { + switch t := obj.(type) { + case *helmv2beta1.HelmRelease, + *kustomizev1.Kustomization, + *sourcev1beta2.HelmRepository, + *sourcev1beta2.HelmChart, + *sourcev1beta2.Bucket, + *sourcev1.GitRepository, + *sourcev1beta2.OCIRepository, + *gitopssets.GitOpsSet: + return &SuspendableAdapter{Object: t}, nil case *clusterreflectorv1alpha1.AutomatedClusterDiscovery: - return &AutomatedClusterDiscoveryAdaptor{Object: t}, nil + return &SuspendableAdapter{Object: &AutomatedClusterDiscoveryAdapter{Object: t}}, nil } return nil, fmt.Errorf("unknown object type: %T", obj) diff --git a/pkg/query/configuration/objectkind_test.go b/pkg/query/configuration/objectkind_test.go index 1ee7c189b4..38eb5dda1f 100644 --- a/pkg/query/configuration/objectkind_test.go +++ b/pkg/query/configuration/objectkind_test.go @@ -15,7 +15,6 @@ import ( // like being in the expected flux api version. For example, flux v1 available // kinds should be using v1 api version func TestObjectsKinds(t *testing.T) { - g := NewWithT(t) t.Run("should contain v1 kustomizations", func(t *testing.T) { diff --git a/ui/src/components/Explorer/ExplorerTable.tsx b/ui/src/components/Explorer/ExplorerTable.tsx index d31bb443a9..f5586dc3dc 100644 --- a/ui/src/components/Explorer/ExplorerTable.tsx +++ b/ui/src/components/Explorer/ExplorerTable.tsx @@ -93,17 +93,9 @@ export const defaultExplorerFields: ExplorerField[] = [ - {o?.status} ); @@ -136,6 +128,57 @@ export function addFieldsWithIndex( return newFields; } +enum ObjectStatus { + Success = 'Success', + Failed = 'Failed', + Reconciling = 'Reconciling', + Suspended = 'Suspended', + PendingAction = 'PendingAction', + NoStatus = '-' +} + +type IndicatorInfo = { + color: string; + type: IconType; +}; + +const getIndicatorInfo = ( + status: string | undefined, +): IndicatorInfo => { + switch (status) { + case ObjectStatus.Success: + return { + color: 'successOriginal', + type: IconType.SuccessIcon, + }; + case ObjectStatus.Reconciling: + return { + color: 'primary', + type: IconType.ReconcileIcon, + }; + case ObjectStatus.Suspended: + return { + color: 'feedbackOriginal', + type: IconType.SuspendedIcon, + }; + case ObjectStatus.PendingAction: + return { + color: 'feedbackOriginal', + type: IconType.PendingActionIcon, + }; + case ObjectStatus.NoStatus: + return { + color: 'neutral20', + type: IconType.RemoveCircleIcon, + }; + default: + return { + color: 'alertOriginal', + type: IconType.ErrorIcon, + }; + } +}; + function ExplorerTable({ className, rows,