LEFT | RIGHT |
1 // launchpad.net/juju/state | |
2 // | |
3 // Copyright (c) 2011-2012 Canonical Ltd. | |
4 | |
5 package state | |
6 | |
7 import ( | 1 import ( |
8 "errors" | 2 "errors" |
9 "fmt" | 3 "fmt" |
10 "launchpad.net/goyaml" | |
11 "launchpad.net/gozk/zookeeper" | 4 "launchpad.net/gozk/zookeeper" |
12 "launchpad.net/juju/go/charm" | 5 "launchpad.net/juju/go/charm" |
13 "strconv" | 6 "strconv" |
14 "strings" | 7 "strings" |
15 ) | 8 ) |
16 | 9 |
17 // ResolvedMode describes the way state transition errors· | |
18 // are resolved.· | |
19 type ResolvedMode int | |
20 | |
21 const ( | |
22 ResolvedNone ResolvedMode = 0 | |
23 ResolvedRetryHooks ResolvedMode = 1000 | |
24 ResolvedNoHooks ResolvedMode = 1001 | |
25 ) | |
26 | |
27 // Port identifies a network port number for a particular protocol. | |
28 type Port struct { | |
29 Protocol string `yaml:"proto"` | |
30 Number int `yaml:"port"` | |
31 } | |
32 | |
33 // Unit represents the state of a service unit. | 10 // Unit represents the state of a service unit. |
34 type Unit struct { | 11 type Unit struct { |
35 » st *State | 12 » zk *zookeeper.Conn |
36 key string | 13 key string |
37 serviceKey string | 14 serviceKey string |
38 serviceName string | 15 serviceName string |
39 sequenceNo int | |
40 } | |
41 | |
42 // ServiceName returns the service name. | |
43 func (u *Unit) ServiceName() string { | |
44 return u.serviceName | |
45 } | |
46 | |
47 // Name returns the unit name. | |
48 func (u *Unit) Name() string { | |
49 return fmt.Sprintf("%s/%d", u.serviceName, u.sequenceNo) | |
50 } | |
51 | |
52 // PublicAddress returns the public address of the unit. | |
53 func (u *Unit) PublicAddress() (string, error) { | |
54 cn, err := readConfigNode(u.st.zk, u.zkPath()) | |
55 if err != nil { | |
56 return "", err | |
57 } | |
58 if address, ok := cn.Get("public-address"); ok { | |
59 return address.(string), nil | |
60 } | |
61 return "", errors.New("unit has no public address") | |
62 } | |
63 | |
64 // SetPublicAddress sets the public address of the unit. | |
65 func (u *Unit) SetPublicAddress(address string) error { | |
66 cn, err := readConfigNode(u.st.zk, u.zkPath()) | |
67 if err != nil { | |
68 return err | |
69 } | |
70 cn.Set("public-address", address) | |
71 _, err = cn.Write() | |
72 if err != nil { | |
73 return err | |
74 } | |
75 return nil | |
76 } | |
77 | |
78 // PrivateAddress returns the private address of the unit. | |
79 func (u *Unit) PrivateAddress() (string, error) { | |
80 cn, err := readConfigNode(u.st.zk, u.zkPath()) | |
81 if err != nil { | |
82 return "", err | |
83 } | |
84 if address, ok := cn.Get("private-address"); ok { | |
85 return address.(string), nil | |
86 } | |
87 return "", errors.New("unit has no private address") | |
88 } | |
89 | |
90 // SetPrivateAddress sets the private address of the unit. | |
91 func (u *Unit) SetPrivateAddress(address string) error { | |
92 cn, err := readConfigNode(u.st.zk, u.zkPath()) | |
93 if err != nil { | |
94 return err | |
95 } | |
96 cn.Set("private-address", address) | |
97 _, err = cn.Write() | |
98 if err != nil { | |
99 return err | |
100 } | |
101 return nil | |
102 } | |
103 | |
104 // CharmURL returns the charm URL this unit is supposed | |
105 // to use. | |
106 func (u *Unit) CharmURL() (url *charm.URL, err error) { | |
107 cn, err := readConfigNode(u.st.zk, u.zkPath()) | |
108 if err != nil { | |
109 return nil, err | |
110 } | |
111 if id, ok := cn.Get("charm"); ok { | |
112 url, err = charm.ParseURL(id.(string)) | |
113 if err != nil { | |
114 return nil, err | |
115 } | |
116 return url, nil | |
117 } | |
118 return nil, errors.New("unit has no charm URL") | |
119 } | |
120 | |
121 // SetCharmURL changes the charm URL for the unit. | |
122 func (u *Unit) SetCharmURL(url *charm.URL) error { | |
123 cn, err := readConfigNode(u.st.zk, u.zkPath()) | |
124 if err != nil { | |
125 return err | |
126 } | |
127 cn.Set("charm", url.String()) | |
128 _, err = cn.Write() | |
129 if err != nil { | |
130 return err | |
131 } | |
132 return nil | |
133 } | |
134 | |
135 // AssignedMachineId returns the id of the assigned machine. | |
136 func (u *Unit) AssignedMachineId() (int, error) { | |
137 topology, err := readTopology(u.st.zk) | |
138 if err != nil { | |
139 return 0, err | |
140 } | |
141 if !topology.HasService(u.serviceKey) || !topology.HasUnit(u.serviceKey,
u.key) { | |
142 return 0, stateChanged | |
143 } | |
144 machineKey, err := topology.UnitMachineKey(u.serviceKey, u.key) | |
145 if err != nil { | |
146 return 0, err | |
147 } | |
148 return machineId(machineKey), nil | |
149 } | |
150 | |
151 // AssignToMachine assigns this unit to a given machine. | |
152 func (u *Unit) AssignToMachine(machine *Machine) error { | |
153 assignUnit := func(t *topology) error { | |
154 if !t.HasService(u.serviceKey) || !t.HasUnit(u.serviceKey, u.key
) { | |
155 return stateChanged | |
156 } | |
157 machineKey, err := t.UnitMachineKey(u.serviceKey, u.key) | |
158 if err == unitNotAssigned { | |
159 return t.AssignUnitToMachine(u.serviceKey, u.key, machin
e.key) | |
160 } else if err != nil { | |
161 return err | |
162 } else if machineKey == machine.key { | |
163 // Everything is fine, it's already assigned. | |
164 return nil | |
165 } | |
166 return fmt.Errorf("unit %q already assigned to machine %d", u.Na
me(), machineId(machineKey)) | |
167 } | |
168 return retryTopologyChange(u.st.zk, assignUnit) | |
169 } | |
170 | |
171 // AssignToUnusedMachine assigns u to a machine without other units. | |
172 // If there are no unused machines besides machine 0, an error is returned. | |
173 func (u *Unit) AssignToUnusedMachine() (*Machine, error) { | |
174 machineKey := "" | |
175 assignUnusedUnit := func(t *topology) error { | |
176 if !t.HasService(u.serviceKey) || !t.HasUnit(u.serviceKey, u.key
) { | |
177 return stateChanged | |
178 } | |
179 for _, machineKey = range t.MachineKeys() { | |
180 if machineId(machineKey) != 0 { | |
181 hasUnits, err := t.MachineHasUnits(machineKey) | |
182 if err != nil { | |
183 return err | |
184 } | |
185 if !hasUnits { | |
186 break | |
187 } | |
188 } | |
189 // Reset machine key. | |
190 machineKey = "" | |
191 } | |
192 if machineKey == "" { | |
193 return errors.New("no unused machine found") | |
194 } | |
195 if err := t.AssignUnitToMachine(u.serviceKey, u.key, machineKey)
; err != nil { | |
196 return err | |
197 } | |
198 return nil | |
199 } | |
200 if err := retryTopologyChange(u.st.zk, assignUnusedUnit); err != nil { | |
201 return nil, err | |
202 } | |
203 return &Machine{u.st, machineKey}, nil | |
204 } | |
205 | |
206 // UnassignFromMachine removes the assignment between this unit and | |
207 // the machine it's assigned to. | |
208 func (u *Unit) UnassignFromMachine() error { | |
209 unassignUnit := func(t *topology) error { | |
210 if !t.HasService(u.serviceKey) || !t.HasUnit(u.serviceKey, u.key
) { | |
211 return stateChanged | |
212 } | |
213 // If for whatever reason it's already not assigned to a | |
214 // machine, ignore it and move forward so that we don't | |
215 // have to deal with conflicts. | |
216 key, err := t.UnitMachineKey(u.serviceKey, u.key) | |
217 if err == nil && key != "" { | |
218 t.UnassignUnitFromMachine(u.serviceKey, u.key) | |
219 } | |
220 return nil | |
221 } | |
222 return retryTopologyChange(u.st.zk, unassignUnit) | |
223 } | |
224 | |
225 // NeedsUpgrade returns whether the unit needs an upgrade. | |
226 func (u *Unit) NeedsUpgrade() (bool, error) { | |
227 stat, err := u.st.zk.Exists(u.zkNeedsUpgradePath()) | |
228 if err != nil { | |
229 return false, err | |
230 } | |
231 return stat != nil, nil | |
232 } | |
233 | |
234 // SetNeedsUpgrade informs the unit that it should perform an upgrade. | |
235 func (u *Unit) SetNeedsUpgrade() error { | |
236 _, err := u.st.zk.Create(u.zkNeedsUpgradePath(), "", 0, zkPermAll) | |
237 if err == zookeeper.ZNODEEXISTS { | |
238 // Node already exists, so same state. | |
239 return nil | |
240 } | |
241 return err | |
242 } | |
243 | |
244 // ClearNeedsUpgrade resets the upgrade notification. It is typically | |
245 // done by the unit agent before beginning the upgrade. | |
246 func (u *Unit) ClearNeedsUpgrade() error { | |
247 err := u.st.zk.Delete(u.zkNeedsUpgradePath(), -1) | |
248 if err == zookeeper.ZNONODE { | |
249 // Node doesn't exist, so same state. | |
250 return nil | |
251 } | |
252 return err | |
253 } | |
254 | |
255 // Resolved returns the value of the resolved setting if any. | |
256 func (u *Unit) Resolved() (ResolvedMode, error) { | |
257 yaml, _, err := u.st.zk.Get(u.zkResolvedPath()) | |
258 if err == zookeeper.ZNONODE { | |
259 // Default value. | |
260 return ResolvedNone, nil | |
261 } | |
262 if err != nil { | |
263 return ResolvedNone, err | |
264 } | |
265 setting := &struct{ Retry ResolvedMode }{} | |
266 if err = goyaml.Unmarshal([]byte(yaml), setting); err != nil { | |
267 return ResolvedNone, err | |
268 } | |
269 mode := setting.Retry | |
270 if err := validResolvedMode(mode); err != nil { | |
271 return ResolvedNone, err | |
272 } | |
273 return mode, nil | |
274 } | |
275 | |
276 // SetResolved marks the unit as having had any previous state | |
277 // transition problems resolved, and informs the unit that it may | |
278 // attempt to reestablish normal workflow. | |
279 // The resolved mode parameter informs whether to attempt to· | |
280 // reexecute previous failed hooks or to continue as if they had· | |
281 // succeeded before. | |
282 func (u *Unit) SetResolved(mode ResolvedMode) error { | |
283 if err := validResolvedMode(mode); err != nil { | |
284 return err | |
285 } | |
286 setting := &struct{ Retry ResolvedMode }{mode} | |
287 yaml, err := goyaml.Marshal(setting) | |
288 if err != nil { | |
289 return err | |
290 } | |
291 _, err = u.st.zk.Create(u.zkResolvedPath(), string(yaml), 0, zkPermAll) | |
292 if err == zookeeper.ZNODEEXISTS { | |
293 return fmt.Errorf("unit %q resolved flag already set", u.Name()) | |
294 } | |
295 return err | |
296 } | |
297 | |
298 // ClearResolved removes any resolved setting on the unit. | |
299 func (u *Unit) ClearResolved() error { | |
300 err := u.st.zk.Delete(u.zkResolvedPath(), -1) | |
301 if err == zookeeper.ZNONODE { | |
302 // Node doesn't exist, so same state. | |
303 return nil | |
304 } | |
305 return err | |
306 } | |
307 | |
308 // OpenPort sets the policy of the port with protocol and number to be opened. | |
309 func (u *Unit) OpenPort(protocol string, number int) error { | |
310 openPort := func(oldYaml string, stat *zookeeper.Stat) (string, error) { | |
311 ports := &struct{ Open []Port }{} | |
312 if oldYaml != "" { | |
313 if err := goyaml.Unmarshal([]byte(oldYaml), ports); err
!= nil { | |
314 return "", err | |
315 } | |
316 } | |
317 portToOpen := Port{protocol, number} | |
318 found := false | |
319 for _, openPort := range ports.Open { | |
320 if openPort == portToOpen { | |
321 found = true | |
322 break | |
323 } | |
324 } | |
325 if !found { | |
326 ports.Open = append(ports.Open, portToOpen) | |
327 } | |
328 newYaml, err := goyaml.Marshal(ports) | |
329 if err != nil { | |
330 return "", err | |
331 } | |
332 return string(newYaml), nil | |
333 } | |
334 return u.st.zk.RetryChange(u.zkPortsPath(), 0, zkPermAll, openPort) | |
335 } | |
336 | |
337 // ClosePort sets the policy of the port with protocol and number to be closed. | |
338 func (u *Unit) ClosePort(protocol string, number int) error { | |
339 closePort := func(oldYaml string, stat *zookeeper.Stat) (string, error)
{ | |
340 ports := &struct{ Open []Port }{} | |
341 if oldYaml != "" { | |
342 if err := goyaml.Unmarshal([]byte(oldYaml), ports); err
!= nil { | |
343 return "", err | |
344 } | |
345 } | |
346 portToClose := Port{protocol, number} | |
347 newOpenPorts := []Port{} | |
348 for _, oldOpenPort := range ports.Open { | |
349 if oldOpenPort != portToClose { | |
350 newOpenPorts = append(newOpenPorts, oldOpenPort) | |
351 } | |
352 } | |
353 ports.Open = newOpenPorts | |
354 newYaml, err := goyaml.Marshal(ports) | |
355 if err != nil { | |
356 return "", err | |
357 } | |
358 return string(newYaml), nil | |
359 } | |
360 return u.st.zk.RetryChange(u.zkPortsPath(), 0, zkPermAll, closePort) | |
361 } | |
362 | |
363 // OpenPorts returns a slice containing the open ports of the unit. | |
364 func (u *Unit) OpenPorts() ([]Port, error) { | |
365 yaml, _, err := u.st.zk.Get(u.zkPortsPath()) | |
366 if err == zookeeper.ZNONODE { | |
367 // Default value. | |
368 return nil, nil | |
369 } | |
370 if err != nil { | |
371 return nil, err | |
372 } | |
373 ports := &struct{ Open []Port }{} | |
374 if err = goyaml.Unmarshal([]byte(yaml), &ports); err != nil { | |
375 return nil, err | |
376 } | |
377 return ports.Open, nil | |
378 } | |
379 | |
380 // zkKey returns the ZooKeeper key of the unit. | |
381 func (u *Unit) zkKey() string { | |
382 return u.key | |
383 } | |
384 | |
385 // zkPath returns the ZooKeeper base path for the unit. | |
386 func (u *Unit) zkPath() string { | |
387 return fmt.Sprintf("/units/%s", u.key) | |
388 } | |
389 | |
390 // zkPortsPath returns the ZooKeeper path for the open ports. | |
391 func (u *Unit) zkPortsPath() string { | |
392 return fmt.Sprintf("/units/%s/ports", u.key) | |
393 } | |
394 | |
395 // zkAgentPath returns the ZooKeeper path for the unit agent. | |
396 func (u *Unit) zkAgentPath() string { | |
397 return fmt.Sprintf("/units/%s/agent", u.key) | |
398 } | |
399 | |
400 // zkNeedsUpgradePath returns the ZooKeeper path for the upgrade flag. | |
401 func (u *Unit) zkNeedsUpgradePath() string { | |
402 return fmt.Sprintf("/units/%s/upgrade", u.key) | |
403 } | |
404 | |
405 // zkResolvedPath returns the ZooKeeper path for the mark to resolve a unit. | |
406 func (u *Unit) zkResolvedPath() string { | |
407 return fmt.Sprintf("/units/%s/resolved", u.key) | |
408 } | |
409 | |
410 // parseUnitName parses a unit name like "wordpress/0" into | |
411 // its service name and sequence number parts. | |
412 func parseUnitName(name string) (serviceName string, seqNo int, err error) { | |
413 parts := strings.Split(name, "/") | |
414 if len(parts) != 2 { | |
415 return "", 0, fmt.Errorf("%q is not a valid unit name", name) | |
416 } | |
417 sequenceNo, err := strconv.ParseInt(parts[1], 10, 0) | |
418 if err != nil { | |
419 return "", 0, err | |
420 } | |
421 return parts[0], int(sequenceNo), nil | |
422 } | |
423 | |
424 // validResolvedMode ensures that only valid values for the | |
425 // resolved mode are used. | |
426 func validResolvedMode(mode ResolvedMode) error { | |
427 if mode != ResolvedRetryHooks && mode != ResolvedNoHooks { | |
428 return fmt.Errorf("invalid error resolution mode: %d", mode) | |
429 } | |
430 return nil | |
431 } | |
LEFT | RIGHT |