github.com/abayer/test-infra@v0.0.5/velodrome/transform/plugins/states.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  	"strings"
    21  	"time"
    22  )
    23  
    24  // State describes a pull-request states, based on the events we've
    25  // seen.
    26  type State interface {
    27  	// Has the state been activated
    28  	Active() bool
    29  	// How long has the state been activated (will panic if not active)
    30  	Age(t time.Time) time.Duration
    31  	// Receive the event, return the new state
    32  	ReceiveEvent(eventName, label string, t time.Time) (State, bool)
    33  }
    34  
    35  // ActiveState describe a states that has been enabled.
    36  type ActiveState struct {
    37  	startTime time.Time
    38  	exit      EventMatcher
    39  }
    40  
    41  var _ State = &ActiveState{}
    42  
    43  // Active if always true for an ActiveState
    44  func (ActiveState) Active() bool {
    45  	return true
    46  }
    47  
    48  // Age gives the time since the state has been activated.
    49  func (a *ActiveState) Age(t time.Time) time.Duration {
    50  	return t.Sub(a.startTime)
    51  }
    52  
    53  // ReceiveEvent checks if the event matches the exit criteria.
    54  // Returns a new InactiveState or self, and true if it changed.
    55  func (a *ActiveState) ReceiveEvent(eventName, label string, t time.Time) (State, bool) {
    56  	if a.exit.Match(eventName, label) {
    57  		return &InactiveState{
    58  			entry: a.exit.Opposite(),
    59  		}, true
    60  	}
    61  	return a, false
    62  }
    63  
    64  // InactiveState describes a state that has not enabled, or been disabled.
    65  type InactiveState struct {
    66  	entry EventMatcher
    67  }
    68  
    69  var _ State = &InactiveState{}
    70  
    71  // Active is always false for an InactiveState
    72  func (InactiveState) Active() bool {
    73  	return false
    74  }
    75  
    76  // Age doesn't make sense for InactiveState.
    77  func (i *InactiveState) Age(t time.Time) time.Duration {
    78  	panic("InactiveState doesn't have an age.")
    79  }
    80  
    81  // ReceiveEvent checks if the event matches the entry criteria
    82  // Returns a new ActiveState or self, and true if it changed.
    83  func (i *InactiveState) ReceiveEvent(eventName, label string, t time.Time) (State, bool) {
    84  	if i.entry.Match(eventName, label) {
    85  		return &ActiveState{
    86  			startTime: t,
    87  			exit:      i.entry.Opposite(),
    88  		}, true
    89  	}
    90  	return i, false
    91  }
    92  
    93  // MultiState tracks multiple individual states at the same time.
    94  type MultiState struct {
    95  	states []State
    96  }
    97  
    98  var _ State = &MultiState{}
    99  
   100  // Active is true if all the states are active.
   101  func (m *MultiState) Active() bool {
   102  	for _, state := range m.states {
   103  		if !state.Active() {
   104  			return false
   105  		}
   106  	}
   107  	return true
   108  }
   109  
   110  // Age returns the time since all states have been activated.
   111  // It will panic if any of the state is not active.
   112  func (m *MultiState) Age(t time.Time) time.Duration {
   113  	minAge := time.Duration(1<<63 - 1)
   114  	for _, state := range m.states {
   115  		stateAge := state.Age(t)
   116  		if stateAge < minAge {
   117  			minAge = stateAge
   118  		}
   119  	}
   120  	return minAge
   121  }
   122  
   123  // ReceiveEvent will send the event to each individual state, and update
   124  // them if they change.
   125  func (m *MultiState) ReceiveEvent(eventName, label string, t time.Time) (State, bool) {
   126  	oneChanged := false
   127  	for i := range m.states {
   128  		state, changed := m.states[i].ReceiveEvent(eventName, label, t)
   129  		if changed {
   130  			oneChanged = true
   131  		}
   132  		m.states[i] = state
   133  
   134  	}
   135  	return m, oneChanged
   136  }
   137  
   138  // NewState creates a MultiState instance based on the statesDescription
   139  // string. statesDescription is a comma separated list of
   140  // events. Events can be prepended with "!" (bang) to say that the state
   141  // will be activated only if this event doesn't happen (or is inverted).
   142  func NewState(statesDescription string) State {
   143  	states := []State{}
   144  
   145  	if statesDescription == "" {
   146  		// Create an infinite inactive state
   147  		return &InactiveState{
   148  			entry: FalseEvent{},
   149  		}
   150  	}
   151  
   152  	splitDescription := strings.Split(statesDescription, ",")
   153  	for _, description := range splitDescription {
   154  		description = strings.TrimSpace(description)
   155  		if strings.HasPrefix(description, "!") {
   156  			states = append(states, &ActiveState{
   157  				startTime: time.Time{},
   158  				exit:      NewEventMatcher(description[1:]),
   159  			})
   160  		} else {
   161  			states = append(states, &InactiveState{
   162  				entry: NewEventMatcher(description),
   163  			})
   164  		}
   165  	}
   166  
   167  	return &MultiState{states: states}
   168  }