OLD | NEW |
| (Empty) |
1 // Copyright 2012, 2013 Canonical Ltd. | |
2 // Licensed under the AGPLv3, see LICENCE file for details. | |
3 | |
4 package apiuniter | |
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/apiuniter/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 RunHo
ok, | |
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.pa
th, 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 } | |
OLD | NEW |