diff --git a/pkg/health/health_test.go b/pkg/health/health_test.go index 5b4ef6b..8f1eca8 100644 --- a/pkg/health/health_test.go +++ b/pkg/health/health_test.go @@ -18,6 +18,15 @@ import ( "sigs.k8s.io/yaml" ) +func assertAppHealthMsg(t *testing.T, yamlPath string, expectedStatus health.HealthStatusCode, expectedHealth health.Health, expectedReady bool, expectedMsg string) { + health := getHealthStatus(yamlPath, t, nil) + assert.NotNil(t, health) + assert.Equal(t, expectedHealth, health.Health) + assert.Equal(t, expectedReady, health.Ready) + assert.Equal(t, expectedStatus, health.Status) + assert.Equal(t, expectedMsg, health.Message) +} + func assertAppHealth(t *testing.T, yamlPath string, expectedStatus health.HealthStatusCode, expectedHealth health.Health, expectedReady bool) { health := getHealthStatus(yamlPath, t, nil) assert.NotNil(t, health) @@ -62,8 +71,11 @@ func getHealthStatus(yamlPath string, t *testing.T, overwrites map[string]string } func TestCrossplane(t *testing.T) { - assertAppHealth(t, "./testdata/crossplane.yaml", "ApplyFailure", health.HealthWarning, true) - assertAppHealth(t, "./testdata/crossplane-healthy.yaml", "Success", health.HealthHealthy, true) + assertAppHealthMsg(t, "./testdata/crossplane-apply-failure.yaml", "ApplyFailure", health.HealthWarning, true, "apply failed: an existing `high_availability.0.standby_availability_zone` can only be changed when exchanged with the zone specified in `zone`: ") + assertAppHealthMsg(t, "./testdata/crossplane-healthy.yaml", "ReconcileSuccess", health.HealthHealthy, true, "") + assertAppHealthMsg(t, "./testdata/crossplane-installed.yaml", "ActivePackageRevision", health.HealthHealthy, true, "") + assertAppHealthMsg(t, "./testdata/crossplane-provider-revision.yaml", "HealthyPackageRevision", health.HealthHealthy, true, "") + assertAppHealthMsg(t, "./testdata/crossplane-reconcile-error.yaml", "ReconcileError", health.HealthUnhealthy, true, "observe failed: cannot run plan: plan failed: Instance cannot be destroyed: Resource azurerm_kubernetes_cluster_node_pool.prodeu01 has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.") } func TestNamespace(t *testing.T) { diff --git a/pkg/health/status.go b/pkg/health/status.go index 7b00c25..0dd065b 100644 --- a/pkg/health/status.go +++ b/pkg/health/status.go @@ -115,12 +115,13 @@ func (mapped *OnCondition) Apply(health *HealthStatus, c *metav1.Condition) { if mapped.Status != "" { if health.Status == "" || mapped.Order >= health.order { health.Status = mapped.Status + health.order = mapped.Order } } else if c.Reason != "" { if health.Status == "" || mapped.Order >= health.order { health.Status = HealthStatusCode(c.Reason) + health.order = mapped.Order } - } if mapped.Message && c.Message != "" { @@ -170,6 +171,7 @@ func (mapped *Condition) Apply(health *HealthStatus, c *metav1.Condition) { } else if c.Status == metav1.ConditionUnknown && mapped.OnUnknown != nil { mapped.OnUnknown.Apply(health, c) } + if reason, ok := mapped.Reasons[c.Reason]; ok { reason.Apply(health, c) } diff --git a/pkg/health/statusMap.yaml b/pkg/health/statusMap.yaml index 34a84e6..7d5e464 100644 --- a/pkg/health/statusMap.yaml +++ b/pkg/health/statusMap.yaml @@ -182,7 +182,20 @@ image.toolkit.fluxcd.io/v1beta2/ImageUpdateAutomation: *flux # Not an actual kind. crossplane.io: conditions: + Healthy: + reasons: + HealthyPackageRevision: + health: healthy + message: true + ready: true + Installed: + reasons: + ActivePackageRevision: + health: healthy + message: true + ready: true Ready: + order: 2 reasons: Available: ready: true @@ -197,7 +210,7 @@ crossplane.io: health: unknown ready: false AsyncOperation: - order: -1 + order: 1 reasons: Finished: order: -1 @@ -207,14 +220,22 @@ crossplane.io: order: -1 ready: false LastAsyncOperation: + order: 3 onFalse: - ready: false + order: 5 + message: true health: warning Synced: + order: 4 reasons: ReconcileSuccess: ready: true ReconcileError: health: unhealthy + message: true ReconcilePaused: ready: false + onFalse: + order: 6 + message: true + health: warning diff --git a/pkg/health/testdata/crossplane.yaml b/pkg/health/testdata/crossplane-apply-failure.yaml similarity index 100% rename from pkg/health/testdata/crossplane.yaml rename to pkg/health/testdata/crossplane-apply-failure.yaml diff --git a/pkg/health/testdata/crossplane-installed.yaml b/pkg/health/testdata/crossplane-installed.yaml new file mode 100644 index 0000000..87341e5 --- /dev/null +++ b/pkg/health/testdata/crossplane-installed.yaml @@ -0,0 +1,28 @@ +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + uid: a84bcf77-4abc-4798-8e99-76c75964424f + name: provider-azure-authorization + labels: + kustomize.toolkit.fluxcd.io/name: common + kustomize.toolkit.fluxcd.io/namespace: flux-system + creationTimestamp: 2023-11-12T15:50:07Z +spec: + package: xpkg.upbound.io/upbound/provider-azure-authorization:v0.38.2 + packagePullPolicy: IfNotPresent + controllerConfigRef: + name: provider-azure-family-config + revisionHistoryLimit: 1 + revisionActivationPolicy: Automatic + skipDependencyResolution: false + ignoreCrossplaneConstraints: false +status: + conditions: + - type: Healthy + reason: HealthyPackageRevision + status: "True" + - type: Installed + reason: ActivePackageRevision + status: "True" + currentRevision: provider-azure-authorization-29b8a8ac7b22 + currentIdentifier: xpkg.upbound.io/upbound/provider-azure-authorization:v0.38.2 diff --git a/pkg/health/testdata/crossplane-provider-revision.yaml b/pkg/health/testdata/crossplane-provider-revision.yaml new file mode 100644 index 0000000..151e998 --- /dev/null +++ b/pkg/health/testdata/crossplane-provider-revision.yaml @@ -0,0 +1,58 @@ +apiVersion: pkg.crossplane.io/v1 +kind: ProviderRevision +metadata: + uid: b42bf5da-9dad-4082-a682-46de82bc996e + name: provider-azure-authorization-29b8a8ac7b22 + labels: + pkg.crossplane.io/package: provider-azure-authorization + pkg.crossplane.io/provider-family: provider-family-azure + finalizers: + - revision.pkg.crossplane.io + annotations: + auth.upbound.io/group: azure.upbound.io + meta.crossplane.io/readme: > + Provider Azure is a Crossplane provider for [Microsoft + Azure](https://azure.microsoft.com) + + developed and supported by Upbound. + + Available resources and their fields can be found in the [Upbound + + Marketplace](https://marketplace.upbound.io/providers/upbound/provider-azure). + + If you encounter an issue please reach out on support@upbound.io email + + address. This is a subpackage for the authorization API group. + meta.crossplane.io/source: github.com/upbound/provider-azure + meta.crossplane.io/maintainer: Upbound + meta.crossplane.io/description: | + Upbound's official Crossplane provider to manage Microsoft Azure + authorization services in Kubernetes. + friendly-name.meta.crossplane.io: Provider Azure (authorization) + ownerReferences: + - uid: a84bcf77-4abc-4798-8e99-76c75964424f + kind: Provider + name: provider-azure-authorization + apiVersion: pkg.crossplane.io/v1 + controller: true + blockOwnerDeletion: true + creationTimestamp: 2023-11-12T15:50:08Z +spec: + image: xpkg.upbound.io/upbound/provider-azure-authorization:v0.38.2 + revision: 1 + desiredState: Active + packagePullPolicy: IfNotPresent + controllerConfigRef: + name: provider-azure-family-config + webhookTLSSecretName: webhook-tls-secret + skipDependencyResolution: false + ignoreCrossplaneConstraints: false +status: + conditions: + - type: Healthy + reason: HealthyPackageRevision + status: "True" + controllerRef: + name: provider-azure-authorization-29b8a8ac7b22 + foundDependencies: 1 + installedDependencies: 1 diff --git a/pkg/health/testdata/crossplane-reconcile-error.yaml b/pkg/health/testdata/crossplane-reconcile-error.yaml new file mode 100644 index 0000000..e2ba5be --- /dev/null +++ b/pkg/health/testdata/crossplane-reconcile-error.yaml @@ -0,0 +1,101 @@ +apiVersion: containerservice.azure.upbound.io/v1beta1 +kind: KubernetesClusterNodePool +metadata: + uid: 8680aee9-b7ed-4ccf-b29b-b9088fd1a686 + name: prodeu01 + labels: + kustomize.toolkit.fluxcd.io/name: workload-prod-az-eu-01 + kustomize.toolkit.fluxcd.io/namespace: flux-system + finalizers: + - finalizer.managedresource.crossplane.io + annotations: + crossplane.io/external-name: prodeu01 + upjet.crossplane.io/provider-meta: '{"e2bfb730-ecaa-11e6-8f88-34363bc7c4c0":{"create":3600000000000,"delete":3600000000000,"read":300000000000,"update":3600000000000},"schema_version":"1"}' + crossplane.io/external-create-pending: 2024-05-23T08:47:21Z + crossplane.io/external-create-succeeded: 2024-05-23T08:47:21Z + creationTimestamp: 2024-05-23T08:15:32Z +spec: + forProvider: + mode: User + osSku: Ubuntu + osType: Linux + vmSize: Standard_D8as_v5 + maxPods: 250 + maxCount: 0 + minCount: 0 + priority: Regular + nodeCount: 0 + osDiskType: Ephemeral + podSubnetId: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.Network/virtualNetworks/workload-prod-az-eu-01/subnets/workload-prod-az-eu-01-pods + osDiskSizeGb: 128 + spotMaxPrice: -1 + vnetSubnetId: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.Network/virtualNetworks/workload-prod-az-eu-01/subnets/workload-prod-az-eu-01-nodes + scaleDownMode: Delete + podSubnetIdRef: + name: workload-prod-az-eu-01-pods + kubeletDiskType: OS + vnetSubnetIdRef: + name: workload-prod-az-eu-01-nodes + enableAutoScaling: true + kubernetesClusterId: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.ContainerService/managedClusters/workload-prod-eu-01 + orchestratorVersion: 1.27.9 + kubernetesClusterIdRef: + name: workload-prod-eu-01 + initProvider: {} + deletionPolicy: Delete + providerConfigRef: + name: azure-north-europe + managementPolicies: + - '*' +status: + atProvider: + id: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.ContainerService/managedClusters/workload-prod-eu-01/agentPools/prodeu01 + mode: User + osSku: Ubuntu + osType: Linux + vmSize: Standard_D4s_v5 + maxPods: 250 + maxCount: 4 + minCount: 0 + priority: Regular + nodeCount: 1 + osDiskType: Managed + fipsEnabled: false + hostGroupId: '' + podSubnetId: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.Network/virtualNetworks/workload-prod-az-eu-01/subnets/workload-prod-az-eu-01-pods + osDiskSizeGb: 128 + spotMaxPrice: -1 + vnetSubnetId: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.Network/virtualNetworks/workload-prod-az-eu-01/subnets/workload-prod-az-eu-01-nodes + scaleDownMode: Delete + evictionPolicy: '' + kubeletDiskType: OS + messageOfTheDay: '' + ultraSsdEnabled: false + enableAutoScaling: true + enableNodePublicIp: false + kubernetesClusterId: /subscriptions/0cd017bb-aa54-4611-b21f-ecf8daee0511/resourceGroups/crossplane/providers/Microsoft.ContainerService/managedClusters/workload-prod-eu-01 + orchestratorVersion: 1.27.9 + customCaTrustEnabled: false + enableHostEncryption: false + nodePublicIpPrefixId: '' + proximityPlacementGroupId: '' + capacityReservationGroupId: '' + conditions: + - type: Ready + reason: Available + status: 'True' + - type: Synced + reason: ReconcileError + status: 'False' + message: 'observe failed: cannot run plan: plan failed: Instance cannot be + destroyed: Resource azurerm_kubernetes_cluster_node_pool.prodeu01 has + lifecycle.prevent_destroy set, but the plan calls for this resource to + be destroyed. To avoid this error and continue with the plan, either + disable lifecycle.prevent_destroy or reduce the scope of the plan using + the -target flag.' + - type: LastAsyncOperation + reason: Success + status: 'True' + - type: AsyncOperation + reason: Finished + status: 'True'