github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/operation/state.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package operation 5 6 import ( 7 "os" 8 9 "github.com/juju/errors" 10 "github.com/juju/utils" 11 "gopkg.in/juju/charm.v6" 12 13 "github.com/juju/juju/worker/uniter/hook" 14 ) 15 16 // Kind enumerates the operations the uniter can perform. 17 type Kind string 18 19 const ( 20 // Install indicates that the uniter is installing the charm. 21 Install Kind = "install" 22 23 // RunHook indicates that the uniter is running a hook. 24 RunHook Kind = "run-hook" 25 26 // RunAction indicates that the uniter is running an action. 27 RunAction Kind = "run-action" 28 29 // Upgrade indicates that the uniter is upgrading the charm. 30 Upgrade Kind = "upgrade" 31 32 // Continue indicates that the uniter should run ModeContinue 33 // to determine the next operation. 34 Continue Kind = "continue" 35 ) 36 37 // Step describes the recorded progression of an operation. 38 type Step string 39 40 const ( 41 // Queued indicates that the uniter should undertake the operation 42 // as soon as possible. 43 Queued Step = "queued" 44 45 // Pending indicates that the uniter has started, but not completed, 46 // the operation. 47 Pending Step = "pending" 48 49 // Done indicates that the uniter has completed the operation, 50 // but may not yet have synchronized all necessary secondary state. 51 Done Step = "done" 52 ) 53 54 // State defines the local persistent state of the uniter, excluding relation 55 // state. 56 type State struct { 57 58 // Leader indicates whether a leader-elected hook has been queued to run, and 59 // no more recent leader-deposed hook has completed. 60 Leader bool `yaml:"leader"` 61 62 // Started indicates whether the start hook has run. 63 Started bool `yaml:"started"` 64 65 // Stopped indicates whether the stop hook has run. 66 Stopped bool `yaml:"stopped"` 67 68 // Installed indicates whether the install hook has run. 69 Installed bool `yaml:"installed"` 70 71 // StatusSet indicates whether the charm being deployed has ever invoked 72 // the status-set hook tool. 73 StatusSet bool `yaml:"status-set"` 74 75 // Kind indicates the current operation. 76 Kind Kind `yaml:"op"` 77 78 // Step indicates the current operation's progression. 79 Step Step `yaml:"opstep"` 80 81 // Hook holds hook information relevant to the current operation. If Kind 82 // is Continue, it holds the last hook that was executed; if Kind is RunHook, 83 // it holds the running hook; if Kind is Upgrade, a non-nil hook indicates 84 // that the uniter should return to that hook's Pending state after the 85 // upgrade is complete (instead of running an upgrade-charm hook). 86 Hook *hook.Info `yaml:"hook,omitempty"` 87 88 // ActionId holds action information relevant to the current operation. If 89 // Kind is Continue, it holds the last action that was executed; if Kind is 90 // RunAction, it holds the running action. 91 ActionId *string `yaml:"action-id,omitempty"` 92 93 // Charm describes the charm being deployed by an Install or Upgrade 94 // operation, and is otherwise blank. 95 CharmURL *charm.URL `yaml:"charm,omitempty"` 96 97 // ConfigHash stores a hash of the latest known charm 98 // configuration settings - it's used to determine whether we need 99 // to run config-changed. 100 ConfigHash string `yaml:"config-hash,omitempty"` 101 102 // TrustHash stores a hash of the latest known charm trust 103 // configuration settings - it's used to determine whether we need 104 // to run config-changed. 105 TrustHash string `yaml:"trust-hash,omitempty"` 106 107 // AddressesHash stores a hash of the latest known 108 // machine/container addresses - it's used to determine whether we 109 // need to run config-changed. 110 AddressesHash string `yaml:"addresses-hash,omitempty"` 111 } 112 113 // validate returns an error if the state violates expectations. 114 func (st State) validate() (err error) { 115 defer errors.DeferredAnnotatef(&err, "invalid operation state") 116 hasHook := st.Hook != nil 117 hasActionId := st.ActionId != nil 118 hasCharm := st.CharmURL != nil 119 switch st.Kind { 120 case Install: 121 if st.Installed { 122 return errors.New("unexpected hook info with Kind Install") 123 } 124 fallthrough 125 case Upgrade: 126 switch { 127 case !hasCharm: 128 return errors.New("missing charm URL") 129 case hasActionId: 130 return errors.New("unexpected action id") 131 } 132 case RunAction: 133 switch { 134 case !hasActionId: 135 return errors.New("missing action id") 136 case hasCharm: 137 return errors.New("unexpected charm URL") 138 } 139 case RunHook: 140 switch { 141 case !hasHook: 142 return errors.New("missing hook info with Kind RunHook") 143 case hasCharm: 144 return errors.New("unexpected charm URL") 145 case hasActionId: 146 return errors.New("unexpected action id") 147 } 148 case Continue: 149 // TODO(jw4) LP-1438489 150 // ModeContinue should no longer have a Hook, but until the upgrade is 151 // fixed we can't fail the validation if it does. 152 if hasHook { 153 logger.Errorf("unexpected hook info with Kind Continue") 154 } 155 switch { 156 case hasCharm: 157 return errors.New("unexpected charm URL") 158 case hasActionId: 159 return errors.New("unexpected action id") 160 } 161 default: 162 return errors.Errorf("unknown operation %q", st.Kind) 163 } 164 switch st.Step { 165 case Queued, Pending, Done: 166 default: 167 return errors.Errorf("unknown operation step %q", st.Step) 168 } 169 if hasHook { 170 return st.Hook.Validate() 171 } 172 return nil 173 } 174 175 // stateChange is useful for a variety of Operation implementations. 176 type stateChange struct { 177 Kind Kind 178 Step Step 179 Hook *hook.Info 180 ActionId *string 181 CharmURL *charm.URL 182 HasRunStatusSet bool 183 } 184 185 func (change stateChange) apply(state State) *State { 186 state.Kind = change.Kind 187 state.Step = change.Step 188 state.Hook = change.Hook 189 state.ActionId = change.ActionId 190 state.CharmURL = change.CharmURL 191 state.StatusSet = state.StatusSet || change.HasRunStatusSet 192 return &state 193 } 194 195 // StateFile holds the disk state for a uniter. 196 type StateFile struct { 197 path string 198 } 199 200 // NewStateFile returns a new StateFile using path. 201 func NewStateFile(path string) *StateFile { 202 return &StateFile{path} 203 } 204 205 // Read reads a State from the file. If the file does not exist it returns 206 // ErrNoStateFile. 207 func (f *StateFile) Read() (*State, error) { 208 var st State 209 if err := utils.ReadYaml(f.path, &st); err != nil { 210 if os.IsNotExist(err) { 211 return nil, ErrNoStateFile 212 } 213 } 214 if err := st.validate(); err != nil { 215 return nil, errors.Errorf("cannot read %q: %v", f.path, err) 216 } 217 return &st, nil 218 } 219 220 // Write stores the supplied state to the file. 221 func (f *StateFile) Write(st *State) error { 222 if err := st.validate(); err != nil { 223 return errors.Trace(err) 224 } 225 return utils.WriteYaml(f.path, st) 226 }