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