...

Source file src/edge-infra.dev/pkg/edge/rollouts/gates.go

Documentation: edge-infra.dev/pkg/edge/rollouts

     1  package rollouts
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  )
     8  
     9  var (
    10  	ErrInvalidGateState = errors.New("invalid gate state")
    11  )
    12  
    13  type GateState bool
    14  
    15  const (
    16  	Open   GateState = true
    17  	Closed GateState = false
    18  )
    19  
    20  type GateApproval string
    21  
    22  const (
    23  	GateApproved GateApproval = "approved"
    24  	GatePending  GateApproval = "pending"
    25  	GateDenied   GateApproval = "denied"
    26  )
    27  
    28  // RolloutGate is a ...
    29  type RolloutGate interface {
    30  	IsOpen() bool
    31  }
    32  
    33  var _ RolloutGate = &TimerGate{}
    34  var _ RolloutGate = &ApprovalGate{}
    35  var _ RolloutGate = &ScheduleGate{}
    36  
    37  // TimerGate is a rollout graph gate that pauses execution for a specified duration
    38  type TimerGate struct {
    39  	BaseNode `json:",inline"`
    40  
    41  	// StartTime is the time that graph execution began. Delay is added as an offset and compared to the current time to
    42  	// determine the state of the gate, ie open or closed
    43  	StartTime time.Time `json:"startTime"`
    44  
    45  	// TODO(dk185217): figure out and document, test, what unit (ns, ms, etc) of time this needs to be
    46  	// Delay is amount of time that the TimerGate should pause execution for
    47  	Delay time.Duration `json:"delayMinutes"`
    48  
    49  	// time is an injectable implementation for getting the current time. Mostly for testability, a default
    50  	// implementation that wraps time.Now() will be used if it is not overwritten
    51  	time TimeSource
    52  }
    53  
    54  func NewTimerGate(key NodeKey, start time.Time, delay time.Duration, ts TimeSource) *TimerGate {
    55  	return &TimerGate{
    56  		BaseNode:  BaseNode{Key: key, Label: "TimerGate", State: Pending},
    57  		StartTime: start,
    58  		Delay:     delay,
    59  		time:      ts,
    60  	}
    61  }
    62  
    63  // Validate ...
    64  func (g *TimerGate) Validate() error {
    65  	if g.GetKey() == "" {
    66  		return ErrInvalidGateState
    67  	}
    68  	return nil
    69  }
    70  
    71  // IsOpen returns true if ...
    72  func (g *TimerGate) IsOpen() bool {
    73  	now := g.time.Now()
    74  	openAt := g.StartTime.Add(g.Delay)
    75  
    76  	isTimeElapsed := now.After(openAt)
    77  	return isTimeElapsed
    78  }
    79  
    80  // ScheduleGate is a rollout graph gate that pauses execution until a specified time
    81  type ScheduleGate struct {
    82  	BaseNode `json:",inline"`
    83  
    84  	// ScheduleTime is the time that the gate should open. Ie, the time until which execution is paused
    85  	ScheduleTime time.Time
    86  
    87  	// time is an injectable implementation for getting the current time. Mostly for testability, a default
    88  	// implementation that wraps time.Now() will be used if it is not overwritten
    89  	time TimeSource
    90  }
    91  
    92  func NewScheduleGate(key NodeKey, schedule time.Time, ts TimeSource) *ScheduleGate {
    93  	return &ScheduleGate{
    94  		BaseNode:     BaseNode{Key: key, Label: "ScheduleGate", State: Pending},
    95  		ScheduleTime: schedule,
    96  		time:         ts,
    97  	}
    98  }
    99  
   100  // IsOpen returns true if ...
   101  func (g *ScheduleGate) IsOpen() bool {
   102  	return time.Now().After(g.ScheduleTime)
   103  }
   104  
   105  // ApprovalGate ...
   106  type ApprovalGate struct {
   107  	BaseNode  `json:",inline"`
   108  	GateState `json:"gate_state"`
   109  
   110  	// approvalChecker ApprovalSource
   111  }
   112  
   113  // TODO(dk185217): Define approval inputs:
   114  // - what principal (user/group/role?) is required to approve
   115  // - any extra config: how many approvers? other config?
   116  type ApprovalSource func(context.Context) (bool, error)
   117  
   118  func NewApprovalGate(key NodeKey) *ApprovalGate {
   119  	return &ApprovalGate{
   120  		BaseNode:  BaseNode{Key: key, Label: "ApprovalGate", State: Pending},
   121  		GateState: Closed,
   122  	}
   123  }
   124  
   125  func (g *ApprovalGate) IsOpen() bool {
   126  	return bool(g.GateState)
   127  }
   128  

View as plain text