~themue/juju-core/go-state-service-relation-watcher

32.1.2 by Frank Mueller
Changes after first review.
1
package state
2
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
3
import (
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
4
	"errors"
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
5
	"fmt"
75.2.1 by Frank Mueller
Continued unit implementation.
6
	"launchpad.net/goyaml"
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
7
	"launchpad.net/gozk/zookeeper"
255 by Gustavo Niemeyer
launchpad.net/juju-core/juju => launchpad.net/juju-core
8
	"launchpad.net/juju-core/charm"
9
	"launchpad.net/juju-core/state/presence"
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
10
	"launchpad.net/juju-core/trivial"
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
11
	"strconv"
12
	"strings"
83.2.8 by Frank Mueller
Change Unit and Machine to directly provide agent related methods
13
	"time"
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
14
)
15
75.2.4 by Frank Mueller
Next fixes after review.
16
// ResolvedMode describes the way state transition errors 
75.2.3 by Frank Mueller
Changes after review.
17
// are resolved. 
75.2.4 by Frank Mueller
Next fixes after review.
18
type ResolvedMode int
75.2.2 by Frank Mueller
Changes after review.
19
75.2.1 by Frank Mueller
Continued unit implementation.
20
const (
75.2.4 by Frank Mueller
Next fixes after review.
21
	ResolvedNone       ResolvedMode = 0
22
	ResolvedRetryHooks ResolvedMode = 1000
23
	ResolvedNoHooks    ResolvedMode = 1001
75.2.1 by Frank Mueller
Continued unit implementation.
24
)
25
193.1.3 by William Reade
address review points
26
// AssignmentPolicy controls what machine a unit will be assigned to.
27
type AssignmentPolicy string
193.1.1 by William Reade
added state.Unit.Place, and PlacementPolicy type
28
29
const (
193.1.3 by William Reade
address review points
30
	// AssignLocal indicates that all service units should be assigned to machine 0.
31
	AssignLocal AssignmentPolicy = "local"
32
	// AssignUnused indicates that every service unit should be assigned to a
33
	// dedicated machine, and that new machines should be launched if required.
34
	AssignUnused AssignmentPolicy = "unused"
193.1.1 by William Reade
added state.Unit.Place, and PlacementPolicy type
35
)
36
354.25.7 by Frank Mueller
state: some more changes after review of unit status
37
// UnitStatus represents the status of the unit agent.
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
38
type UnitStatus string
39
40
const (
354.25.7 by Frank Mueller
state: some more changes after review of unit status
41
	UnitPending   UnitStatus = "pending"   // Agent hasn't started
42
	UnitInstalled UnitStatus = "installed" // Agent has run the installed hook
43
	UnitStarted   UnitStatus = "started"   // Agent is running properly
44
	UnitStopped   UnitStatus = "stopped"   // Agent has stopped running on request
45
	UnitError     UnitStatus = "error"     // Agent is waiting in an error state
46
	UnitDown      UnitStatus = "down"      // Agent is down or not communicating
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
47
)
48
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
49
// NeedsUpgrade describes if a unit needs an
50
// upgrade and if this is forced.
51
type NeedsUpgrade struct {
52
	Upgrade bool
53
	Force   bool
54
}
55
130.1.2 by Frank Mueller
Changes after review.
56
// needsUpgradeNode represents the content of the
57
// upgrade node of a unit.
58
type needsUpgradeNode struct {
59
	Force bool
60
}
61
83.2.10 by Frank Mueller
Integrated new presence.WaitAlive() and added machine agent tests.
62
// agentPingerPeriod defines the period of pinging the
63
// ZooKeeper to signal that a unit agent is alive. It's
64
// also used by machine.
213.5.5 by William Reade
address review points
65
var (
83.2.10 by Frank Mueller
Integrated new presence.WaitAlive() and added machine agent tests.
66
	agentPingerPeriod = 1 * time.Second
83.2.8 by Frank Mueller
Change Unit and Machine to directly provide agent related methods
67
)
68
75.2.4 by Frank Mueller
Next fixes after review.
69
// Port identifies a network port number for a particular protocol.
75.2.2 by Frank Mueller
Changes after review.
70
type Port struct {
75.2.4 by Frank Mueller
Next fixes after review.
71
	Protocol string `yaml:"proto"`
72
	Number   int    `yaml:"port"`
75.2.1 by Frank Mueller
Continued unit implementation.
73
}
74
131.1.4 by Frank Mueller
Final changes after review.
75
// openPortsNode represents the content of the
76
// ports node of a unit.
77
type openPortsNode struct {
78
	Open []Port
79
}
80
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
81
// Unit represents the state of a service unit.
32.1.2 by Frank Mueller
Changes after first review.
82
type Unit struct {
313.1.1 by William Reade
store principalKey in state.Unit
83
	st           *State
84
	key          string
85
	serviceName  string
86
	principalKey string
346.5.1 by Roger Peppe
state: propose tools not versions
87
	agentTools
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
88
}
89
90
// ServiceName returns the service name.
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
91
func (u *Unit) ServiceName() string {
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
92
	return u.serviceName
32.1.2 by Frank Mueller
Changes after first review.
93
}
94
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
95
// Name returns the unit name.
96
func (u *Unit) Name() string {
196.3.5 by Roger Peppe
state: fixes for review
97
	return fmt.Sprintf("%s/%d", u.serviceName, keySeq(u.key))
196.3.3 by Roger Peppe
state: use a string for unit key
98
}
99
221.3.2 by Frank Mueller
state: Fixed review comments in unit.go.
100
// String returns the unit as string.
101
func (u *Unit) String() string {
102
	return u.Name()
103
}
104
196.3.5 by Roger Peppe
state: fixes for review
105
// makeUnitKey returns a unit key made up from the service key
212.1.2 by Roger Peppe
state: use "seq" instead of "id" in makeUnitKey
106
// and the unit sequence number within the service.
107
func makeUnitKey(serviceKey string, unitSeq int) string {
196.3.3 by Roger Peppe
state: use a string for unit key
108
	if !strings.HasPrefix(serviceKey, "service-") {
109
		panic(fmt.Errorf("invalid service key %q", serviceKey))
110
	}
212.1.2 by Roger Peppe
state: use "seq" instead of "id" in makeUnitKey
111
	return fmt.Sprintf("unit-%s-%010d", serviceKey[len("service-"):], unitSeq)
196.3.3 by Roger Peppe
state: use a string for unit key
112
}
113
114
func serviceKeyForUnitKey(unitKey string) (string, error) {
115
	if !strings.HasPrefix(unitKey, "unit-") {
196.3.5 by Roger Peppe
state: fixes for review
116
		return "", fmt.Errorf("invalid unit key %q", unitKey)
196.3.3 by Roger Peppe
state: use a string for unit key
117
	}
118
	k := unitKey[len("unit-"):]
119
	i := strings.Index(k, "-")
120
	if i <= 0 {
196.3.5 by Roger Peppe
state: fixes for review
121
		return "", fmt.Errorf("invalid unit key %q", unitKey)
196.3.3 by Roger Peppe
state: use a string for unit key
122
	}
123
	return "service-" + k[0:i], nil
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
124
}
125
331.11.1 by Roger Peppe
state: embed agentVersion into Unit
126
func newUnit(st *State, serviceName string, key, principalKey string) *Unit {
127
	u := &Unit{
331.11.3 by Roger Peppe
state: unit version test passes
128
		st:           st,
129
		serviceName:  serviceName,
331.11.1 by Roger Peppe
state: embed agentVersion into Unit
130
		key:          key,
131
		principalKey: principalKey,
132
	}
346.5.1 by Roger Peppe
state: propose tools not versions
133
	u.agentTools = agentTools{
354.21.5 by Roger Peppe
merge trunk
134
		st:    st,
331.11.4 by Roger Peppe
merge trunk
135
		path:  u.zkPath(),
331.11.1 by Roger Peppe
state: embed agentVersion into Unit
136
		agent: "unit",
137
	}
138
	return u
139
}
140
213.2.1 by Roger Peppe
state: implement Machine.Units
141
func (st *State) unitFromKey(t *topology, unitKey string) (*Unit, error) {
217.1.4 by Roger Peppe
state: infer machine id for subordinate units
142
	tsvc, tunit, err := t.serviceAndUnit(unitKey)
213.2.1 by Roger Peppe
state: implement Machine.Units
143
	if err != nil {
144
		return nil, err
145
	}
331.11.3 by Roger Peppe
state: unit version test passes
146
	return newUnit(st, tsvc.Name, unitKey, tunit.Principal), nil
213.2.1 by Roger Peppe
state: implement Machine.Units
147
}
148
51.1.3 by Frank Mueller
Done review changes.
149
// PublicAddress returns the public address of the unit.
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
150
func (u *Unit) PublicAddress() (string, error) {
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
151
	return getConfigString(u.st.zk, u.zkPath(), "public-address",
152
		"public address of unit %q", u)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
153
}
154
155
// SetPublicAddress sets the public address of the unit.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
156
func (u *Unit) SetPublicAddress(address string) (err error) {
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
157
	return setConfigString(u.st.zk, u.zkPath(), "public-address", address,
158
		"public address of unit %q", u)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
159
}
160
51.1.3 by Frank Mueller
Done review changes.
161
// PrivateAddress returns the private address of the unit.
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
162
func (u *Unit) PrivateAddress() (string, error) {
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
163
	return getConfigString(u.st.zk, u.zkPath(), "private-address",
164
		"private address of unit %q", u)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
165
}
166
167
// SetPrivateAddress sets the private address of the unit.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
168
func (u *Unit) SetPrivateAddress(address string) (err error) {
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
169
	return setConfigString(u.st.zk, u.zkPath(), "private-address", address,
170
		"private address of unit %q", u)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
171
}
172
354.25.6 by Frank Mueller
state: changes on unit status after review
173
// Status returns the status of the unit's agent.
354.25.8 by Frank Mueller
state: final changes before submit
174
func (u *Unit) Status() (s UnitStatus, info string, err error) {
354.25.7 by Frank Mueller
state: some more changes after review of unit status
175
	cn, err := readConfigNode(u.st.zk, u.zkPath())
354.25.2 by Frank Mueller
state: unit status now defaults to 'pending'
176
	if err != nil {
354.25.8 by Frank Mueller
state: final changes before submit
177
		return "", "", fmt.Errorf("cannot read status of unit %q: %v", u, err)
354.25.7 by Frank Mueller
state: some more changes after review of unit status
178
	}
179
	raw, found := cn.Get("status")
180
	if !found {
181
		return UnitPending, "", nil
182
	}
354.25.8 by Frank Mueller
state: final changes before submit
183
	s = UnitStatus(raw.(string))
184
	switch s {
354.25.7 by Frank Mueller
state: some more changes after review of unit status
185
	case UnitError:
354.25.6 by Frank Mueller
state: changes on unit status after review
186
		// We always expect an info if status is 'error'.
354.25.7 by Frank Mueller
state: some more changes after review of unit status
187
		raw, found = cn.Get("status-info")
188
		if !found {
354.25.8 by Frank Mueller
state: final changes before submit
189
			panic("no status-info found for unit error")
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
190
		}
354.25.8 by Frank Mueller
state: final changes before submit
191
		return s, raw.(string), nil
354.25.7 by Frank Mueller
state: some more changes after review of unit status
192
	case UnitStopped:
193
		return UnitStopped, "", nil
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
194
	}
195
	alive, err := u.AgentAlive()
196
	if err != nil {
354.25.7 by Frank Mueller
state: some more changes after review of unit status
197
		return "", "", err
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
198
	}
199
	if !alive {
354.25.8 by Frank Mueller
state: final changes before submit
200
		s = UnitDown
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
201
	}
354.25.8 by Frank Mueller
state: final changes before submit
202
	return s, "", nil
354.25.1 by Frank Mueller
state: added status to unit
203
}
204
205
// SetStatus sets the status of the unit.
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
206
func (u *Unit) SetStatus(status UnitStatus, info string) error {
354.25.7 by Frank Mueller
state: some more changes after review of unit status
207
	if status == UnitPending {
354.25.6 by Frank Mueller
state: changes on unit status after review
208
		panic("unit status must not be set to pending")
209
	}
354.25.7 by Frank Mueller
state: some more changes after review of unit status
210
	cn, err := readConfigNode(u.st.zk, u.zkPath())
211
	if err != nil {
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
212
		return err
213
	}
354.25.7 by Frank Mueller
state: some more changes after review of unit status
214
	cn.Set("status", status)
215
	cn.Set("status-info", info)
216
	_, err = cn.Write()
217
	if err != nil {
354.25.8 by Frank Mueller
state: final changes before submit
218
		return fmt.Errorf("cannot set status of unit %q: %v", u, err)
354.25.5 by Frank Mueller
state: changed unit status to own type, constants and additional info field
219
	}
220
	return nil
354.25.1 by Frank Mueller
state: added status to unit
221
}
222
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
223
// CharmURL returns the charm URL this unit is supposed
224
// to use.
225
func (u *Unit) CharmURL() (url *charm.URL, err error) {
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
226
	surl, err := getConfigString(u.st.zk, u.zkPath(), "charm",
331.9.6 by Roger Peppe
state: fix error messages
227
		"charm URL of unit %q", u)
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
228
	if err != nil {
229
		return nil, err
230
	}
231
	url, err = charm.ParseURL(surl)
232
	if err != nil {
331.9.7 by Roger Peppe
state: error message fixes for review
233
		return nil, fmt.Errorf("failed to parse charm URL of unit %q: %v", u, err)
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
234
	}
235
	return url, err
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
236
}
237
238
// SetCharmURL changes the charm URL for the unit.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
239
func (u *Unit) SetCharmURL(url *charm.URL) (err error) {
331.9.5 by Roger Peppe
state: give a format argument to get/setConfigNode
240
	return setConfigString(u.st.zk, u.zkPath(), "charm", url.String(),
331.9.7 by Roger Peppe
state: error message fixes for review
241
		"charm URL of unit %q", u)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
242
}
243
199.1.1 by William Reade
subordinate service units can now be added to principal service units
244
// IsPrincipal returns whether the unit is deployed in its own container,
245
// and can therefore have subordinate services deployed alongside it.
232.1.1 by Roger Peppe
state: remove error return from Unit.IsPrincipal
246
func (u *Unit) IsPrincipal() bool {
313.1.2 by William Reade
use empty Unit.principalKey to denote unit principalicity
247
	return u.principalKey == ""
199.1.1 by William Reade
subordinate service units can now be added to principal service units
248
}
249
51.1.3 by Frank Mueller
Done review changes.
250
// AssignedMachineId returns the id of the assigned machine.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
251
func (u *Unit) AssignedMachineId() (id int, err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
252
	defer trivial.ErrorContextf(&err, "cannot get machine id of unit %q", u)
75.2.1 by Frank Mueller
Continued unit implementation.
253
	topology, err := readTopology(u.st.zk)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
254
	if err != nil {
51.1.3 by Frank Mueller
Done review changes.
255
		return 0, err
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
256
	}
196.3.1 by Roger Peppe
state: move units under service directory
257
	if !topology.HasUnit(u.key) {
51.1.3 by Frank Mueller
Done review changes.
258
		return 0, stateChanged
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
259
	}
196.3.1 by Roger Peppe
state: move units under service directory
260
	machineKey, err := topology.UnitMachineKey(u.key)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
261
	if err != nil {
51.1.3 by Frank Mueller
Done review changes.
262
		return 0, err
263
	}
196.3.5 by Roger Peppe
state: fixes for review
264
	return keySeq(machineKey), nil
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
265
}
266
267
// AssignToMachine assigns this unit to a given machine.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
268
func (u *Unit) AssignToMachine(machine *Machine) (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
269
	defer trivial.ErrorContextf(&err, "cannot assign unit %q to machine %s", u, machine)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
270
	assignUnit := func(t *topology) error {
196.3.1 by Roger Peppe
state: move units under service directory
271
		if !t.HasUnit(u.key) {
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
272
			return stateChanged
273
		}
196.3.1 by Roger Peppe
state: move units under service directory
274
		machineKey, err := t.UnitMachineKey(u.key)
51.1.3 by Frank Mueller
Done review changes.
275
		if err == unitNotAssigned {
196.3.1 by Roger Peppe
state: move units under service directory
276
			return t.AssignUnitToMachine(u.key, machine.key)
51.1.3 by Frank Mueller
Done review changes.
277
		} else if err != nil {
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
278
			return err
279
		} else if machineKey == machine.key {
280
			// Everything is fine, it's already assigned.
281
			return nil
282
		}
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
283
		return fmt.Errorf("unit already assigned to machine %d", keySeq(machineKey))
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
284
	}
75.2.1 by Frank Mueller
Continued unit implementation.
285
	return retryTopologyChange(u.st.zk, assignUnit)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
286
}
287
221.3.2 by Frank Mueller
state: Fixed review comments in unit.go.
288
var noUnusedMachines = errors.New("all machines in use")
193.1.1 by William Reade
added state.Unit.Place, and PlacementPolicy type
289
51.1.3 by Frank Mueller
Done review changes.
290
// AssignToUnusedMachine assigns u to a machine without other units.
291
// If there are no unused machines besides machine 0, an error is returned.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
292
func (u *Unit) AssignToUnusedMachine() (m *Machine, err error) {
51.1.3 by Frank Mueller
Done review changes.
293
	machineKey := ""
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
294
	assignUnusedUnit := func(t *topology) error {
196.3.1 by Roger Peppe
state: move units under service directory
295
		if !t.HasUnit(u.key) {
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
296
			return stateChanged
297
		}
51.1.3 by Frank Mueller
Done review changes.
298
		for _, machineKey = range t.MachineKeys() {
196.3.5 by Roger Peppe
state: fixes for review
299
			if keySeq(machineKey) != 0 {
51.1.3 by Frank Mueller
Done review changes.
300
				hasUnits, err := t.MachineHasUnits(machineKey)
301
				if err != nil {
302
					return err
303
				}
304
				if !hasUnits {
305
					break
306
				}
307
			}
51.1.4 by Frank Mueller
Comment change and moving of an error variable after review.
308
			// Reset machine key.
51.1.3 by Frank Mueller
Done review changes.
309
			machineKey = ""
310
		}
311
		if machineKey == "" {
193.1.1 by William Reade
added state.Unit.Place, and PlacementPolicy type
312
			return noUnusedMachines
51.1.3 by Frank Mueller
Done review changes.
313
		}
196.3.1 by Roger Peppe
state: move units under service directory
314
		if err := t.AssignUnitToMachine(u.key, machineKey); err != nil {
51.1.3 by Frank Mueller
Done review changes.
315
			return err
316
		}
317
		return nil
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
318
	}
75.2.1 by Frank Mueller
Continued unit implementation.
319
	if err := retryTopologyChange(u.st.zk, assignUnusedUnit); err != nil {
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
320
		if err == noUnusedMachines {
321
			return nil, err
322
		}
326.1.1 by Roger Peppe
can't -> cannot
323
		return nil, fmt.Errorf("cannot assign unit %q to unused machine: %v", u, err)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
324
	}
331.10.2 by Roger Peppe
state: add agent version to Machine
325
	return newMachine(u.st, machineKey), nil
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
326
}
327
32.1.5 by Frank Mueller
Moved state_test.go into package state_test. Added unit methods to Service,
328
// UnassignFromMachine removes the assignment between this unit and
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
329
// the machine it's assigned to.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
330
func (u *Unit) UnassignFromMachine() (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
331
	defer trivial.ErrorContextf(&err, "cannot unassign unit %q from machine", u.Name())
32.1.5 by Frank Mueller
Moved state_test.go into package state_test. Added unit methods to Service,
332
	unassignUnit := func(t *topology) error {
196.3.1 by Roger Peppe
state: move units under service directory
333
		if !t.HasUnit(u.key) {
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
334
			return stateChanged
32.1.5 by Frank Mueller
Moved state_test.go into package state_test. Added unit methods to Service,
335
		}
336
		// If for whatever reason it's already not assigned to a
337
		// machine, ignore it and move forward so that we don't
338
		// have to deal with conflicts.
196.3.1 by Roger Peppe
state: move units under service directory
339
		key, err := t.UnitMachineKey(u.key)
32.1.7 by Frank Mueller
Fixes after last review and also changed from "node" as identifier name
340
		if err == nil && key != "" {
196.3.1 by Roger Peppe
state: move units under service directory
341
			t.UnassignUnitFromMachine(u.key)
32.1.5 by Frank Mueller
Moved state_test.go into package state_test. Added unit methods to Service,
342
		}
343
		return nil
344
	}
75.2.1 by Frank Mueller
Continued unit implementation.
345
	return retryTopologyChange(u.st.zk, unassignUnit)
346
}
347
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
348
// NeedsUpgrade returns whether the unit needs an upgrade 
349
// and if it does, if this is forced.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
350
func (u *Unit) NeedsUpgrade() (needsUpgrade *NeedsUpgrade, err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
351
	defer trivial.ErrorContextf(&err, "cannot check if unit %q needs an upgrade", u.Name())
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
352
	yaml, _, err := u.st.zk.Get(u.zkNeedsUpgradePath())
353
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
130.1.2 by Frank Mueller
Changes after review.
354
		return &NeedsUpgrade{}, nil
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
355
	}
