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 }