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