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  }