From 751516199804821cf4006dc4963e04616e2b12ce Mon Sep 17 00:00:00 2001 From: Petr Muller Date: Mon, 27 May 2024 20:16:13 +0200 Subject: [PATCH] `upgrade status`: polish alert insights - skip alerts without required labels - add context on why we show the insight (started firing during update or is known to affect updates) - skip alerts with info level - explicitly mention the alert does not have a runbook - fix `shortDuration` for more cases, including `now`, add tests - handle also `message` annotation on alerts --- pkg/cli/admin/upgrade/status/alerts.go | 58 ++++++++++++++++-- pkg/cli/admin/upgrade/status/alerts_test.go | 50 ++++++++++----- .../examples/4.14.1-degraded.detailed-output | 2 +- .../status/examples/4.14.1-degraded.output | 2 +- ...4.15.0-ec2-unavailable-mco-20m-alerts.json | 37 ++++++++++- ....0-ec2-unavailable-mco-20m.detailed-output | 26 ++++++-- .../4.15.0-ec2-unavailable-mco-20m.output | 6 +- ...ted-pdb-prohibits-draining.detailed-output | 2 +- ...lane-updated-pdb-prohibits-draining.output | 2 +- pkg/cli/admin/upgrade/status/health.go | 4 +- pkg/cli/admin/upgrade/status/health_test.go | 61 ++++++++++++++++++- 11 files changed, 212 insertions(+), 38 deletions(-) diff --git a/pkg/cli/admin/upgrade/status/alerts.go b/pkg/cli/admin/upgrade/status/alerts.go index cf66178d9b..a9387b865b 100644 --- a/pkg/cli/admin/upgrade/status/alerts.go +++ b/pkg/cli/admin/upgrade/status/alerts.go @@ -31,6 +31,7 @@ type AlertAnnotations struct { Description string `json:"description,omitempty"` Summary string `json:"summary,omitempty"` Runbook string `json:"runbook_url,omitempty"` + Message string `json:"message,omitempty"` } type Alert struct { @@ -57,27 +58,72 @@ func parseAlertDataToInsights(alertData AlertData, startedAt time.Time) []update var updateInsights []updateInsight for _, alert := range alerts { - if startedAt.After(alert.ActiveAt) && !allowedAlerts.Contains(alert.Labels.AlertName) { + var alertName string + if alertName = alert.Labels.AlertName; alertName == "" { continue } + + var description string + startedDuringUpdate := startedAt.Before(alert.ActiveAt) + affectsUpdates := allowedAlerts.Contains(alertName) + + if affectsUpdates { + if startedDuringUpdate { + description = "Alert known to affect updates started firing during the update." + } else { + description = "Alert known to affect updates has been firing since before the update started." + } + } else if startedDuringUpdate { + description = "Alert started firing during the update." + } else { + // Do not show alerts that were firing before the update started unless they are on the allowlist + continue + } + if alert.State == "pending" { continue } + var level impactLevel + if level = alertImpactLevel(alert.Labels.Severity); level < warningImpactLevel { + continue + } + + var runbook string + if runbook = alert.Annotations.Runbook; runbook == "" { + runbook = "" + } + + switch { + case alert.Annotations.Message != "" && alert.Annotations.Description != "": + description += " The alert description is: " + alert.Annotations.Description + " | " + alert.Annotations.Message + case alert.Annotations.Description != "": + description += " The alert description is: " + alert.Annotations.Description + case alert.Annotations.Message != "": + description += " The alert description is: " + alert.Annotations.Message + default: + description += " The alert has no description." + } + + var summary string + if summary = alert.Annotations.Summary; summary == "" { + summary = alertName + } + updateInsights = append(updateInsights, updateInsight{ startedAt: alert.ActiveAt, impact: updateInsightImpact{ - level: alertImpactLevel(alert.Labels.Severity), + level: level, impactType: unknownImpactType, - summary: "Alert: " + alert.Annotations.Summary, - description: alert.Annotations.Description, + summary: "Alert is firing: " + summary, + description: description, }, - remediation: updateInsightRemediation{reference: alert.Annotations.Runbook}, + remediation: updateInsightRemediation{reference: runbook}, scope: updateInsightScope{ scopeType: scopeTypeCluster, resources: []scopeResource{{ kind: scopeGroupKind{group: configv1.GroupName, kind: "Alert"}, namespace: alert.Labels.Namespace, - name: alert.Labels.AlertName, + name: alertName, }}, }, }) diff --git a/pkg/cli/admin/upgrade/status/alerts_test.go b/pkg/cli/admin/upgrade/status/alerts_test.go index db68ab2a05..6549761653 100644 --- a/pkg/cli/admin/upgrade/status/alerts_test.go +++ b/pkg/cli/admin/upgrade/status/alerts_test.go @@ -1,10 +1,10 @@ package status import ( - "reflect" "testing" "time" + "github.com/google/go-cmp/cmp" configv1 "github.com/openshift/api/config/v1" ) @@ -15,7 +15,6 @@ func TestParseAlertDataToInsights(t *testing.T) { tests := []struct { name string alertData AlertData - startedAt time.Time expectedCount int }{ { @@ -23,7 +22,6 @@ func TestParseAlertDataToInsights(t *testing.T) { alertData: AlertData{ Data: Data{Alerts: []Alert{}}, }, - startedAt: now, expectedCount: 0, }, { @@ -36,7 +34,6 @@ func TestParseAlertDataToInsights(t *testing.T) { }, }, }, - startedAt: now, expectedCount: 1, }, { @@ -48,7 +45,6 @@ func TestParseAlertDataToInsights(t *testing.T) { }, }, }, - startedAt: now, expectedCount: 0, }, { @@ -56,12 +52,11 @@ func TestParseAlertDataToInsights(t *testing.T) { alertData: AlertData{ Data: Data{ Alerts: []Alert{ - {ActiveAt: now.Add(-20 * time.Minute), Labels: AlertLabels{Severity: "info", Namespace: "default", AlertName: "PodDisruptionBudgetAtLimit"}, Annotations: AlertAnnotations{Summary: "PodDisruptionBudgetAtLimit is at limit"}}, - {ActiveAt: now.Add(-20 * time.Minute), Labels: AlertLabels{Severity: "info", Namespace: "default", AlertName: "AlertmanagerReceiversNotConfigured"}, Annotations: AlertAnnotations{Summary: "Receivers (notification integrations) are not configured on Alertmanager"}}, + {ActiveAt: now.Add(-20 * time.Minute), Labels: AlertLabels{Severity: "warning", Namespace: "default", AlertName: "PodDisruptionBudgetAtLimit"}, Annotations: AlertAnnotations{Summary: "PodDisruptionBudgetAtLimit is at limit"}}, + {ActiveAt: now.Add(-20 * time.Minute), Labels: AlertLabels{Severity: "warning", Namespace: "default", AlertName: "AlertmanagerReceiversNotConfigured"}, Annotations: AlertAnnotations{Summary: "Receivers (notification integrations) are not configured on Alertmanager"}}, }, }, }, - startedAt: now, expectedCount: 1, }, { @@ -69,11 +64,32 @@ func TestParseAlertDataToInsights(t *testing.T) { alertData: AlertData{ Data: Data{ Alerts: []Alert{ - {ActiveAt: now.Add(-20 * time.Minute), Labels: AlertLabels{Severity: "info", Namespace: "default", AlertName: "AlertmanagerReceiversNotConfigured"}, Annotations: AlertAnnotations{Summary: "Receivers (notification integrations) are not configured on Alertmanager"}}, + {ActiveAt: now.Add(-20 * time.Minute), Labels: AlertLabels{Severity: "warning", Namespace: "default", AlertName: "AlertmanagerReceiversNotConfigured"}, Annotations: AlertAnnotations{Summary: "Receivers (notification integrations) are not configured on Alertmanager"}}, + }, + }, + }, + expectedCount: 0, + }, + { + name: "Info Alert Active After Start Time, Not Allowed", + alertData: AlertData{ + Data: Data{ + Alerts: []Alert{ + {ActiveAt: now.Add(10 * time.Minute), Labels: AlertLabels{Severity: "info", Namespace: "default", AlertName: "NodeDown"}, Annotations: AlertAnnotations{Summary: "Node is down"}}, + }, + }, + }, + expectedCount: 0, + }, + { + name: "Info Alert Active After Start Time, Not Allowed", + alertData: AlertData{ + Data: Data{ + Alerts: []Alert{ + {ActiveAt: now.Add(10 * time.Minute), Labels: AlertLabels{Severity: "info", Namespace: "default", AlertName: "NodeDown"}, Annotations: AlertAnnotations{Summary: "Node is down"}}, }, }, }, - startedAt: now, expectedCount: 0, }, } @@ -81,7 +97,7 @@ func TestParseAlertDataToInsights(t *testing.T) { // Execute test cases for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - insights := parseAlertDataToInsights(tt.alertData, tt.startedAt) + insights := parseAlertDataToInsights(tt.alertData, now) if got := len(insights); got != tt.expectedCount { t.Errorf("parseAlertDataToInsights() = %v, want %v", got, tt.expectedCount) } @@ -112,10 +128,12 @@ func TestParseAlertDataToInsightsWithData(t *testing.T) { { startedAt: now.Add(10 * time.Minute), impact: updateInsightImpact{ - level: alertImpactLevel("critical"), - impactType: unknownImpactType, - summary: "Alert: Node is down", + level: alertImpactLevel("critical"), + impactType: unknownImpactType, + summary: "Alert is firing: Node is down", + description: "Alert started firing during the update. The alert has no description.", }, + remediation: updateInsightRemediation{reference: ""}, scope: updateInsightScope{ scopeType: scopeTypeCluster, resources: []scopeResource{ @@ -147,8 +165,8 @@ func TestParseAlertDataToInsightsWithData(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { insights := parseAlertDataToInsights(tt.alertData, tt.startedAt) - if !reflect.DeepEqual(insights, tt.expectedInsights) { - t.Errorf("parseAlertDataToInsights() got %#v, want %#v", insights, tt.expectedInsights) + if diff := cmp.Diff(tt.expectedInsights, insights, allowUnexportedInsightStructs); diff != "" { + t.Errorf("parseAlertDataToInsights() differs from expected:\n%s", diff) } }) } diff --git a/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.detailed-output b/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.detailed-output index 5e176f7488..4909f991e1 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.detailed-output +++ b/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.detailed-output @@ -74,7 +74,7 @@ Message: Cluster Operator control-plane-machine-set is unavailable (UnavailableR Description: Missing 1 available replica(s) Message: Cluster Version version is failing to proceed with the update (ClusterOperatorsDegraded) - Since: 0s + Since: now Level: Warning Impact: Update Stalled Reference: https://github.com/openshift/runbooks/blob/master/alerts/cluster-monitoring-operator/ClusterOperatorDegraded.md diff --git a/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.output b/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.output index 5de21712a5..191ed269f8 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.output +++ b/pkg/cli/admin/upgrade/status/examples/4.14.1-degraded.output @@ -32,6 +32,6 @@ SINCE LEVEL IMPACT MESSAGE 58m18s Error API Availability Cluster Operator kube-scheduler is degraded (NodeController_MasterNodesReady) 58m38s Error API Availability Cluster Operator etcd is degraded (EtcdEndpoints_ErrorUpdatingEtcdEndpoints::EtcdMembers_UnhealthyMembers::NodeController_MasterNodesReady) 1h0m17s Error API Availability Cluster Operator control-plane-machine-set is unavailable (UnavailableReplicas) -0s Warning Update Stalled Cluster Version version is failing to proceed with the update (ClusterOperatorsDegraded) +now Warning Update Stalled Cluster Version version is failing to proceed with the update (ClusterOperatorsDegraded) Run with --details=health for additional description and links to related online documentation diff --git a/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m-alerts.json b/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m-alerts.json index 071965738b..420792885e 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m-alerts.json +++ b/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m-alerts.json @@ -178,7 +178,7 @@ "severity": "warning" }, "annotations": { - "description": "PodDisruptionBudgetAtLimit. in namespace <>", + "description": "PodDisruptionBudgetAtLimit in namespace <>", "runbook_url": "https:///PDB.md", "summary": "PodDisruptionBudgetAtLimit for pods <>" }, @@ -187,6 +187,39 @@ "value": "6.708842592592593e-01", "partialResponseStrategy": "WARN" }, + { + "labels": { + "alertname": "PodDisruptionBudgetAtLimit", + "controller": "alertmanager", + "namespace": "openshift-monitoring", + "severity": "warning" + }, + "annotations": { + "summary": "PodDisruptionBudgetAtLimit for some reason does not have runbook and description" + }, + "state": "firing", + "activeAt": "2023-11-23T15:39:33.014999722Z", + "value": "6.708842592592593e-01", + "partialResponseStrategy": "WARN" + }, + { + "labels": { + "alertname": "PodDisruptionBudgetAtLimitWithMessage", + "controller": "alertmanager", + "namespace": "openshift-monitoring", + "severity": "warning" + }, + "annotations": { + "summary": "This alert has a message, description, runbook and summary", + "description": "This alert has a description", + "runbook_url": "https:///runbook.md", + "message": "This alert has a message, too" + }, + "state": "firing", + "activeAt": "2023-11-24T15:31:52.75038242Z", + "value": "6.708842592592593e-01", + "partialResponseStrategy": "WARN" + }, { "labels": { "alertname": "PrometheusOperatorWatchErrors", @@ -221,4 +254,4 @@ } ] } -} \ No newline at end of file +} diff --git a/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.detailed-output b/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.detailed-output index 1c6d866941..c083b22120 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.detailed-output +++ b/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.detailed-output @@ -35,20 +35,38 @@ Message: Cluster Operator machine-config is unavailable (MachineConfigController clusteroperators.config.openshift.io: machine-config Description: Cluster not available for [{operator 4.14.0-rc.3}]: ControllerConfig.machineconfiguration.openshift.io "machine-config-controller" is invalid: [status.controllerCertificates[0].notAfter: Required value, status.controllerCertificates[0].notBefore: Required value, status.controllerCertificates[1].notAfter: Required value, status.controllerCertificates[1].notBefore: Required value, status.controllerCertificates[2].notAfter: Required value, status.controllerCertificates[2].notBefore: Required value, status.controllerCertificates[3].notAfter: Required value, status.controllerCertificates[3].notBefore: Required value, status.controllerCertificates[4].notAfter: Required value, status.controllerCertificates[4].notBefore: Required value, status.controllerCertificates[5].notAfter: Required value, status.controllerCertificates[5].notBefore: Required value, status.controllerCertificates[6].notAfter: Required value, status.controllerCertificates[6].notBefore: Required value, status.controllerCertificates[7].notAfter: Required value, status.controllerCertificates[7].notBefore: Required value, status.controllerCertificates[8].notAfter: Required value, status.controllerCertificates[8].notBefore: Required value, status.controllerCertificates[9].notAfter: Required value, status.controllerCertificates[9].notBefore: Required value, : Invalid value: "null": some validation rules were not checked because the object was invalid; correct the existing errors to complete validation] -Message: Alert: Pod has been in a non-ready state for more than 15 minutes. +Message: Alert is firing: Pod has been in a non-ready state for more than 15 minutes. Since: 6m35s Level: Warning Impact: Unknown Reference: https://github.com/openshift/runbooks/blob/master/alerts/cluster-monitoring-operator/KubePodNotReady.md Resources: alerts.config.openshift.io: openshift-kube-apiserver/KubePodNotReady - Description: Pod openshift-kube-apiserver/kube-apiserver-startup-monitor-ip-10-0-60-26.us-west-1.compute.internal has been in a non-ready state for longer than 15 minutes. + Description: Alert started firing during the update. The alert description is: Pod openshift-kube-apiserver/kube-apiserver-startup-monitor-ip-10-0-60-26.us-west-1.compute.internal has been in a non-ready state for longer than 15 minutes. -Message: Alert: PodDisruptionBudgetAtLimit for pods <> +Message: Alert is firing: This alert has a message, description, runbook and summary + Since: 16m35s + Level: Warning + Impact: Unknown + Reference: https:///runbook.md + Resources: + alerts.config.openshift.io: openshift-monitoring/PodDisruptionBudgetAtLimitWithMessage + Description: Alert started firing during the update. The alert description is: This alert has a description | This alert has a message, too + +Message: Alert is firing: PodDisruptionBudgetAtLimit for pods <> Since: 24h8m54s Level: Warning Impact: Unknown Reference: https:///PDB.md Resources: alerts.config.openshift.io: openshift-monitoring/PodDisruptionBudgetAtLimit - Description: PodDisruptionBudgetAtLimit. in namespace <> + Description: Alert known to affect updates has been firing since before the update started. The alert description is: PodDisruptionBudgetAtLimit in namespace <> + +Message: Alert is firing: PodDisruptionBudgetAtLimit for some reason does not have runbook and description + Since: 24h8m54s + Level: Warning + Impact: Unknown + Reference: + Resources: + alerts.config.openshift.io: openshift-monitoring/PodDisruptionBudgetAtLimit + Description: Alert known to affect updates has been firing since before the update started. The alert has no description. diff --git a/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.output b/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.output index f230be862d..185a59896d 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.output +++ b/pkg/cli/admin/upgrade/status/examples/4.15.0-ec2-unavailable-mco-20m.output @@ -28,7 +28,9 @@ ip-10-0-99-40.us-east-2.compute.internal Outdated Pending 4.14.0-rc.3 = Update Health = SINCE LEVEL IMPACT MESSAGE 20m24s Error API Availability Cluster Operator machine-config is unavailable (MachineConfigControllerFailed) -6m35s Warning Unknown Alert: Pod has been in a non-ready state for more than 15 minutes. -24h8m54s Warning Unknown Alert: PodDisruptionBudgetAtLimit for pods <> +6m35s Warning Unknown Alert is firing: Pod has been in a non-ready state for more than 15 minutes. +16m35s Warning Unknown Alert is firing: This alert has a message, description, runbook and summary +24h8m54s Warning Unknown Alert is firing: PodDisruptionBudgetAtLimit for pods <> +24h8m54s Warning Unknown Alert is firing: PodDisruptionBudgetAtLimit for some reason does not have runbook and description Run with --details=health for additional description and links to related online documentation diff --git a/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.detailed-output b/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.detailed-output index c59b8c70ff..984eb8171b 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.detailed-output +++ b/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.detailed-output @@ -146,7 +146,7 @@ Message: Node build0-gstfj-ci-tests-worker-c-dcz9p is degraded Description: failed to drain node: build0-gstfj-ci-tests-worker-c-dcz9p after 1 hour. Please see machine-config-controller logs for more information Message: Cluster Version version is failing to proceed with the update (ClusterOperatorDegraded) - Since: 0s + Since: now Level: Warning Impact: Update Stalled Reference: https://github.com/openshift/runbooks/blob/master/alerts/cluster-monitoring-operator/ClusterOperatorDegraded.md diff --git a/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.output b/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.output index e94131d679..371358d1c1 100644 --- a/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.output +++ b/pkg/cli/admin/upgrade/status/examples/4.16.0-ec2-control-plane-updated-pdb-prohibits-draining.output @@ -44,7 +44,7 @@ SINCE LEVEL IMPACT MESSAGE - Error Update Stalled Node build0-gstfj-ci-tests-worker-b-jv5bg is degraded - Error Update Stalled Node build0-gstfj-ci-tests-worker-b-kj6gk is degraded - Error Update Stalled Node build0-gstfj-ci-tests-worker-c-dcz9p is degraded -0s Warning Update Stalled Cluster Version version is failing to proceed with the update (ClusterOperatorDegraded) +now Warning Update Stalled Cluster Version version is failing to proceed with the update (ClusterOperatorDegraded) - Warning Update Speed Node build0-gstfj-ci-prowjobs-worker-d-ddnxd is unavailable - Warning Update Speed Node build0-gstfj-ci-tests-worker-c-jq5rk is unavailable diff --git a/pkg/cli/admin/upgrade/status/health.go b/pkg/cli/admin/upgrade/status/health.go index 24c23b5e4b..d37c129b65 100644 --- a/pkg/cli/admin/upgrade/status/health.go +++ b/pkg/cli/admin/upgrade/status/health.go @@ -172,9 +172,9 @@ func assessUpdateInsights(insights []updateInsight, upgradingFor time.Duration, func shortDuration(d time.Duration) string { orig := d.String() switch { - case orig == "0h0m0s": + case orig == "0h0m0s" || orig == "0s": return "now" - case strings.HasSuffix(orig, "0m0s"): + case strings.HasSuffix(orig, "h0m0s"): return orig[:len(orig)-4] case strings.HasSuffix(orig, "m0s"): return orig[:len(orig)-2] diff --git a/pkg/cli/admin/upgrade/status/health_test.go b/pkg/cli/admin/upgrade/status/health_test.go index 4a4d14ac1f..3f22764214 100644 --- a/pkg/cli/admin/upgrade/status/health_test.go +++ b/pkg/cli/admin/upgrade/status/health_test.go @@ -191,7 +191,7 @@ func TestUpdateHealthData_Write(t *testing.T) { "5s Error API Availability Something that broke API and happened recently\n" + "20s Error Cluster Capacity Something that limits cluster capacity\n" + "10s Warning Update Speed Something that slows the update\n" + - "0s Info None Something with no impact that happened right now\n\n" + + "now Info None Something with no impact that happened right now\n\n" + "Run with --details=health for additional description and links to related online documentation\n", }, { @@ -202,7 +202,7 @@ func TestUpdateHealthData_Write(t *testing.T) { "Message: Something that broke API and happened recently\n Since: 5s\n Level: Error\n Impact: API Availability\n Reference: https://docs.openshift.com/container-platform/4.14/authentication/understanding-authentication.html\n Description: Only one auth replica is available\n\n" + "Message: Something that limits cluster capacity\n Since: 20s\n Level: Error\n Impact: Cluster Capacity\n Reference: https://docs.openshift.com/container-platform/4.14/nodes/pods/nodes-pods-autoscaling.html\n Description: Autoscaler is disabled, you should enable it\n\n" + "Message: Something that slows the update\n Since: 10s\n Level: Warning\n Impact: Update Speed\n Reference: https://cs.wikipedia.org/wiki/Hardware\n Description: Your pathetic hardware is slowing OCP down\n\n" + - "Message: Something with no impact that happened right now\n Since: 0s\n Level: Info\n Impact: None\n Reference: https://docs.openshift.com\n Description: This is a test\n", + "Message: Something with no impact that happened right now\n Since: now\n Level: Info\n Impact: None\n Reference: https://docs.openshift.com\n Description: This is a test\n", }, } for _, tc := range testCases { @@ -218,3 +218,60 @@ func TestUpdateHealthData_Write(t *testing.T) { }) } } + +func TestShortDuration(t *testing.T) { + testCases := []struct { + duration string + expected string + }{ + { + duration: "1s", + expected: "1s", + }, + { + duration: "1m", + expected: "1m", + }, + { + duration: "1h", + expected: "1h", + }, { + duration: "1h1m1s", + expected: "1h1m1s", + }, { + duration: "1h10m", + expected: "1h10m", + }, { + duration: "1h0m10s", + expected: "1h0m10s", + }, + { + duration: "10h10m0s", + expected: "10h10m", + }, + { + duration: "10h10m10s", + expected: "10h10m10s", + }, + { + duration: "0h10m0s", + expected: "10m", + }, + { + duration: "0h0m0s", + expected: "now", + }, + } + + for _, tc := range testCases { + t.Run(tc.duration, func(t *testing.T) { + d, err := time.ParseDuration(tc.duration) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if diff := cmp.Diff(tc.expected, shortDuration(d)); diff != "" { + t.Fatalf("Output differs from expected :\n%s", diff) + } + }) + } +}