launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/uniter/state.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "launchpad.net/errgo/errors" 8 "os" 9 10 "launchpad.net/juju-core/charm" 11 "launchpad.net/juju-core/utils" 12 uhook "launchpad.net/juju-core/worker/uniter/hook" 13 ) 14 15 // Op enumerates the operations the uniter can perform. 16 type Op string 17 18 const ( 19 // Install indicates that the uniter is installing the charm. 20 Install Op = "install" 21 22 // RunHook indicates that the uniter is running a hook. 23 RunHook Op = "run-hook" 24 25 // Upgrade indicates that the uniter is upgrading the charm. 26 Upgrade Op = "upgrade" 27 28 // Continue indicates that the uniter should run ModeContinue 29 // to determine the next operation. 30 Continue Op = "continue" 31 ) 32 33 // OpStep describes the recorded progression of an operation. 34 type OpStep string 35 36 const ( 37 // Queued indicates that the uniter should undertake the operation 38 // as soon as possible. 39 Queued OpStep = "queued" 40 41 // Pending indicates that the uniter has started, but not completed, 42 // the operation. 43 Pending OpStep = "pending" 44 45 // Done indicates that the uniter has completed the operation, 46 // but may not yet have synchronized all necessary secondary state. 47 Done OpStep = "done" 48 ) 49 50 // State defines the local persistent state of the uniter, excluding relation 51 // state. 52 type State struct { 53 // Started indicates whether the start hook has run. 54 Started bool 55 56 // Op indicates the current operation. 57 Op Op 58 59 // OpStep indicates the current operation's progression. 60 OpStep OpStep 61 62 // Hook holds hook information relevant to the current operation. If Op 63 // is Continue, it holds the last hook that was executed; if Op is RunHook, 64 // it holds the running hook; if Op is Upgrade, a non-nil hook indicates 65 // that the uniter should return to that hook's Pending state after the 66 // upgrade is complete (instead of running an upgrade-charm hook). 67 Hook *uhook.Info `yaml:"hook,omitempty"` 68 69 // Charm describes the charm being deployed by an Install or Upgrade 70 // operation, and is otherwise blank. 71 CharmURL *charm.URL `yaml:"charm,omitempty"` 72 } 73 74 // validate returns an error if the state violates expectations. 75 func (st State) validate() (err error) { 76 defer utils.ErrorContextf(&err, "invalid uniter state") 77 hasHook := st.Hook != nil 78 hasCharm := st.CharmURL != nil 79 switch st.Op { 80 case Install: 81 if hasHook { 82 return errors.Newf("unexpected hook info") 83 } 84 fallthrough 85 case Upgrade: 86 if !hasCharm { 87 return errors.Newf("missing charm URL") 88 } 89 case Continue, RunHook: 90 if !hasHook { 91 return errors.Newf("missing hook info") 92 } else if hasCharm { 93 return errors.Newf("unexpected charm URL") 94 } 95 default: 96 return errors.Newf("unknown operation %q", st.Op) 97 } 98 switch st.OpStep { 99 case Queued, Pending, Done: 100 default: 101 return errors.Newf("unknown operation step %q", st.OpStep) 102 } 103 if hasHook { 104 return st.Hook.Validate() 105 } 106 return nil 107 } 108 109 // StateFile holds the disk state for a uniter. 110 type StateFile struct { 111 path string 112 } 113 114 // NewStateFile returns a new StateFile using path. 115 func NewStateFile(path string) *StateFile { 116 return &StateFile{path} 117 } 118 119 var ErrNoStateFile = errors.New("uniter state file does not exist") 120 121 // Read reads a State from the file. If the file does not exist it returns 122 // ErrNoStateFile. 123 func (f *StateFile) Read() (*State, error) { 124 var st State 125 if err := utils.ReadYaml(f.path, &st); err != nil { 126 if os.IsNotExist(errors.Cause(err)) { 127 return nil, ErrNoStateFile 128 } 129 } 130 if err := st.validate(); err != nil { 131 return nil, errors.Notef(err, "cannot read charm state at %q", f.path) 132 } 133 return &st, nil 134 } 135 136 // Write stores the supplied state to the file. 137 func (f *StateFile) Write(started bool, op Op, step OpStep, hi *uhook.Info, url *charm.URL) error { 138 st := &State{ 139 Started: started, 140 Op: op, 141 OpStep: step, 142 Hook: hi, 143 CharmURL: url, 144 } 145 if err := st.validate(); err != nil { 146 panic(err) 147 } 148 return utils.WriteYaml(f.path, st) 149 }