diff --git a/charts/core/Chart.yaml b/charts/core/Chart.yaml index 3c9fa29..8ccd003 100644 --- a/charts/core/Chart.yaml +++ b/charts/core/Chart.yaml @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn Core type: application -version: v1.1.0-rc5 -appVersion: v1.1.0-rc5 +version: v1.1.0-rc6 +appVersion: v1.1.0-rc6 icon: https://assets.unikorn-cloud.org/images/logos/dark-on-light/icon.svg diff --git a/pkg/apis/unikorn/v1alpha1/types.go b/pkg/apis/unikorn/v1alpha1/types.go index 7473dc1..8025cab 100644 --- a/pkg/apis/unikorn/v1alpha1/types.go +++ b/pkg/apis/unikorn/v1alpha1/types.go @@ -283,7 +283,7 @@ const ( // ConditionReason defines the possible reasons of a resource // condition. These are generic and may be used by any condition. -// +kubebuilder:validation:Enum=Provisioning;Provisioned;Cancelled;Errored;Deprovisioning;Deprovisioned;Healthy;Degraded +// +kubebuilder:validation:Enum=Provisioning;Provisioned;Cancelled;Errored;Deprovisioning;Deprovisioned;Unknown;Healthy;Degraded type ConditionReason string // Condition reasons for ConditionAvailable. @@ -313,6 +313,8 @@ const ( // Condition reasons for ConditionHealthy. const ( + // ConditionReasonUnknown means the health status cannot be derived. + ConditionReasonUnknown ConditionReason = "Unknown" // ConditionReasonHealthy means all subresources associated with the // resource are in a healthy state. ConditionReasonHealthy ConditionReason = "Healthy" diff --git a/pkg/cd/argocd/driver.go b/pkg/cd/argocd/driver.go index 87d8f04..ae0df74 100644 --- a/pkg/cd/argocd/driver.go +++ b/pkg/cd/argocd/driver.go @@ -179,6 +179,35 @@ func convertApplicationList(in *argoprojv1.ApplicationList) map[*cd.ResourceIden return out } +// GetHealthStatus returns an overall health status of all applications +// referenced by the resource identifier. +func (d *Driver) GetHealthStatus(ctx context.Context, id *cd.ResourceIdentifier) (cd.HealthStatus, error) { + options := &client.ListOptions{ + Namespace: namespace, + LabelSelector: labels.SelectorFromSet(applicationLabelsForOwningResource(id)), + } + + var resources argoprojv1.ApplicationList + + if err := d.client.List(ctx, &resources, options); err != nil { + return cd.HealthStatusUnknown, err + } + + for i := range resources.Items { + application := &resources.Items[i] + + if application.Status.Health == nil { + return cd.HealthStatusUnknown, nil + } + + if application.Status.Health.Status != argoprojv1.Healthy { + return cd.HealthStatusDegraded, nil + } + } + + return cd.HealthStatusHealthy, nil +} + // ListHelmApplications gets all applications that match the resource identifier. func (d *Driver) ListHelmApplications(ctx context.Context, id *cd.ResourceIdentifier) (map[*cd.ResourceIdentifier]*cd.HelmApplication, error) { options := &client.ListOptions{ diff --git a/pkg/cd/interfaces.go b/pkg/cd/interfaces.go index 2e55df2..93b127d 100644 --- a/pkg/cd/interfaces.go +++ b/pkg/cd/interfaces.go @@ -34,6 +34,10 @@ type Driver interface { // your hack. Kind() DriverKind + // GetHealthStatus returns an overall health status of all applications + // referenced by the resource identifier. + GetHealthStatus(ctx context.Context, id *ResourceIdentifier) (HealthStatus, error) + // ListHelmApplications gets all applications that match the resource identifier. ListHelmApplications(ctx context.Context, id *ResourceIdentifier) (map[*ResourceIdentifier]*HelmApplication, error) diff --git a/pkg/cd/mock/interfaces.go b/pkg/cd/mock/interfaces.go index 8edb130..14a29a6 100644 --- a/pkg/cd/mock/interfaces.go +++ b/pkg/cd/mock/interfaces.go @@ -95,6 +95,21 @@ func (mr *MockDriverMockRecorder) DeleteHelmApplication(ctx, id, backgroundDelet return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteHelmApplication", reflect.TypeOf((*MockDriver)(nil).DeleteHelmApplication), ctx, id, backgroundDelete) } +// GetHealthStatus mocks base method. +func (m *MockDriver) GetHealthStatus(ctx context.Context, id *cd.ResourceIdentifier) (cd.HealthStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHealthStatus", ctx, id) + ret0, _ := ret[0].(cd.HealthStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetHealthStatus indicates an expected call of GetHealthStatus. +func (mr *MockDriverMockRecorder) GetHealthStatus(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHealthStatus", reflect.TypeOf((*MockDriver)(nil).GetHealthStatus), ctx, id) +} + // Kind mocks base method. func (m *MockDriver) Kind() cd.DriverKind { m.ctrl.T.Helper() diff --git a/pkg/cd/types.go b/pkg/cd/types.go index cb19893..18a8665 100644 --- a/pkg/cd/types.go +++ b/pkg/cd/types.go @@ -147,3 +147,16 @@ type Cluster struct { // import that cluster as a region. Prefix string } + +// HealthStatus is used to describe the health of the application. +type HealthStatus string + +const ( + // HealthStatusUnknown means the application health cannot be derived. + HealthStatusUnknown HealthStatus = "unknown" + // HealthStatusHealthy means everything is as expected. + HealthStatusHealthy HealthStatus = "healthy" + // HealthStatusDegraded means the application may still function + // but is in a degraded state. + HealthStatusDegraded HealthStatus = "degraded" +)