github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/metrics/prowjobs/collector.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package prowjobs 18 19 import ( 20 "time" 21 22 "github.com/prometheus/client_golang/prometheus" 23 "github.com/sirupsen/logrus" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 26 "k8s.io/client-go/tools/cache" 27 28 prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1" 29 ) 30 31 func update(histogramVec *prometheus.HistogramVec, oldJob *prowapi.ProwJob, newJob *prowapi.ProwJob) { 32 if oldJob == nil || oldJob.Status.State == newJob.Status.State { 33 return 34 } 35 36 var oldTime *metav1.Time 37 var newTime *metav1.Time 38 39 switch oldJob.Status.State { 40 case prowapi.TriggeredState: 41 oldTime = &oldJob.CreationTimestamp 42 case prowapi.PendingState: 43 oldTime = oldJob.Status.PendingTime 44 } 45 46 switch newJob.Status.State { 47 case prowapi.FailureState, prowapi.SuccessState, prowapi.ErrorState, prowapi.AbortedState: 48 newTime = newJob.Status.CompletionTime 49 case prowapi.PendingState: 50 newTime = newJob.Status.PendingTime 51 } 52 53 if oldTime == nil || newTime == nil { 54 return 55 } 56 57 labels := getJobLabel(oldJob, newJob) 58 histogram, err := histogramVec.GetMetricWithLabelValues(labels.values()...) 59 if err != nil { 60 logrus.WithError(err).Error("Failed to get a histogram for a prowjob") 61 return 62 } 63 histogram.Observe(newTime.Sub(oldTime.Time).Seconds()) 64 } 65 66 // NewProwJobLifecycleHistogramVec creates histograms which can track the timespan between ProwJob state transitions. 67 // The histograms are based on the job name, the old job state and the new job state. 68 // Data is collected by hooking itself into the prowjob informer. 69 // The collector will never record the same state transition twice, even if reboots happen. 70 func NewProwJobLifecycleHistogramVec(informer cache.SharedIndexInformer) *prometheus.HistogramVec { 71 histogramVec := newHistogramVec() 72 informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 73 UpdateFunc: func(oldJob, newJob interface{}) { 74 update(histogramVec, oldJob.(*prowapi.ProwJob), newJob.(*prowapi.ProwJob)) 75 }, 76 }) 77 return histogramVec 78 } 79 80 func getJobLabel(oldJob *prowapi.ProwJob, newJob *prowapi.ProwJob) jobLabel { 81 jl := jobLabel{ 82 jobNamespace: newJob.Namespace, 83 jobName: newJob.Spec.Job, 84 jobType: string(newJob.Spec.Type), 85 state: string(newJob.Status.State), 86 last_state: string(oldJob.Status.State), 87 } 88 89 if newJob.Spec.Refs != nil { 90 jl.org = newJob.Spec.Refs.Org 91 jl.repo = newJob.Spec.Refs.Repo 92 jl.baseRef = newJob.Spec.Refs.BaseRef 93 } else if len(newJob.Spec.ExtraRefs) > 0 { 94 jl.org = newJob.Spec.ExtraRefs[0].Org 95 jl.repo = newJob.Spec.ExtraRefs[0].Repo 96 jl.baseRef = newJob.Spec.ExtraRefs[0].BaseRef 97 } 98 99 return jl 100 } 101 102 type jobLabel struct { 103 jobNamespace string 104 jobName string 105 jobType string 106 last_state string 107 state string 108 org string 109 repo string 110 baseRef string 111 } 112 113 func (jl *jobLabel) values() []string { 114 return []string{jl.jobNamespace, jl.jobName, jl.jobType, jl.last_state, jl.state, jl.org, jl.repo, jl.baseRef} 115 } 116 117 func newHistogramVec() *prometheus.HistogramVec { 118 return prometheus.NewHistogramVec( 119 prometheus.HistogramOpts{ 120 Name: "prow_job_runtime_seconds", 121 Buckets: []float64{ 122 time.Minute.Seconds() / 2, 123 (1 * time.Minute).Seconds(), 124 (2 * time.Minute).Seconds(), 125 (5 * time.Minute).Seconds(), 126 (10 * time.Minute).Seconds(), 127 (1 * time.Hour).Seconds() / 2, 128 (1 * time.Hour).Seconds(), 129 (2 * time.Hour).Seconds(), 130 (3 * time.Hour).Seconds(), 131 (4 * time.Hour).Seconds(), 132 (5 * time.Hour).Seconds(), 133 (6 * time.Hour).Seconds(), 134 (7 * time.Hour).Seconds(), 135 (8 * time.Hour).Seconds(), 136 (9 * time.Hour).Seconds(), 137 (10 * time.Hour).Seconds(), 138 }, 139 }, 140 []string{ 141 // namespace of the job 142 "job_namespace", 143 // name of the job 144 "job_name", 145 // type of the prowjob: presubmit, postsubmit, periodic, batch 146 "type", 147 // last state of the prowjob: triggered, pending, success, failure, aborted, error 148 "last_state", 149 // state of the prowjob: triggered, pending, success, failure, aborted, error 150 "state", 151 // the org of the prowjob's repo 152 "org", 153 // the prowjob's repo 154 "repo", 155 // the base_ref of the prowjob's repo 156 "base_ref", 157 }, 158 ) 159 }