75.2.1 by Frank Mueller
Continued unit implementation.
356
	if err != nil {
130.1.2 by Frank Mueller
Changes after review.
357
		return nil, err
358
	}
359
	var setting needsUpgradeNode
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
360
	if err = goyaml.Unmarshal([]byte(yaml), &setting); err != nil {
130.1.2 by Frank Mueller
Changes after review.
361
		return nil, err
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
362
	}
363
	return &NeedsUpgrade{true, setting.Force}, nil
75.2.1 by Frank Mueller
Continued unit implementation.
364
}
365
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
366
// SetNeedsUpgrade informs the unit that it should perform 
367
// a regular or forced upgrade.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
368
func (u *Unit) SetNeedsUpgrade(force bool) (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
369
	defer trivial.ErrorContextf(&err, "cannot inform unit %q about upgrade", u.Name())
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
370
	setNeedsUpgrade := func(oldYaml string, stat *zookeeper.Stat) (string, error) {
130.1.2 by Frank Mueller
Changes after review.
371
		var setting needsUpgradeNode
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
372
		if oldYaml == "" {
373
			setting.Force = force
374
			newYaml, err := goyaml.Marshal(setting)
375
			if err != nil {
376
				return "", err
377
			}
378
			return string(newYaml), nil
379
		}
380
		if err := goyaml.Unmarshal([]byte(oldYaml), &setting); err != nil {
381
			return "", err
382
		}
383
		if setting.Force != force {
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
384
			return "", fmt.Errorf("upgrade already enabled")
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
385
		}
386
		return oldYaml, nil
75.2.1 by Frank Mueller
Continued unit implementation.
387
	}
130.1.1 by Frank Mueller
Added force flag to needs-upgrade methods and watcher.
388
	return u.st.zk.RetryChange(u.zkNeedsUpgradePath(), 0, zkPermAll, setNeedsUpgrade)
75.2.1 by Frank Mueller
Continued unit implementation.
389
}
390
75.2.4 by Frank Mueller
Next fixes after review.
391
// ClearNeedsUpgrade resets the upgrade notification. It is typically
75.2.1 by Frank Mueller
Continued unit implementation.
392
// done by the unit agent before beginning the upgrade.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
393
func (u *Unit) ClearNeedsUpgrade() (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
394
	defer trivial.ErrorContextf(&err, "upgrade notification for unit %q cannot be reset", u.Name())
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
395
	err = u.st.zk.Delete(u.zkNeedsUpgradePath(), -1)
92.1.1 by Roger Peppe
changes for new zk error type
396
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
75.2.1 by Frank Mueller
Continued unit implementation.
397
		// Node doesn't exist, so same state.
398
		return nil
399
	}
