github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/model/upgradeseries.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package model
     5  
     6  import "github.com/juju/errors"
     7  
     8  // UpgradeSeriesStatus is the current status of a series upgrade for units
     9  type UpgradeSeriesStatus string
    10  
    11  func (s UpgradeSeriesStatus) String() string {
    12  	return string(s)
    13  }
    14  
    15  const (
    16  	UpgradeSeriesNotStarted       UpgradeSeriesStatus = "not started"
    17  	UpgradeSeriesValidate         UpgradeSeriesStatus = "validate"
    18  	UpgradeSeriesPrepareStarted   UpgradeSeriesStatus = "prepare started"
    19  	UpgradeSeriesPrepareRunning   UpgradeSeriesStatus = "prepare running"
    20  	UpgradeSeriesPrepareCompleted UpgradeSeriesStatus = "prepare completed"
    21  	UpgradeSeriesCompleteStarted  UpgradeSeriesStatus = "complete started"
    22  	UpgradeSeriesCompleteRunning  UpgradeSeriesStatus = "complete running"
    23  	UpgradeSeriesCompleted        UpgradeSeriesStatus = "completed"
    24  	UpgradeSeriesError            UpgradeSeriesStatus = "error"
    25  )
    26  
    27  // Graph is a type for representing a Directed acyclic graph (DAG).
    28  type Graph map[UpgradeSeriesStatus][]UpgradeSeriesStatus
    29  
    30  // Validate attempts to ensure that all edges from a vertex have a vertex to
    31  // the root graph.
    32  func (g Graph) Validate() error {
    33  	for vertex, vertices := range g {
    34  		for _, child := range vertices {
    35  			if !g.ValidState(child) {
    36  				return errors.NotValidf("vertex %q edge to vertex %q is", vertex, child)
    37  			}
    38  		}
    39  	}
    40  	return nil
    41  }
    42  
    43  // ValidState checks that a state is a valid vertex, as graphs have to ensure
    44  // that all edges to other vertices are also valid then this should be fine to
    45  // do.
    46  func (g Graph) ValidState(state UpgradeSeriesStatus) bool {
    47  	_, ok := g[state]
    48  	return ok
    49  }
    50  
    51  // UpgradeSeriesGraph defines a graph for moving between vertices of an upgrade
    52  // series.
    53  func UpgradeSeriesGraph() Graph {
    54  	return map[UpgradeSeriesStatus][]UpgradeSeriesStatus{
    55  		// Some clients are older and don't know about the validation phase, so
    56  		// in that case we allow them to jump to prepare-started.
    57  		UpgradeSeriesNotStarted: {
    58  			UpgradeSeriesPrepareStarted,
    59  			UpgradeSeriesValidate,
    60  			UpgradeSeriesError,
    61  		},
    62  		UpgradeSeriesValidate: {
    63  			UpgradeSeriesPrepareStarted,
    64  			UpgradeSeriesError,
    65  		},
    66  		UpgradeSeriesPrepareStarted: {
    67  			UpgradeSeriesPrepareRunning,
    68  			UpgradeSeriesError,
    69  		},
    70  		UpgradeSeriesPrepareRunning: {
    71  			UpgradeSeriesPrepareCompleted,
    72  			UpgradeSeriesError,
    73  		},
    74  		UpgradeSeriesPrepareCompleted: {
    75  			UpgradeSeriesCompleteStarted,
    76  			UpgradeSeriesError,
    77  		},
    78  		UpgradeSeriesCompleteStarted: {
    79  			UpgradeSeriesCompleteRunning,
    80  			UpgradeSeriesError,
    81  		},
    82  		UpgradeSeriesCompleteRunning: {
    83  			UpgradeSeriesCompleted,
    84  			UpgradeSeriesError,
    85  		},
    86  		UpgradeSeriesCompleted: {
    87  			UpgradeSeriesError,
    88  		},
    89  		UpgradeSeriesError: {},
    90  	}
    91  }
    92  
    93  // UpgradeSeriesFSM defines a finite state machine from a given graph of
    94  // possible vertices to transition. The FSM can start in any position using the
    95  // initial state and can move along the edges to the correct vertex.
    96  type UpgradeSeriesFSM struct {
    97  	state    UpgradeSeriesStatus
    98  	vertices Graph
    99  }
   100  
   101  // NewUpgradeSeriesFSM creates a UpgradeSeriesFSM from a graph and an initial
   102  // state.
   103  func NewUpgradeSeriesFSM(graph Graph, initial UpgradeSeriesStatus) (*UpgradeSeriesFSM, error) {
   104  	if err := graph.Validate(); err != nil {
   105  		return nil, errors.Trace(err)
   106  	}
   107  	return &UpgradeSeriesFSM{
   108  		state:    initial,
   109  		vertices: graph,
   110  	}, nil
   111  }
   112  
   113  // TransitionTo attempts to transition from the current state to the new given
   114  // state. If the state is currently at the requested state, then that's
   115  // classified as a no-op and no transition is required.
   116  func (u *UpgradeSeriesFSM) TransitionTo(state UpgradeSeriesStatus) bool {
   117  	if u.state == state {
   118  		return false
   119  	}
   120  
   121  	for _, vertex := range u.vertices[u.state] {
   122  		if vertex == state {
   123  			u.state = state
   124  			return true
   125  		}
   126  	}
   127  	return false
   128  }
   129  
   130  // State returns the current state of the fsm.
   131  func (u *UpgradeSeriesFSM) State() UpgradeSeriesStatus {
   132  	return u.state
   133  }