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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migration
     5  
     6  // Phase values specify model migration phases.
     7  type Phase int
     8  
     9  // Enumerate all possible migration phases.
    10  const (
    11  	UNKNOWN Phase = iota
    12  	NONE
    13  	QUIESCE
    14  	IMPORT
    15  	PROCESSRELATIONS
    16  	VALIDATION
    17  	SUCCESS
    18  	LOGTRANSFER
    19  	REAP
    20  	REAPFAILED
    21  	DONE
    22  	ABORT
    23  	ABORTDONE
    24  )
    25  
    26  var phaseNames = []string{
    27  	"UNKNOWN", // To catch uninitialised fields.
    28  	"NONE",    // For watchers to indicate there's never been a migration attempt.
    29  	"QUIESCE",
    30  	"IMPORT",
    31  	"PROCESSRELATIONS",
    32  	"VALIDATION",
    33  	"SUCCESS",
    34  	"LOGTRANSFER",
    35  	"REAP",
    36  	"REAPFAILED",
    37  	"DONE",
    38  	"ABORT",
    39  	"ABORTDONE",
    40  }
    41  
    42  // Those phases are only used to get a complete successful round for testing purposes.
    43  func SuccessfulMigrationPhases() []Phase {
    44  	return []Phase{
    45  		IMPORT,
    46  		PROCESSRELATIONS,
    47  		VALIDATION,
    48  		SUCCESS,
    49  		LOGTRANSFER,
    50  		REAP,
    51  		DONE,
    52  	}
    53  }
    54  
    55  // String returns the name of an model migration phase constant.
    56  func (p Phase) String() string {
    57  	i := int(p)
    58  	if i >= 0 && i < len(phaseNames) {
    59  		return phaseNames[i]
    60  	}
    61  	return "UNKNOWN"
    62  }
    63  
    64  // CanTransitionTo returns true if the given phase is a valid next
    65  // model migration phase.
    66  func (p Phase) CanTransitionTo(targetPhase Phase) bool {
    67  	nextPhases, exists := validTransitions[p]
    68  	if !exists {
    69  		return false
    70  	}
    71  	for _, nextPhase := range nextPhases {
    72  		if nextPhase == targetPhase {
    73  			return true
    74  		}
    75  	}
    76  	return false
    77  }
    78  
    79  // IsTerminal returns true if the phase is one which signifies the end
    80  // of a migration.
    81  func (p Phase) IsTerminal() bool {
    82  	for _, t := range terminalPhases {
    83  		if p == t {
    84  			return true
    85  		}
    86  	}
    87  	return false
    88  }
    89  
    90  // IsRunning returns true if the phase indicates the migration is
    91  // active and up to or at the SUCCESS phase. It returns false if the
    92  // phase is one of the final cleanup phases or indicates an failed
    93  // migration.
    94  func (p Phase) IsRunning() bool {
    95  	if p.IsTerminal() {
    96  		return false
    97  	}
    98  	switch p {
    99  	case QUIESCE, IMPORT, PROCESSRELATIONS, VALIDATION, SUCCESS:
   100  		return true
   101  	default:
   102  		return false
   103  	}
   104  }
   105  
   106  // Define all possible phase transitions.
   107  //
   108  // The keys are the "from" states and the values enumerate the
   109  // possible "to" states.
   110  var validTransitions = map[Phase][]Phase{
   111  	QUIESCE:          {IMPORT, ABORT},
   112  	IMPORT:           {PROCESSRELATIONS, ABORT},
   113  	PROCESSRELATIONS: {VALIDATION, ABORT},
   114  	VALIDATION:       {SUCCESS, ABORT},
   115  	SUCCESS:          {LOGTRANSFER},
   116  	LOGTRANSFER:      {REAP},
   117  	REAP:             {DONE, REAPFAILED},
   118  	ABORT:            {ABORTDONE},
   119  }
   120  
   121  var terminalPhases []Phase
   122  
   123  func init() {
   124  	// Compute the terminal phases.
   125  	for p := 0; p <= len(phaseNames); p++ {
   126  		phase := Phase(p)
   127  		if _, exists := validTransitions[phase]; !exists {
   128  			terminalPhases = append(terminalPhases, phase)
   129  		}
   130  	}
   131  }
   132  
   133  // ParsePhase converts a string model migration phase name
   134  // to its constant value.
   135  func ParsePhase(target string) (Phase, bool) {
   136  	for p, name := range phaseNames {
   137  		if target == name {
   138  			return Phase(p), true
   139  		}
   140  	}
   141  	return UNKNOWN, false
   142  }