400
	return err
401
}
402
111.9.2 by Frank Mueller
Comment change.
403
// WatchNeedsUpgrade creates a watcher for the upgrade notification
404
// of the unit. See SetNeedsUpgrade and ClearNeedsUpgrade for details.
111.9.1 by Frank Mueller
Added NeedsUpgradeWatcher for Units and harmonized the watcher tests.
405
func (u *Unit) WatchNeedsUpgrade() *NeedsUpgradeWatcher {
406
	return newNeedsUpgradeWatcher(u.st, u.zkNeedsUpgradePath())
407
}
408
75.2.6 by Frank Mueller
Fixed comment.
409
// Resolved returns the resolved mode for the unit.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
410
func (u *Unit) Resolved() (mode ResolvedMode, err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
411
	defer trivial.ErrorContextf(&err, "cannot get resolved mode for unit %q", u)
75.2.1 by Frank Mueller
Continued unit implementation.
412
	yaml, _, err := u.st.zk.Get(u.zkResolvedPath())
92.1.1 by Roger Peppe
changes for new zk error type
413
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
75.2.1 by Frank Mueller
Continued unit implementation.
414
		// Default value.
75.2.4 by Frank Mueller
Next fixes after review.
415
		return ResolvedNone, nil
75.2.1 by Frank Mueller
Continued unit implementation.
416
	}
