From aaa8e415fbda88297f836f5f68e397881221c33e Mon Sep 17 00:00:00 2001 From: Marcus Noble Date: Tue, 22 Aug 2023 16:18:03 +0100 Subject: [PATCH] feat: add ability to include reason in count metrics Adds a configuration flag to enable including the `reason` for a TaskRun or PipelineRun status on their count metrics. This allows for more fine-grained monitoring and alerting of run failures. Signed-off-by: Marcus Noble --- config/config-observability.yaml | 1 + docs/metrics.md | 6 +- pkg/apis/config/metrics.go | 13 ++- pkg/apis/config/metrics_test.go | 13 +++ .../testdata/config-observability-reason.yaml | 31 +++++++ pkg/pipelinerunmetrics/metrics.go | 13 ++- pkg/pipelinerunmetrics/metrics_test.go | 88 +++++++++++++++++-- pkg/taskrunmetrics/metrics.go | 14 ++- pkg/taskrunmetrics/metrics_test.go | 64 ++++++++++++-- 9 files changed, 223 insertions(+), 20 deletions(-) create mode 100644 pkg/apis/config/testdata/config-observability-reason.yaml diff --git a/config/config-observability.yaml b/config/config-observability.yaml index 1f97697ba8e..f1f800beb06 100644 --- a/config/config-observability.yaml +++ b/config/config-observability.yaml @@ -58,3 +58,4 @@ data: metrics.taskrun.duration-type: "histogram" metrics.pipelinerun.level: "pipeline" metrics.pipelinerun.duration-type: "histogram" + metrics.count.enable-reason: "false" diff --git a/docs/metrics.md b/docs/metrics.md index 301a0e4de59..41bc51c4550 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -15,10 +15,10 @@ We expose several kinds of exporters, including Prometheus, Google Stackdriver, |-----------------------------------------------------------------------------------------| ----------- | ----------- | ----------- | | `tekton_pipelines_controller_pipelinerun_duration_seconds_[bucket, sum, count]` | Histogram/LastValue(Gauge) | `*pipeline`=<pipeline_name>
`*pipelinerun`=<pipelinerun_name>
`status`=<status>
`namespace`=<pipelinerun-namespace> | experimental | | `tekton_pipelines_controller_pipelinerun_taskrun_duration_seconds_[bucket, sum, count]` | Histogram/LastValue(Gauge) | `*pipeline`=<pipeline_name>
`*pipelinerun`=<pipelinerun_name>
`status`=<status>
`*task`=<task_name>
`*taskrun`=<taskrun_name>
`namespace`=<pipelineruns-taskruns-namespace>| experimental | -| `tekton_pipelines_controller_pipelinerun_count` | Counter | `status`=<status> | experimental | +| `tekton_pipelines_controller_pipelinerun_count` | Counter | `status`=<status>
`*reason`=<reason> | experimental | | `tekton_pipelines_controller_running_pipelineruns_count` | Gauge | | experimental | | `tekton_pipelines_controller_taskrun_duration_seconds_[bucket, sum, count]` | Histogram/LastValue(Gauge) | `status`=<status>
`*task`=<task_name>
`*taskrun`=<taskrun_name>
`namespace`=<pipelineruns-taskruns-namespace> | experimental | -| `tekton_pipelines_controller_taskrun_count` | Counter | `status`=<status> | experimental | +| `tekton_pipelines_controller_taskrun_count` | Counter | `status`=<status>
`*reason`=<reason> | experimental | | `tekton_pipelines_controller_running_taskruns_count` | Gauge | | experimental | | `tekton_pipelines_controller_running_taskruns_throttled_by_quota_count` | Gauge | | experimental | | `tekton_pipelines_controller_running_taskruns_throttled_by_node_count` | Gauge | | experimental | @@ -40,6 +40,7 @@ A sample config-map has been provided as [config-observability](./../config/conf metrics.taskrun.duration-type: "histogram" metrics.pipelinerun.level: "pipeline" metrics.pipelinerun.duration-type: "histogram" + metrics.count.enable-reason: "false" ``` Following values are available in the configmap: @@ -56,6 +57,7 @@ Following values are available in the configmap: | metrics.taskrun.duration-type | `lastvalue` | `tekton_pipelines_controller_pipelinerun_taskrun_duration_seconds` and `tekton_pipelines_controller_taskrun_duration_seconds` is of type gauge or lastvalue | | metrics.pipelinerun.duration-type | `histogram` | `tekton_pipelines_controller_pipelinerun_duration_seconds` is of type histogram | | metrics.pipelinerun.duration-type | `lastvalue` | `tekton_pipelines_controller_pipelinerun_duration_seconds` is of type gauge or lastvalue | +| metrics.count.enable-reason | `false` | Sets if the `reason` label should be included on count metrics | Histogram value isn't available when pipelinerun or taskrun labels are selected. The Lastvalue or Gauge will be provided. Histogram would serve no purpose because it would generate a single bar. TaskRun and PipelineRun level metrics aren't recommended because they lead to an unbounded cardinality which degrades the observability database. diff --git a/pkg/apis/config/metrics.go b/pkg/apis/config/metrics.go index 9422ec5eb43..2b18f6d0c38 100644 --- a/pkg/apis/config/metrics.go +++ b/pkg/apis/config/metrics.go @@ -36,6 +36,9 @@ const ( // metrics to use for aggregating duration for pipelinerun metricsDurationPipelinerunType = "metrics.pipelinerun.duration-type" + // countWithReasonKey sets if the reason label should be included on count metrics + countWithReasonKey = "metrics.count.enable-reason" + // DefaultTaskrunLevel determines to what level to aggregate metrics // when it isn't specified in configmap DefaultTaskrunLevel = TaskrunLevelAtTask @@ -92,6 +95,7 @@ type Metrics struct { PipelinerunLevel string DurationTaskrunType string DurationPipelinerunType string + CountWithReason bool } // GetMetricsConfigName returns the name of the configmap containing all @@ -113,7 +117,8 @@ func (cfg *Metrics) Equals(other *Metrics) bool { return other.TaskrunLevel == cfg.TaskrunLevel && other.PipelinerunLevel == cfg.PipelinerunLevel && other.DurationTaskrunType == cfg.DurationTaskrunType && - other.DurationPipelinerunType == cfg.DurationPipelinerunType + other.DurationPipelinerunType == cfg.DurationPipelinerunType && + other.CountWithReason == cfg.CountWithReason } // newMetricsFromMap returns a Config given a map corresponding to a ConfigMap @@ -123,6 +128,7 @@ func newMetricsFromMap(cfgMap map[string]string) (*Metrics, error) { PipelinerunLevel: DefaultPipelinerunLevel, DurationTaskrunType: DefaultDurationTaskrunType, DurationPipelinerunType: DefaultDurationPipelinerunType, + CountWithReason: false, } if taskrunLevel, ok := cfgMap[metricsTaskrunLevelKey]; ok { @@ -138,6 +144,11 @@ func newMetricsFromMap(cfgMap map[string]string) (*Metrics, error) { if durationPipelinerun, ok := cfgMap[metricsDurationPipelinerunType]; ok { tc.DurationPipelinerunType = durationPipelinerun } + + if countWithReason, ok := cfgMap[countWithReasonKey]; ok && countWithReason != "false" { + tc.CountWithReason = true + } + return &tc, nil } diff --git a/pkg/apis/config/metrics_test.go b/pkg/apis/config/metrics_test.go index e61fa7a5863..bf9795de667 100644 --- a/pkg/apis/config/metrics_test.go +++ b/pkg/apis/config/metrics_test.go @@ -38,6 +38,7 @@ func TestNewMetricsFromConfigMap(t *testing.T) { PipelinerunLevel: config.PipelinerunLevelAtPipelinerun, DurationTaskrunType: config.DurationPipelinerunTypeHistogram, DurationPipelinerunType: config.DurationPipelinerunTypeHistogram, + CountWithReason: false, }, fileName: config.GetMetricsConfigName(), }, @@ -47,9 +48,20 @@ func TestNewMetricsFromConfigMap(t *testing.T) { PipelinerunLevel: config.PipelinerunLevelAtNS, DurationTaskrunType: config.DurationTaskrunTypeHistogram, DurationPipelinerunType: config.DurationPipelinerunTypeLastValue, + CountWithReason: false, }, fileName: "config-observability-namespacelevel", }, + { + expectedConfig: &config.Metrics{ + TaskrunLevel: config.TaskrunLevelAtNS, + PipelinerunLevel: config.PipelinerunLevelAtNS, + DurationTaskrunType: config.DurationTaskrunTypeHistogram, + DurationPipelinerunType: config.DurationPipelinerunTypeLastValue, + CountWithReason: true, + }, + fileName: "config-observability-reason", + }, } for _, tc := range testCases { @@ -64,6 +76,7 @@ func TestNewMetricsFromEmptyConfigMap(t *testing.T) { PipelinerunLevel: config.PipelinerunLevelAtPipeline, DurationTaskrunType: config.DurationPipelinerunTypeHistogram, DurationPipelinerunType: config.DurationPipelinerunTypeHistogram, + CountWithReason: false, } verifyConfigFileWithExpectedMetricsConfig(t, MetricsConfigEmptyName, expectedConfig) } diff --git a/pkg/apis/config/testdata/config-observability-reason.yaml b/pkg/apis/config/testdata/config-observability-reason.yaml new file mode 100644 index 00000000000..a71a1ba3fed --- /dev/null +++ b/pkg/apis/config/testdata/config-observability-reason.yaml @@ -0,0 +1,31 @@ +# Copyright 2019 The Tekton Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-observability + namespace: tekton-pipelines + labels: + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-pipelines +data: + metrics.backend-destination: prometheus + metrics.stackdriver-project-id: "" + metrics.allow-stackdriver-custom-metrics: "false" + metrics.taskrun.level: "namespace" + metrics.taskrun.duration-type: "histogram" + metrics.pipelinerun.level: "namespace" + metrics.pipelinerun.duration-type: "lastvalue" + metrics.count.enable-reason: "true" diff --git a/pkg/pipelinerunmetrics/metrics.go b/pkg/pipelinerunmetrics/metrics.go index cedd0c0f30a..4794bc8955e 100644 --- a/pkg/pipelinerunmetrics/metrics.go +++ b/pkg/pipelinerunmetrics/metrics.go @@ -43,6 +43,7 @@ var ( pipelineTag = tag.MustNewKey("pipeline") namespaceTag = tag.MustNewKey("namespace") statusTag = tag.MustNewKey("status") + reasonTag = tag.MustNewKey("reason") prDuration = stats.Float64( "pipelinerun_duration_seconds", @@ -160,11 +161,15 @@ func viewRegister(cfg *config.Metrics) error { TagKeys: append([]tag.Key{statusTag, namespaceTag}, prunTag...), } + prCountViewTags := []tag.Key{statusTag} + if cfg.CountWithReason { + prCountViewTags = append(prCountViewTags, reasonTag) + } prCountView = &view.View{ Description: prCount.Description(), Measure: prCount, Aggregation: view.Count(), - TagKeys: []tag.Key{statusTag}, + TagKeys: prCountViewTags, } runningPRsCountView = &view.View{ Description: runningPRsCount.Description(), @@ -253,13 +258,15 @@ func (r *Recorder) DurationAndCount(pr *v1.PipelineRun, beforeCondition *apis.Co } } + cond := pr.Status.GetCondition(apis.ConditionSucceeded) status := "success" - if cond := pr.Status.GetCondition(apis.ConditionSucceeded); cond.Status == corev1.ConditionFalse { + if cond.Status == corev1.ConditionFalse { status = "failed" if cond.Reason == v1.PipelineRunReasonCancelled.String() { status = "cancelled" } } + reason := cond.Reason pipelineName := "anonymous" if pr.Spec.PipelineRef != nil && pr.Spec.PipelineRef.Name != "" { @@ -268,7 +275,7 @@ func (r *Recorder) DurationAndCount(pr *v1.PipelineRun, beforeCondition *apis.Co ctx, err := tag.New( context.Background(), append([]tag.Mutator{tag.Insert(namespaceTag, pr.Namespace), - tag.Insert(statusTag, status)}, r.insertTag(pipelineName, pr.Name)...)...) + tag.Insert(statusTag, status), tag.Insert(reasonTag, reason)}, r.insertTag(pipelineName, pr.Name)...)...) if err != nil { return err } diff --git a/pkg/pipelinerunmetrics/metrics_test.go b/pkg/pipelinerunmetrics/metrics_test.go index 5191ca99fc8..6e96fe84006 100644 --- a/pkg/pipelinerunmetrics/metrics_test.go +++ b/pkg/pipelinerunmetrics/metrics_test.go @@ -42,7 +42,7 @@ var ( completionTime = metav1.NewTime(startTime.Time.Add(time.Minute)) ) -func getConfigContext() context.Context { +func getConfigContext(countWithReason bool) context.Context { ctx := context.Background() cfg := &config.Config{ Metrics: &config.Metrics{ @@ -50,6 +50,7 @@ func getConfigContext() context.Context { PipelinerunLevel: config.PipelinerunLevelAtPipelinerun, DurationTaskrunType: config.DefaultDurationTaskrunType, DurationPipelinerunType: config.DefaultDurationPipelinerunType, + CountWithReason: countWithReason, }, } return config.ToContext(ctx, cfg) @@ -71,7 +72,7 @@ func TestMetricsOnStore(t *testing.T) { defer log.Sync() logger := log.Sugar() - ctx := getConfigContext() + ctx := getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -117,6 +118,7 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { expectedDuration float64 expectedCount int64 beforeCondition *apis.Condition + countWithReason bool }{{ name: "for succeeded pipeline", pipelineRun: &v1.PipelineRun{ @@ -149,6 +151,7 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, }, { name: "for succeeded pipeline different condition", pipelineRun: &v1.PipelineRun{ @@ -184,6 +187,7 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { Type: apis.ConditionReady, Status: corev1.ConditionUnknown, }, + countWithReason: false, }, { name: "for succeeded pipeline recount", pipelineRun: &v1.PipelineRun{ @@ -212,6 +216,7 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { Type: apis.ConditionSucceeded, Status: corev1.ConditionTrue, }, + countWithReason: false, }, { name: "for cancelled pipeline", pipelineRun: &v1.PipelineRun{ @@ -245,6 +250,7 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, }, { name: "for failed pipeline", pipelineRun: &v1.PipelineRun{ @@ -277,6 +283,7 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, }, { name: "for pipeline without start or completion time", pipelineRun: &v1.PipelineRun{ @@ -306,11 +313,82 @@ func TestRecordPipelineRunDurationCount(t *testing.T) { expectedDuration: 0, expectedCount: 1, beforeCondition: nil, + countWithReason: false, + }, { + name: "for failed pipeline with reason", + pipelineRun: &v1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{Name: "pipelinerun-1", Namespace: "ns"}, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{Name: "pipeline-1"}, + }, + Status: v1.PipelineRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: "Failed", + }}, + }, + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + StartTime: &startTime, + CompletionTime: &completionTime, + }, + }, + }, + expectedDurationTags: map[string]string{ + "pipeline": "pipeline-1", + "pipelinerun": "pipelinerun-1", + "namespace": "ns", + "status": "failed", + }, + expectedCountTags: map[string]string{ + "status": "failed", + "reason": "Failed", + }, + expectedDuration: 60, + expectedCount: 1, + beforeCondition: nil, + countWithReason: true, + }, { + name: "for cancelled pipeline with reason", + pipelineRun: &v1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{Name: "pipelinerun-1", Namespace: "ns"}, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{Name: "pipeline-1"}, + }, + Status: v1.PipelineRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: ReasonCancelled.String(), + }}, + }, + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + StartTime: &startTime, + CompletionTime: &completionTime, + }, + }, + }, + expectedDurationTags: map[string]string{ + "pipeline": "pipeline-1", + "pipelinerun": "pipelinerun-1", + "namespace": "ns", + "status": "cancelled", + }, + expectedCountTags: map[string]string{ + "status": "cancelled", + "reason": ReasonCancelled.String(), + }, + expectedDuration: 60, + expectedCount: 1, + beforeCondition: nil, + countWithReason: true, }} { t.Run(test.name, func(t *testing.T) { unregisterMetrics() - ctx := getConfigContext() + ctx := getConfigContext(test.countWithReason) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -363,7 +441,7 @@ func TestRecordRunningPipelineRunsCount(t *testing.T) { } } - ctx = getConfigContext() + ctx = getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -443,7 +521,7 @@ func TestRecordRunningPipelineRunsResolutionWaitCounts(t *testing.T) { } } - ctx = getConfigContext() + ctx = getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) diff --git a/pkg/taskrunmetrics/metrics.go b/pkg/taskrunmetrics/metrics.go index 99bf6646f6f..e5b251b497d 100644 --- a/pkg/taskrunmetrics/metrics.go +++ b/pkg/taskrunmetrics/metrics.go @@ -50,6 +50,7 @@ var ( taskTag = tag.MustNewKey("task") namespaceTag = tag.MustNewKey("namespace") statusTag = tag.MustNewKey("status") + reasonTag = tag.MustNewKey("reason") podTag = tag.MustNewKey("pod") trDurationView *view.View @@ -203,11 +204,16 @@ func viewRegister(cfg *config.Metrics) error { Aggregation: distribution, TagKeys: append([]tag.Key{statusTag, namespaceTag}, append(trunTag, prunTag...)...), } + + trCountViewTags := []tag.Key{statusTag} + if cfg.CountWithReason { + trCountViewTags = append(trCountViewTags, reasonTag) + } trCountView = &view.View{ Description: trCount.Description(), Measure: trCount, Aggregation: view.Count(), - TagKeys: []tag.Key{statusTag}, + TagKeys: trCountViewTags, } runningTRsCountView = &view.View{ Description: runningTRsCount.Description(), @@ -328,13 +334,15 @@ func (r *Recorder) DurationAndCount(ctx context.Context, tr *v1.TaskRun, beforeC taskName = tr.Spec.TaskRef.Name } + cond := tr.Status.GetCondition(apis.ConditionSucceeded) status := "success" - if cond := tr.Status.GetCondition(apis.ConditionSucceeded); cond.Status == corev1.ConditionFalse { + if cond.Status == corev1.ConditionFalse { status = "failed" } + reason := cond.Reason durationStat := trDuration - tags := []tag.Mutator{tag.Insert(namespaceTag, tr.Namespace), tag.Insert(statusTag, status)} + tags := []tag.Mutator{tag.Insert(namespaceTag, tr.Namespace), tag.Insert(statusTag, status), tag.Insert(reasonTag, reason)} if ok, pipeline, pipelinerun := IsPartOfPipeline(tr); ok { durationStat = prTRDuration tags = append(tags, r.insertPipelineTag(pipeline, pipelinerun)...) diff --git a/pkg/taskrunmetrics/metrics_test.go b/pkg/taskrunmetrics/metrics_test.go index 58be4285add..437fd032846 100644 --- a/pkg/taskrunmetrics/metrics_test.go +++ b/pkg/taskrunmetrics/metrics_test.go @@ -44,7 +44,7 @@ var ( completionTime = metav1.NewTime(startTime.Time.Add(time.Minute)) ) -func getConfigContext() context.Context { +func getConfigContext(countWithReason bool) context.Context { ctx := context.Background() cfg := &config.Config{ Metrics: &config.Metrics{ @@ -52,6 +52,7 @@ func getConfigContext() context.Context { PipelinerunLevel: config.PipelinerunLevelAtPipelinerun, DurationTaskrunType: config.DefaultDurationTaskrunType, DurationPipelinerunType: config.DefaultDurationPipelinerunType, + CountWithReason: countWithReason, }, } return config.ToContext(ctx, cfg) @@ -83,7 +84,7 @@ func TestMetricsOnStore(t *testing.T) { defer log.Sync() logger := log.Sugar() - ctx := getConfigContext() + ctx := getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -135,6 +136,7 @@ func TestRecordTaskRunDurationCount(t *testing.T) { expectedDuration float64 expectedCount int64 beforeCondition *apis.Condition + countWithReason bool }{{ name: "for succeeded taskrun", taskRun: &v1.TaskRun{ @@ -168,6 +170,7 @@ func TestRecordTaskRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, }, { name: "for succeeded taskrun with before condition", taskRun: &v1.TaskRun{ @@ -204,6 +207,7 @@ func TestRecordTaskRunDurationCount(t *testing.T) { Type: apis.ConditionReady, Status: corev1.ConditionUnknown, }, + countWithReason: false, }, { name: "for succeeded taskrun recount", taskRun: &v1.TaskRun{ @@ -233,6 +237,7 @@ func TestRecordTaskRunDurationCount(t *testing.T) { Type: apis.ConditionSucceeded, Status: corev1.ConditionTrue, }, + countWithReason: false, }, { name: "for failed taskrun", taskRun: &v1.TaskRun{ @@ -266,6 +271,7 @@ func TestRecordTaskRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, }, { name: "for succeeded taskrun in pipelinerun", taskRun: &v1.TaskRun{ @@ -307,6 +313,7 @@ func TestRecordTaskRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, }, { name: "for failed taskrun in pipelinerun", taskRun: &v1.TaskRun{ @@ -348,11 +355,56 @@ func TestRecordTaskRunDurationCount(t *testing.T) { expectedDuration: 60, expectedCount: 1, beforeCondition: nil, + countWithReason: false, + }, { + name: "for failed taskrun in pipelinerun with reason", + taskRun: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "taskrun-1", Namespace: "ns", + Labels: map[string]string{ + pipeline.PipelineLabelKey: "pipeline-1", + pipeline.PipelineRunLabelKey: "pipelinerun-1", + }, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "task-1"}, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: "TaskRunImagePullFailed", + }}, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &startTime, + CompletionTime: &completionTime, + }, + }, + }, + metricName: "pipelinerun_taskrun_duration_seconds", + expectedDurationTags: map[string]string{ + "pipeline": "pipeline-1", + "pipelinerun": "pipelinerun-1", + "task": "task-1", + "taskrun": "taskrun-1", + "namespace": "ns", + "status": "failed", + }, + expectedCountTags: map[string]string{ + "status": "failed", + "reason": "TaskRunImagePullFailed", + }, + expectedDuration: 60, + expectedCount: 1, + beforeCondition: nil, + countWithReason: true, }} { t.Run(c.name, func(t *testing.T) { unregisterMetrics() - ctx := getConfigContext() + ctx := getConfigContext(c.countWithReason) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -404,7 +456,7 @@ func TestRecordRunningTaskRunsCount(t *testing.T) { } } - ctx = getConfigContext() + ctx = getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -498,7 +550,7 @@ func TestRecordRunningTaskRunsThrottledCounts(t *testing.T) { } } - ctx = getConfigContext() + ctx = getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err) @@ -565,7 +617,7 @@ func TestRecordPodLatency(t *testing.T) { t.Run(td.name, func(t *testing.T) { unregisterMetrics() - ctx := getConfigContext() + ctx := getConfigContext(false) metrics, err := NewRecorder(ctx) if err != nil { t.Fatalf("NewRecorder: %v", err)