github.com/abayer/test-infra@v0.0.5/velodrome/transform/plugins/bundle.go (about)

     1  /*
     2  Copyright 2017 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 plugins
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"sort"
    23  	"time"
    24  )
    25  
    26  // ByDuration sorts a slice of time.Duration
    27  type ByDuration []time.Duration
    28  
    29  func (b ByDuration) Len() int           { return len(b) }
    30  func (b ByDuration) Less(i, j int) bool { return b[i] < b[j] }
    31  func (b ByDuration) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
    32  
    33  // BundledStates saves the state of multiple issues/pull-requests. This
    34  // will also allow us to then compute statistics on these states like:
    35  // - Number of pull-requests in activated states (first return value of
    36  // Total())
    37  // - Sum of time since activated pull-requests are activated (second
    38  // return value of Total())
    39  // - Get a specific percentile time for activated pull-requests
    40  // (Percentile())
    41  type BundledStates struct {
    42  	description string
    43  	states      map[string]State
    44  }
    45  
    46  // NewBundledStates is the constructor for BundledStates
    47  func NewBundledStates(description string) BundledStates {
    48  	return BundledStates{
    49  		description: description,
    50  		states:      map[string]State{},
    51  	}
    52  }
    53  
    54  // ReceiveEvent is called when something happens on an issue. The state
    55  // for that issue is updated.
    56  func (b BundledStates) ReceiveEvent(ID string, eventName, label string, t time.Time) bool {
    57  	state, ok := b.states[ID]
    58  	if !ok {
    59  		state = NewState(b.description)
    60  	}
    61  	state, changed := state.ReceiveEvent(eventName, label, t)
    62  	b.states[ID] = state
    63  	return changed
    64  }
    65  
    66  // ages return the age of each active states
    67  func (b BundledStates) ages(t time.Time) map[string]time.Duration {
    68  	ages := map[string]time.Duration{}
    69  
    70  	for id, state := range b.states {
    71  		if !state.Active() {
    72  			continue
    73  		}
    74  		ages[id] = state.Age(t)
    75  	}
    76  	return ages
    77  }
    78  
    79  // Total counts number of active state, and total age (in minutes, to compute average)
    80  func (b BundledStates) Total(t time.Time) (count int, sum int64) {
    81  	for _, age := range b.ages(t) {
    82  		count++
    83  		sum += int64(age / time.Minute)
    84  	}
    85  	return
    86  }
    87  
    88  // Percentile returns given percentile for age of all active states at time t
    89  func (b BundledStates) Percentile(t time.Time, percentile int) time.Duration {
    90  	if percentile > 100 || percentile <= 0 {
    91  		panic(fmt.Errorf("percentile %d is out of scope", percentile))
    92  	}
    93  
    94  	ages := []time.Duration{}
    95  	for _, age := range b.ages(t) {
    96  		ages = append(ages, age)
    97  	}
    98  
    99  	if len(ages) == 0 {
   100  		return 0
   101  	}
   102  
   103  	sort.Sort(ByDuration(ages))
   104  
   105  	index := int(math.Ceil(float64(percentile)*float64(len(ages))/100) - 1)
   106  	if index >= len(ages) {
   107  		panic(fmt.Errorf("Index is out of range: %d/%d", index, len(ages)))
   108  	}
   109  	return ages[index]
   110  }