417
	if err != nil {
75.2.4 by Frank Mueller
Next fixes after review.
418
		return ResolvedNone, err
75.2.1 by Frank Mueller
Continued unit implementation.
419
	}
75.2.4 by Frank Mueller
Next fixes after review.
420
	setting := &struct{ Retry ResolvedMode }{}
75.2.1 by Frank Mueller
Continued unit implementation.
421
	if err = goyaml.Unmarshal([]byte(yaml), setting); err != nil {
75.2.4 by Frank Mueller
Next fixes after review.
422
		return ResolvedNone, err
423
	}
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
424
	mode = setting.Retry
111.10.1 by Frank Mueller
Rollback and ResolveWatcher changes after review.
425
	if err := validResolvedMode(mode, false); err != nil {
75.2.4 by Frank Mueller
Next fixes after review.
426
		return ResolvedNone, err
427
	}
428
	return mode, nil
75.2.1 by Frank Mueller
Continued unit implementation.
429
}
430
75.2.4 by Frank Mueller
Next fixes after review.
431
// SetResolved marks the unit as having had any previous state
432
// transition problems resolved, and informs the unit that it may
433
// attempt to reestablish normal workflow.
434
// The resolved mode parameter informs whether to attempt to 
435
// reexecute previous failed hooks or to continue as if they had 
436
// succeeded before.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
437
func (u *Unit) SetResolved(mode ResolvedMode) (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
438
	defer trivial.ErrorContextf(&err, "cannot set resolved mode for unit %q", u)
111.10.1 by Frank Mueller
Rollback and ResolveWatcher changes after review.
439
	if err := validResolvedMode(mode, false); err != nil {
75.2.2 by Frank Mueller
Changes after review.
440
		return err
75.2.1 by Frank Mueller
Continued unit implementation.
441
	}
75.2.5 by Frank Mueller
Last changes before submit.
442
	setting := &struct{ Retry ResolvedMode }{mode}
75.2.1 by Frank Mueller
Continued unit implementation.
443
	yaml, err := goyaml.Marshal(setting)
444
	if err != nil {
445
		return err
446
	}
447
	_, err = u.st.zk.Create(u.zkResolvedPath(), string(yaml), 0, zkPermAll)
92.1.1 by Roger Peppe
changes for new zk error type
448
	if zookeeper.IsError(err, zookeeper.ZNODEEXISTS) {
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
449
		return fmt.Errorf("flag already set")
75.2.1 by Frank Mueller
Continued unit implementation.
450
	}
451
	return err
452
}
453
454
// ClearResolved removes any resolved setting on the unit.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
455
func (u *Unit) ClearResolved() (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
456
	defer trivial.ErrorContextf(&err, "resolved mode for unit %q cannot be cleared", u)
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
457
	err = u.st.zk.Delete(u.zkResolvedPath(), -1)
92.1.1 by Roger Peppe
changes for new zk error type
458
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
75.2.1 by Frank Mueller
Continued unit implementation.
459
		// Node doesn't exist, so same state.
460
		return nil
461
	}
462
	return err
463
}
464
111.10.1 by Frank Mueller
Rollback and ResolveWatcher changes after review.
465
// WatchResolved returns a watcher that fires when the unit 
466
// is marked as having had its problems resolved. See 
467
// SetResolved for details.
468
func (u *Unit) WatchResolved() *ResolvedWatcher {
469
	return newResolvedWatcher(u.st, u.zkResolvedPath())
470
}
471
75.2.5 by Frank Mueller
Last changes before submit.
472
// OpenPort sets the policy of the port with protocol and number to be opened.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
473
func (u *Unit) OpenPort(protocol string, number int) (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
474
	defer trivial.ErrorContextf(&err, "cannot open port %s:%d for unit %q", protocol, number, u)
75.2.3 by Frank Mueller
Changes after review.
475
	openPort := func(oldYaml string, stat *zookeeper.Stat) (string, error) {
131.1.4 by Frank Mueller
Final changes after review.
476
		var ports openPortsNode
75.2.3 by Frank Mueller
Changes after review.
477
		if oldYaml != "" {
131.1.4 by Frank Mueller
Final changes after review.
478
			if err := goyaml.Unmarshal([]byte(oldYaml), &ports); err != nil {
75.2.1 by Frank Mueller
Continued unit implementation.
479
				return "", err
480
			}
481
		}
75.2.4 by Frank Mueller
Next fixes after review.
482
		portToOpen := Port{protocol, number}
75.2.2 by Frank Mueller
Changes after review.
483
		found := false
75.2.4 by Frank Mueller
Next fixes after review.
484
		for _, openPort := range ports.Open {
75.2.3 by Frank Mueller
Changes after review.
485
			if openPort == portToOpen {
75.2.2 by Frank Mueller
Changes after review.
486
				found = true
487
				break
75.2.1 by Frank Mueller
Continued unit implementation.
488
			}
489
		}
75.2.2 by Frank Mueller
Changes after review.
490
		if !found {
75.2.4 by Frank Mueller
Next fixes after review.
491
			ports.Open = append(ports.Open, portToOpen)
75.2.2 by Frank Mueller
Changes after review.
492
		}
75.2.3 by Frank Mueller
Changes after review.
493
		newYaml, err := goyaml.Marshal(ports)
75.2.1 by Frank Mueller
Continued unit implementation.
494
		if err != nil {
495
			return "", err
496
		}
75.2.3 by Frank Mueller
Changes after review.
497
		return string(newYaml), nil
75.2.1 by Frank Mueller
Continued unit implementation.
498
	}
499
	return u.st.zk.RetryChange(u.zkPortsPath(), 0, zkPermAll, openPort)
500
}
501
75.2.5 by Frank Mueller
Last changes before submit.
502
// ClosePort sets the policy of the port with protocol and number to be closed.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
503
func (u *Unit) ClosePort(protocol string, number int) (err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
504
	defer trivial.ErrorContextf(&err, "cannot close port %s:%d for unit %q", protocol, number, u)
75.2.3 by Frank Mueller
Changes after review.
505
	closePort := func(oldYaml string, stat *zookeeper.Stat) (string, error) {
131.1.4 by Frank Mueller
Final changes after review.
506
		var ports openPortsNode
75.2.3 by Frank Mueller
Changes after review.
507
		if oldYaml != "" {
131.1.4 by Frank Mueller
Final changes after review.
508
			if err := goyaml.Unmarshal([]byte(oldYaml), &ports); err != nil {
75.2.1 by Frank Mueller
Continued unit implementation.
509
				return "", err
510
			}
511
		}
75.2.4 by Frank Mueller
Next fixes after review.
512
		portToClose := Port{protocol, number}
75.2.3 by Frank Mueller
Changes after review.
513
		newOpenPorts := []Port{}
75.2.4 by Frank Mueller
Next fixes after review.
514
		for _, oldOpenPort := range ports.Open {
75.2.3 by Frank Mueller
Changes after review.
515
			if oldOpenPort != portToClose {
516
				newOpenPorts = append(newOpenPorts, oldOpenPort)
75.2.1 by Frank Mueller
Continued unit implementation.
517
			}
518
		}
75.2.4 by Frank Mueller
Next fixes after review.
519
		ports.Open = newOpenPorts
75.2.3 by Frank Mueller
Changes after review.
520
		newYaml, err := goyaml.Marshal(ports)
75.2.1 by Frank Mueller
Continued unit implementation.
521
		if err != nil {
522
			return "", err
523
		}
75.2.3 by Frank Mueller
Changes after review.
524
		return string(newYaml), nil
75.2.1 by Frank Mueller
Continued unit implementation.
525
	}
526
	return u.st.zk.RetryChange(u.zkPortsPath(), 0, zkPermAll, closePort)
527
}
528
529
// OpenPorts returns a slice containing the open ports of the unit.
221.3.1 by Frank Mueller
state: Improvement and decoration of errors in unit.go.
530
func (u *Unit) OpenPorts() (openPorts []Port, err error) {
354.38.8 by William Reade
moved relation/util to new trivial package; removed duplicated errorContextfs
531
	defer trivial.ErrorContextf(&err, "cannot get open ports of unit %q", u)
75.2.1 by Frank Mueller
Continued unit implementation.
532
	yaml, _, err := u.st.zk.Get(u.zkPortsPath())
92.1.1 by Roger Peppe
changes for new zk error type
533
	if zookeeper.IsError(err, zookeeper.ZNONODE) {
75.2.1 by Frank Mueller
Continued unit implementation.
534
		// Default value.
75.2.2 by Frank Mueller
Changes after review.
535
		return nil, nil
75.2.1 by Frank Mueller
Continued unit implementation.
536
	}
537
	if err != nil {
538
		return nil, err
539
	}
131.1.4 by Frank Mueller
Final changes after review.
540
	var ports openPortsNode
75.2.4 by Frank Mueller
Next fixes after review.
541
	if err = goyaml.Unmarshal([]byte(yaml), &ports); err != nil {
75.2.1 by Frank Mueller
Continued unit implementation.
542
		return nil, err
543
	}
75.2.4 by Frank Mueller
Next fixes after review.
544
	return ports.Open, nil
32.1.5 by Frank Mueller
Moved state_test.go into package state_test. Added unit methods to Service,
545
}
546
131.1.4 by Frank Mueller
Final changes after review.
547
// WatchResolved returns a watcher that fires when the
548
// list of open ports of the unit is changed.
549
// See OpenPorts for details.
131.1.1 by Frank Mueller
Added PortsWatcher including the tests.
550
func (u *Unit) WatchPorts() *PortsWatcher {
551
	return newPortsWatcher(u.st, u.zkPortsPath())
552
}
553
83.2.8 by Frank Mueller
Change Unit and Machine to directly provide agent related methods
554
// AgentAlive returns whether the respective remote agent is alive.
555
func (u *Unit) AgentAlive() (bool, error) {
556
	return presence.Alive(u.st.zk, u.zkAgentPath())
557
}
558
559
// WaitAgentAlive blocks until the respective agent is alive.
560
func (u *Unit) WaitAgentAlive(timeout time.Duration) error {
83.2.10 by Frank Mueller
Integrated new presence.WaitAlive() and added machine agent tests.
561
	err := presence.WaitAlive(u.st.zk, u.zkAgentPath(), timeout)
83.2.8 by Frank Mueller
Change Unit and Machine to directly provide agent related methods
562
	if err != nil {
221.3.3 by Frank Mueller
status: Small changes after review.
563
		return fmt.Errorf("waiting for agent of unit %q: %v", u, err)
83.2.8 by Frank Mueller
Change Unit and Machine to directly provide agent related methods
564
	}
565
	return nil
566
}
567
83.2.11 by Frank Mueller
Merged change of presence and fixed some review issues.
568
// SetAgentAlive signals that the agent for unit u is alive
569
// by starting a pinger on its presence node. It returns the
570
// started pinger.
83.2.8 by Frank Mueller
Change Unit and Machine to directly provide agent related methods
571
func (u *Unit) SetAgentAlive() (*presence.Pinger, error) {
83.2.10 by Frank Mueller
Integrated new presence.WaitAlive() and added machine agent tests.
572
	return presence.StartPinger(u.st.zk, u.zkAgentPath(), agentPingerPeriod)
83.2.7 by Frank Mueller
Finalized Agent, removed AgentEntity which has been only for testing purposes,
573
}
574
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
575
// zkPath returns the ZooKeeper base path for the unit.
576
func (u *Unit) zkPath() string {
196.3.3 by Roger Peppe
state: use a string for unit key
577
	skey, err := serviceKeyForUnitKey(u.key)
578
	if err != nil {
579
		panic(err)
580
	}
581
	return fmt.Sprintf("/services/%s/units/%s", skey, u.key)
51.1.1 by Frank Mueller
Added first parts of Machine and extended topology and Unit with
582
}
583
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
584
// zkPortsPath returns the ZooKeeper path for the open ports.
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
585
func (u *Unit) zkPortsPath() string {
196.3.1 by Roger Peppe
state: move units under service directory
586
	return u.zkPath() + "/ports"
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
587
}
588
589
// zkAgentPath returns the ZooKeeper path for the unit agent.
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
590
func (u *Unit) zkAgentPath() string {
196.3.1 by Roger Peppe
state: move units under service directory
591
	return u.zkPath() + "/agent"
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
592
}
593
75.2.2 by Frank Mueller
Changes after review.
594
// zkNeedsUpgradePath returns the ZooKeeper path for the upgrade flag.
595
func (u *Unit) zkNeedsUpgradePath() string {
196.3.1 by Roger Peppe
state: move units under service directory
596
	return u.zkPath() + "/upgrade"
75.2.1 by Frank Mueller
Continued unit implementation.
597
}
598
599
// zkResolvedPath returns the ZooKeeper path for the mark to resolve a unit.
600
func (u *Unit) zkResolvedPath() string {
196.3.1 by Roger Peppe
state: move units under service directory
601
	return u.zkPath() + "/resolved"
75.2.1 by Frank Mueller
Continued unit implementation.
602
}
603
32.1.6 by Frank Mueller
Done all last review comments without new functionality. So code can be
604
// parseUnitName parses a unit name like "wordpress/0" into
605
// its service name and sequence number parts.
196.3.5 by Roger Peppe
state: fixes for review
606
func parseUnitName(name string) (serviceName string, serviceSeq int, err error) {
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
607
	parts := strings.Split(name, "/")
608
	if len(parts) != 2 {
32.1.11 by Frank Mueller
Done review changes.
609
		return "", 0, fmt.Errorf("%q is not a valid unit name", name)
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
610
	}
331.12.2 by William Reade
address review points
611
	seq, err := strconv.Atoi(parts[1])
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
612
	if err != nil {
331.12.2 by William Reade
address review points
613
		return "", 0, fmt.Errorf("%q is not a valid unit name", name)
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
614
	}
196.3.5 by Roger Peppe
state: fixes for review
615
	return parts[0], int(seq), nil
32.1.4 by Frank Mueller
Adopted last review and integrated unit state retrieval.
616
}
75.2.4 by Frank Mueller
Next fixes after review.
617
111.10.3 by Frank Mueller
Changes after review.
618
// parseResolvedMode returns the resolved mode serialized
619
// in yaml if it is valid, or an error otherwise.
111.10.1 by Frank Mueller
Rollback and ResolveWatcher changes after review.
620
func parseResolvedMode(yaml string) (ResolvedMode, error) {
621
	var setting struct {
622
		Retry ResolvedMode
623
	}
624
	if err := goyaml.Unmarshal([]byte(yaml), &setting); err != nil {
625
		return ResolvedNone, err
626
	}
627
	mode := setting.Retry
628
	if err := validResolvedMode(mode, true); err != nil {
629
		return ResolvedNone, err
630
	}
631
	return mode, nil
632
}
633
111.10.3 by Frank Mueller
Changes after review.
634
// validResolvedMode returns an error if the provided
635
// mode isn't valid. ResolvedNone is only considered a
636
// valid mode if acceptNone is true.
111.10.1 by Frank Mueller
Rollback and ResolveWatcher changes after review.
637
func validResolvedMode(mode ResolvedMode, acceptNone bool) error {
638
	if acceptNone && mode == ResolvedNone {
639
		return nil
640
	}
75.2.4 by Frank Mueller
Next fixes after review.
641
	if mode != ResolvedRetryHooks && mode != ResolvedNoHooks {
642
		return fmt.Errorf("invalid error resolution mode: %d", mode)
643
	}
644
	return nil
645
}