Merge lp:~thumper/juju-core/provisioner-reget-state-info into lp:~juju/juju-core/trunk

Proposed by Tim Penhey
Status: Superseded
Proposed branch: lp:~thumper/juju-core/provisioner-reget-state-info
Merge into: lp:~juju/juju-core/trunk
Diff against target: 2421 lines (+589/-351)
33 files modified
.lbox.check (+1/-1)
cmd/juju/plugin_test.go (+3/-2)
cmd/juju/status.go (+88/-45)
cmd/juju/status_test.go (+97/-0)
environs/azure/environ.go (+6/-5)
environs/azure/instance.go (+2/-2)
environs/dummy/environs.go (+20/-19)
environs/ec2/ec2.go (+29/-28)
environs/ec2/export_test.go (+6/-5)
environs/ec2/live_test.go (+7/-6)
environs/interface.go (+6/-35)
environs/jujutest/livetests.go (+9/-8)
environs/jujutest/tests.go (+2/-1)
environs/maas/environ.go (+12/-11)
environs/maas/environ_test.go (+5/-4)
environs/maas/instance.go (+2/-2)
environs/openstack/local_test.go (+3/-2)
environs/openstack/provider.go (+29/-28)
environs/openstack/provider_test.go (+4/-4)
instance/instance.go (+41/-0)
juju/testing/conn.go (+2/-1)
state/container.go (+15/-3)
state/export_test.go (+4/-0)
state/machine.go (+13/-2)
state/machine_test.go (+9/-0)
state/state.go (+43/-4)
state/state_test.go (+64/-0)
worker/firewaller/firewaller_test.go (+3/-3)
worker/provisioner/broker.go (+4/-4)
worker/provisioner/export_test.go (+2/-2)
worker/provisioner/provisioner.go (+11/-15)
worker/provisioner/provisioner_task.go (+43/-32)
worker/provisioner/provisioner_test.go (+4/-77)
To merge this branch: bzr merge lp:~thumper/juju-core/provisioner-reget-state-info
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+168562@code.launchpad.net

This proposal has been superseded by a proposal from 2013-06-17.

Description of the change

Provisioner gets addresses for each new machine.

The addresses are retrieved each time we attempt to start a machine. This
allows the provisioner to take advantage of any new api or state servers that
are started as part of a block of machines being provisioned at one time.

There is some saving of addresses at this stage to make the existing tests
pass, even though this goes a little against what William has mentioned
before. If we have invalid config, the tests assume that we continue with old
info, whereas William has suggested that we pause provisioning while the
config is broken.

One behavioural tweak in this branch. If we do fail to setup authentication,
we no longer kill the task, but instead log and continue. We will attempt to
start it again next time through the loop.

https://codereview.appspot.com/9824047/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+168562_code.launchpad.net,

Message:
Please take a look.

Description:
Provisioner gets addresses for each new machine.

The addresses are retrieved each time we attempt to start a machine.
This
allows the provisioner to take advantage of any new api or state servers
that
are started as part of a block of machines being provisioned at one
time.

There is some saving of addresses at this stage to make the existing
tests
pass, even though this goes a little against what William has mentioned
before. If we have invalid config, the tests assume that we continue
with old
info, whereas William has suggested that we pause provisioning while the
config is broken.

One behavioural tweak in this branch. If we do fail to setup
authentication,
we no longer kill the task, but instead log and continue. We will
attempt to
start it again next time through the loop.

https://code.launchpad.net/~thumper/juju-core/provisioner-reget-state-info/+merge/168562

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/9824047/

Affected files:
   A [revision details]
   M worker/provisioner/export_test.go
   M worker/provisioner/provisioner.go
   M worker/provisioner/provisioner_task.go

Revision history for this message
William Reade (fwereade) wrote :

Strongly -1 on the clever bits here. Just barf on failure and I'll be
happy :).

https://codereview.appspot.com/9824047/diff/1/worker/provisioner/provisioner_task.go
File worker/provisioner/provisioner_task.go (right):

https://codereview.appspot.com/9824047/diff/1/worker/provisioner/provisioner_task.go#newcode304
worker/provisioner/provisioner_task.go:304: // Don't return a real
error, just try again next time.
This is problematic, because the watcher won't report that machine again
until it changes -- which it won't. For this to work, we need the
provisioner to be restarted regularly... but I'm hoping for a
provisioner that can run for weeks without breaking sweat.

https://codereview.appspot.com/9824047/diff/1/worker/provisioner/provisioner_task.go#newcode360
worker/provisioner/provisioner_task.go:360: // info if config becomes
invalid.
Honestly, I think this is sufficient justification to nuke the whole
provisioner; it'll be restarted in a few seconds, and for now I'd rather
not even try to provision a machine (which can be expected to cost our
users actual money) unless I'm as sure as I can be that I'll be able to
start a functioning machine agent...

https://codereview.appspot.com/9824047/

Revision history for this message
William Reade (fwereade) wrote :

Re what we do without a valid environment... hmm. Thinking aloud:

* we certainly start off with an invalid environ config, and only get a
valid one once whoever started the environment has connected for the
first time.

* today, on first startup, there will be one machine present; but it'll
be marked as provisioned already, so it won't cause env access directly;
and if we're tolerant of list-instances failures, we'll be able to
complete the first processMachines without difficulty. But I don't think
we can necessarily depend on that forever...

* in theory, it is impossible to set an invalid environ config in state,
except via the special mechanism at bootstrap time.

* in practice, an environ config can most certainly become invalid --
you can (1) replace the config with *any* valid config, which might ofc
be invalid in this context (changed environ type, for example, will
screw us most comprehensively) and (2) config update is racy anyway,
such that two users making valid changes concurrently can end up with an
environment config that reflects neither of their desires... and I bet
there's a way for that to cause invalidity. (oh, and, on inspection,
(3): it looks like values can't be dependably unset anyway. we probably
need that too. grar.)

* BUT the consequences of an invalid environ affect many more things
than just the provisioner.

* AND SO it would be folly to spend time and effort hardening the
provisioner against catastrophic environ failure when we could be fixing
the root cause.

* AND THUS you should (1) wait for a valid config at startup before
processing machines and (2) proceed otherwise as though the environment
configuration were always guaranteed consistent and valid from a juju
perspective and (3) use the time thereby saved to fix SetEnvironConfig.

* BUT ALSO remember that a valid *environ config* does not necessarily
contain valid *provider credentials*. So issues can always arise at
runtime anyway, and should I think be dealt with as follows:

   * StartInstance: record on machine via SetInstanceError.
   * AllInstances: log and ignore -- I don't think there are negative
consequences (out-of-date
     instance list is always possible anyway).
   * StopInstances: log and ignore, they'll be tried again next time we
processMachines. (We should
     add a periodic processMachines call so that we do at least handle
them in something resembling
     a timely fashion. Not too hard, I think?)

https://codereview.appspot.com/9824047/

Revision history for this message
Tim Penhey (thumper) wrote :

Please take a look.

https://codereview.appspot.com/9824047/diff/1/worker/provisioner/provisioner_task.go
File worker/provisioner/provisioner_task.go (right):

https://codereview.appspot.com/9824047/diff/1/worker/provisioner/provisioner_task.go#newcode304
worker/provisioner/provisioner_task.go:304: // Don't return a real
error, just try again next time.
On 2013/06/11 09:08:21, fwereade wrote:
> This is problematic, because the watcher won't report that machine
again until
> it changes -- which it won't. For this to work, we need the
provisioner to be
> restarted regularly... but I'm hoping for a provisioner that can run
for weeks
> without breaking sweat.

OK, reverted.

https://codereview.appspot.com/9824047/diff/1/worker/provisioner/provisioner_task.go#newcode360
worker/provisioner/provisioner_task.go:360: // info if config becomes
invalid.
On 2013/06/11 09:08:21, fwereade wrote:
> Honestly, I think this is sufficient justification to nuke the whole
> provisioner; it'll be restarted in a few seconds, and for now I'd
rather not
> even try to provision a machine (which can be expected to cost our
users actual
> money) unless I'm as sure as I can be that I'll be able to start a
functioning
> machine agent...

OK, nuking in progress.

https://codereview.appspot.com/9824047/

Revision history for this message
William Reade (fwereade) wrote :
Revision history for this message
Ian Booth (wallyworld) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.lbox.check'
--- .lbox.check 2013-04-08 13:46:30 +0000
+++ .lbox.check 2013-06-17 04:13:30 +0000
@@ -2,7 +2,7 @@
22
3set -e3set -e
44
5BADFMT=`find * -name '*.go' | xargs gofmt -l`5BADFMT=`find * -name '*.go' -not -name '.#*' | xargs gofmt -l`
6if [ -n "$BADFMT" ]; then6if [ -n "$BADFMT" ]; then
7 BADFMT=`echo "$BADFMT" | sed "s/^/ /"`7 BADFMT=`echo "$BADFMT" | sed "s/^/ /"`
8 echo -e "gofmt is sad:\n\n$BADFMT"8 echo -e "gofmt is sad:\n\n$BADFMT"
99
=== modified file 'cmd/juju/plugin_test.go'
--- cmd/juju/plugin_test.go 2013-06-13 10:56:08 +0000
+++ cmd/juju/plugin_test.go 2013-06-17 04:13:30 +0000
@@ -17,6 +17,7 @@
17)17)
1818
19type PluginSuite struct {19type PluginSuite struct {
20 testing.LoggingSuite
20 oldPath string21 oldPath string
21 home *testing.FakeHome22 home *testing.FakeHome
22}23}
@@ -24,6 +25,7 @@
24var _ = Suite(&PluginSuite{})25var _ = Suite(&PluginSuite{})
2526
26func (suite *PluginSuite) SetUpTest(c *C) {27func (suite *PluginSuite) SetUpTest(c *C) {
28 suite.LoggingSuite.SetUpTest(c)
27 suite.oldPath = os.Getenv("PATH")29 suite.oldPath = os.Getenv("PATH")
28 suite.home = testing.MakeSampleHome(c)30 suite.home = testing.MakeSampleHome(c)
29 os.Setenv("PATH", "/bin:"+testing.HomePath())31 os.Setenv("PATH", "/bin:"+testing.HomePath())
@@ -32,6 +34,7 @@
32func (suite *PluginSuite) TearDownTest(c *C) {34func (suite *PluginSuite) TearDownTest(c *C) {
33 suite.home.Restore()35 suite.home.Restore()
34 os.Setenv("PATH", suite.oldPath)36 os.Setenv("PATH", suite.oldPath)
37 suite.LoggingSuite.TearDownTest(c)
35}38}
3639
37func (*PluginSuite) TestFindPlugins(c *C) {40func (*PluginSuite) TestFindPlugins(c *C) {
@@ -195,8 +198,6 @@
195 fi198 fi
196 echo "{{.Name}} description"199 echo "{{.Name}} description"
197 exit {{.ExitStatus}}200 exit {{.ExitStatus}}
198else
199 echo "No --description" >2
200fi201fi
201202
202if [ "$1" = "--help" ]; then203if [ "$1" = "--help" ]; then
203204
=== modified file 'cmd/juju/status.go'
--- cmd/juju/status.go 2013-06-04 11:50:12 +0000
+++ cmd/juju/status.go 2013-06-17 04:13:30 +0000
@@ -10,6 +10,7 @@
10 "launchpad.net/juju-core/charm"10 "launchpad.net/juju-core/charm"
11 "launchpad.net/juju-core/cmd"11 "launchpad.net/juju-core/cmd"
12 "launchpad.net/juju-core/environs"12 "launchpad.net/juju-core/environs"
13 "launchpad.net/juju-core/instance"
13 "launchpad.net/juju-core/juju"14 "launchpad.net/juju-core/juju"
14 "launchpad.net/juju-core/state"15 "launchpad.net/juju-core/state"
15 "launchpad.net/juju-core/state/api/params"16 "launchpad.net/juju-core/state/api/params"
@@ -42,8 +43,8 @@
42}43}
4344
44type statusContext struct {45type statusContext struct {
45 instances map[state.InstanceId]environs.Instance46 instances map[state.InstanceId]instance.Instance
46 machines map[string]*state.Machine47 machines map[string][]*state.Machine
47 services map[string]*state.Service48 services map[string]*state.Service
48 units map[string]map[string]*state.Unit49 units map[string]map[string]*state.Unit
49}50}
@@ -55,14 +56,14 @@
55 }56 }
56 defer conn.Close()57 defer conn.Close()
5758
58 var ctxt statusContext59 var context statusContext
59 if ctxt.machines, err = fetchAllMachines(conn.State); err != nil {60 if context.machines, err = fetchAllMachines(conn.State); err != nil {
60 return err61 return err
61 }62 }
62 if ctxt.services, ctxt.units, err = fetchAllServicesAndUnits(conn.State); err != nil {63 if context.services, context.units, err = fetchAllServicesAndUnits(conn.State); err != nil {
63 return err64 return err
64 }65 }
65 ctxt.instances, err = fetchAllInstances(conn.Environ)66 context.instances, err = fetchAllInstances(conn.Environ)
66 if err != nil {67 if err != nil {
67 // We cannot see instances from the environment, but68 // We cannot see instances from the environment, but
68 // there's still lots of potentially useful info to print.69 // there's still lots of potentially useful info to print.
@@ -72,15 +73,15 @@
72 Machines map[string]machineStatus `json:"machines"`73 Machines map[string]machineStatus `json:"machines"`
73 Services map[string]serviceStatus `json:"services"`74 Services map[string]serviceStatus `json:"services"`
74 }{75 }{
75 Machines: ctxt.processMachines(),76 Machines: context.processMachines(),
76 Services: ctxt.processServices(),77 Services: context.processServices(),
77 }78 }
78 return c.out.Write(ctx, result)79 return c.out.Write(ctx, result)
79}80}
8081
81// fetchAllInstances returns a map from instance id to instance.82// fetchAllInstances returns a map from instance id to instance.
82func fetchAllInstances(env environs.Environ) (map[state.InstanceId]environs.Instance, error) {83func fetchAllInstances(env environs.Environ) (map[state.InstanceId]instance.Instance, error) {
83 m := make(map[state.InstanceId]environs.Instance)84 m := make(map[state.InstanceId]instance.Instance)
84 insts, err := env.AllInstances()85 insts, err := env.AllInstances()
85 if err != nil {86 if err != nil {
86 return nil, err87 return nil, err
@@ -91,15 +92,29 @@
91 return m, nil92 return m, nil
92}93}
9394
94// fetchAllMachines returns a map from machine id to machine.95// fetchAllMachines returns a map from top level machine id to machines, where machines[0] is the host
95func fetchAllMachines(st *state.State) (map[string]*state.Machine, error) {96// machine and machines[1..n] are any containers (including nested ones).
96 v := make(map[string]*state.Machine)97func fetchAllMachines(st *state.State) (map[string][]*state.Machine, error) {
98 v := make(map[string][]*state.Machine)
97 machines, err := st.AllMachines()99 machines, err := st.AllMachines()
98 if err != nil {100 if err != nil {
99 return nil, err101 return nil, err
100 }102 }
103 // AllMachines gives us machines sorted by id.
101 for _, m := range machines {104 for _, m := range machines {
102 v[m.Id()] = m105 parentId, ok := m.ParentId()
106 if !ok {
107 // Only top level host machines go directly into the machine map.
108 v[m.Id()] = []*state.Machine{m}
109 } else {
110 topParentId := state.TopParentId(m.Id())
111 machines, ok := v[topParentId]
112 if !ok {
113 panic(fmt.Errorf("unexpected machine id %q", parentId))
114 }
115 machines = append(machines, m)
116 v[topParentId] = machines
117 }
103 }118 }
104 return v, nil119 return v, nil
105}120}
@@ -128,15 +143,40 @@
128 return svcMap, unitMap, nil143 return svcMap, unitMap, nil
129}144}
130145
131func (ctxt *statusContext) processMachines() map[string]machineStatus {146func (context *statusContext) processMachines() map[string]machineStatus {
132 machinesMap := make(map[string]machineStatus)147 machinesMap := make(map[string]machineStatus)
133 for _, m := range ctxt.machines {148 for id, machines := range context.machines {
134 machinesMap[m.Id()] = ctxt.processMachine(m)149 hostStatus := context.makeMachineStatus(machines[0])
150 context.processMachine(machines, &hostStatus, 0)
151 machinesMap[id] = hostStatus
135 }152 }
136 return machinesMap153 return machinesMap
137}154}
138155
139func (ctxt *statusContext) processMachine(machine *state.Machine) (status machineStatus) {156func (context *statusContext) processMachine(machines []*state.Machine, host *machineStatus, startIndex int) (nextIndex int) {
157 nextIndex = startIndex + 1
158 currentHost := host
159 var previousContainer *machineStatus
160 for nextIndex < len(machines) {
161 machine := machines[nextIndex]
162 container := context.makeMachineStatus(machine)
163 if currentHost.Id == state.ParentId(machine.Id()) {
164 currentHost.Containers[machine.Id()] = container
165 previousContainer = &container
166 nextIndex++
167 } else {
168 if state.NestingLevel(machine.Id()) > state.NestingLevel(previousContainer.Id) {
169 nextIndex = context.processMachine(machines, previousContainer, nextIndex-1)
170 } else {
171 break
172 }
173 }
174 }
175 return
176}
177
178func (context *statusContext) makeMachineStatus(machine *state.Machine) (status machineStatus) {
179 status.Id = machine.Id()
140 status.Life,180 status.Life,
141 status.AgentVersion,181 status.AgentVersion,
142 status.AgentState,182 status.AgentState,
@@ -146,7 +186,7 @@
146 instid, ok := machine.InstanceId()186 instid, ok := machine.InstanceId()
147 if ok {187 if ok {
148 status.InstanceId = instid188 status.InstanceId = instid
149 instance, ok := ctxt.instances[instid]189 instance, ok := context.instances[instid]
150 if ok {190 if ok {
151 status.DNSName, _ = instance.DNSName()191 status.DNSName, _ = instance.DNSName()
152 } else {192 } else {
@@ -163,43 +203,44 @@
163 // in the output.203 // in the output.
164 status.AgentState = ""204 status.AgentState = ""
165 }205 }
206 status.Containers = make(map[string]machineStatus)
166 return207 return
167}208}
168209
169func (ctxt *statusContext) processServices() map[string]serviceStatus {210func (context *statusContext) processServices() map[string]serviceStatus {
170 servicesMap := make(map[string]serviceStatus)211 servicesMap := make(map[string]serviceStatus)
171 for _, s := range ctxt.services {212 for _, s := range context.services {
172 servicesMap[s.Name()] = ctxt.processService(s)213 servicesMap[s.Name()] = context.processService(s)
173 }214 }
174 return servicesMap215 return servicesMap
175}216}
176217
177func (ctxt *statusContext) processService(service *state.Service) (status serviceStatus) {218func (context *statusContext) processService(service *state.Service) (status serviceStatus) {
178 url, _ := service.CharmURL()219 url, _ := service.CharmURL()
179 status.Charm = url.String()220 status.Charm = url.String()
180 status.Exposed = service.IsExposed()221 status.Exposed = service.IsExposed()
181 status.Life = processLife(service)222 status.Life = processLife(service)
182 var err error223 var err error
183 status.Relations, status.SubordinateTo, err = ctxt.processRelations(service)224 status.Relations, status.SubordinateTo, err = context.processRelations(service)
184 if err != nil {225 if err != nil {
185 status.Err = err226 status.Err = err
186 return227 return
187 }228 }
188 if service.IsPrincipal() {229 if service.IsPrincipal() {
189 status.Units = ctxt.processUnits(ctxt.units[service.Name()])230 status.Units = context.processUnits(context.units[service.Name()])
190 }231 }
191 return status232 return status
192}233}
193234
194func (ctxt *statusContext) processUnits(units map[string]*state.Unit) map[string]unitStatus {235func (context *statusContext) processUnits(units map[string]*state.Unit) map[string]unitStatus {
195 unitsMap := make(map[string]unitStatus)236 unitsMap := make(map[string]unitStatus)
196 for _, unit := range units {237 for _, unit := range units {
197 unitsMap[unit.Name()] = ctxt.processUnit(unit)238 unitsMap[unit.Name()] = context.processUnit(unit)
198 }239 }
199 return unitsMap240 return unitsMap
200}241}
201242
202func (ctxt *statusContext) processUnit(unit *state.Unit) (status unitStatus) {243func (context *statusContext) processUnit(unit *state.Unit) (status unitStatus) {
203 status.PublicAddress, _ = unit.PublicAddress()244 status.PublicAddress, _ = unit.PublicAddress()
204 if unit.IsPrincipal() {245 if unit.IsPrincipal() {
205 status.Machine, _ = unit.AssignedMachineId()246 status.Machine, _ = unit.AssignedMachineId()
@@ -212,16 +253,16 @@
212 if subUnits := unit.SubordinateNames(); len(subUnits) > 0 {253 if subUnits := unit.SubordinateNames(); len(subUnits) > 0 {
213 status.Subordinates = make(map[string]unitStatus)254 status.Subordinates = make(map[string]unitStatus)
214 for _, name := range subUnits {255 for _, name := range subUnits {
215 subUnit := ctxt.unitByName(name)256 subUnit := context.unitByName(name)
216 status.Subordinates[name] = ctxt.processUnit(subUnit)257 status.Subordinates[name] = context.processUnit(subUnit)
217 }258 }
218 }259 }
219 return260 return
220}261}
221262
222func (ctxt *statusContext) unitByName(name string) *state.Unit {263func (context *statusContext) unitByName(name string) *state.Unit {
223 serviceName := strings.Split(name, "/")[0]264 serviceName := strings.Split(name, "/")[0]
224 return ctxt.units[serviceName][name]265 return context.units[serviceName][name]
225}266}
226267
227func (*statusContext) processRelations(service *state.Service) (related map[string][]string, subord []string, err error) {268func (*statusContext) processRelations(service *state.Service) (related map[string][]string, subord []string, err error) {
@@ -311,15 +352,17 @@
311}352}
312353
313type machineStatus struct {354type machineStatus struct {
314 Err error `json:"-" yaml:",omitempty"`355 Err error `json:"-" yaml:",omitempty"`
315 AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"`356 AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"`
316 AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"`357 AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"`
317 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"`358 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"`
318 DNSName string `json:"dns-name,omitempty" yaml:"dns-name,omitempty"`359 DNSName string `json:"dns-name,omitempty" yaml:"dns-name,omitempty"`
319 InstanceId state.InstanceId `json:"instance-id,omitempty" yaml:"instance-id,omitempty"`360 InstanceId state.InstanceId `json:"instance-id,omitempty" yaml:"instance-id,omitempty"`
320 InstanceState string `json:"instance-state,omitempty" yaml:"instance-state,omitempty"`361 InstanceState string `json:"instance-state,omitempty" yaml:"instance-state,omitempty"`
321 Life string `json:"life,omitempty" yaml:"life,omitempty"`362 Life string `json:"life,omitempty" yaml:"life,omitempty"`
322 Series string `json:"series,omitempty" yaml:"series,omitempty"`363 Series string `json:"series,omitempty" yaml:"series,omitempty"`
364 Id string `json:"-" yaml:"-"`
365 Containers map[string]machineStatus `json:"containers,omitempty" yaml:"containers,omitempty"`
323}366}
324367
325// A goyaml bug means we can't declare these types368// A goyaml bug means we can't declare these types
326369
=== modified file 'cmd/juju/status_test.go'
--- cmd/juju/status_test.go 2013-05-27 03:00:31 +0000
+++ cmd/juju/status_test.go 2013-06-17 04:13:30 +0000
@@ -117,6 +117,32 @@
117 "instance-id": "dummyenv-4",117 "instance-id": "dummyenv-4",
118 "series": "series",118 "series": "series",
119 }119 }
120 machine1WithContainers = M{
121 "agent-state": "started",
122 "containers": M{
123 "1/lxc/0": M{
124 "agent-state": "started",
125 "containers": M{
126 "1/lxc/0/lxc/0": M{
127 "agent-state": "started",
128 "dns-name": "dummyenv-3.dns",
129 "instance-id": "dummyenv-3",
130 "series": "series",
131 },
132 },
133 "dns-name": "dummyenv-2.dns",
134 "instance-id": "dummyenv-2",
135 "series": "series",
136 },
137 "1/lxc/1": M{
138 "instance-id": "pending",
139 "series": "series",
140 },
141 },
142 "dns-name": "dummyenv-1.dns",
143 "instance-id": "dummyenv-1",
144 "series": "series",
145 }
120 unexposedService = M{146 unexposedService = M{
121 "charm": "local:series/dummy-1",147 "charm": "local:series/dummy-1",
122 "exposed": false,148 "exposed": false,
@@ -716,6 +742,59 @@
716 },742 },
717 },743 },
718 },744 },
745 ), test(
746 "machines with containers",
747 addMachine{"0", state.JobManageEnviron},
748 startAliveMachine{"0"},
749 setMachineStatus{"0", params.StatusStarted, ""},
750 addCharm{"mysql"},
751 addService{"mysql", "mysql"},
752 setServiceExposed{"mysql", true},
753
754 addMachine{"1", state.JobHostUnits},
755 startAliveMachine{"1"},
756 setMachineStatus{"1", params.StatusStarted, ""},
757 addAliveUnit{"mysql", "1"},
758 setUnitStatus{"mysql/0", params.StatusStarted, ""},
759
760 // A container on machine 1.
761 addContainer{"1", "1/lxc/0", state.JobHostUnits},
762 startAliveMachine{"1/lxc/0"},
763 setMachineStatus{"1/lxc/0", params.StatusStarted, ""},
764 addAliveUnit{"mysql", "1/lxc/0"},
765 setUnitStatus{"mysql/1", params.StatusStarted, ""},
766 addContainer{"1", "1/lxc/1", state.JobHostUnits},
767
768 // A nested container.
769 addContainer{"1/lxc/0", "1/lxc/0/lxc/0", state.JobHostUnits},
770 startAliveMachine{"1/lxc/0/lxc/0"},
771 setMachineStatus{"1/lxc/0/lxc/0", params.StatusStarted, ""},
772
773 expect{
774 "machines with nested containers",
775 M{
776 "machines": M{
777 "0": machine0,
778 "1": machine1WithContainers,
779 },
780 "services": M{
781 "mysql": M{
782 "charm": "local:series/mysql-1",
783 "exposed": true,
784 "units": M{
785 "mysql/0": M{
786 "machine": "1",
787 "agent-state": "started",
788 },
789 "mysql/1": M{
790 "machine": "1/lxc/0",
791 "agent-state": "started",
792 },
793 },
794 },
795 },
796 },
797 },
719 ),798 ),
720}799}
721800
@@ -732,6 +811,24 @@
732 c.Assert(m.Id(), Equals, am.machineId)811 c.Assert(m.Id(), Equals, am.machineId)
733}812}
734813
814type addContainer struct {
815 parentId string
816 machineId string
817 job state.MachineJob
818}
819
820func (ac addContainer) step(c *C, ctx *context) {
821 params := &state.AddMachineParams{
822 ParentId: ac.parentId,
823 ContainerType: state.LXC,
824 Series: "series",
825 Jobs: []state.MachineJob{ac.job},
826 }
827 m, err := ctx.st.AddMachineWithConstraints(params)
828 c.Assert(err, IsNil)
829 c.Assert(m.Id(), Equals, ac.machineId)
830}
831
735type startMachine struct {832type startMachine struct {
736 machineId string833 machineId string
737}834}
738835
=== modified file 'environs/azure/environ.go'
--- environs/azure/environ.go 2013-06-11 12:41:10 +0000
+++ environs/azure/environ.go 2013-06-17 04:13:30 +0000
@@ -7,6 +7,7 @@
7 "launchpad.net/juju-core/constraints"7 "launchpad.net/juju-core/constraints"
8 "launchpad.net/juju-core/environs"8 "launchpad.net/juju-core/environs"
9 "launchpad.net/juju-core/environs/config"9 "launchpad.net/juju-core/environs/config"
10 "launchpad.net/juju-core/instance"
10 "launchpad.net/juju-core/state"11 "launchpad.net/juju-core/state"
11 "launchpad.net/juju-core/state/api"12 "launchpad.net/juju-core/state/api"
12 "launchpad.net/juju-core/state/api/params"13 "launchpad.net/juju-core/state/api/params"
@@ -43,22 +44,22 @@
43}44}
4445
45// StartInstance is specified in the Environ interface.46// StartInstance is specified in the Environ interface.
46func (env *azureEnviron) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance, error) {47func (env *azureEnviron) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, error) {
47 panic("unimplemented")48 panic("unimplemented")
48}49}
4950
50// StopInstances is specified in the Environ interface.51// StopInstances is specified in the Environ interface.
51func (env *azureEnviron) StopInstances([]environs.Instance) error {52func (env *azureEnviron) StopInstances([]instance.Instance) error {
52 panic("unimplemented")53 panic("unimplemented")
53}54}
5455
55// Instances is specified in the Environ interface.56// Instances is specified in the Environ interface.
56func (env *azureEnviron) Instances(ids []state.InstanceId) ([]environs.Instance, error) {57func (env *azureEnviron) Instances(ids []state.InstanceId) ([]instance.Instance, error) {
57 panic("unimplemented")58 panic("unimplemented")
58}59}
5960
60// AllInstances is specified in the Environ interface.61// AllInstances is specified in the Environ interface.
61func (env *azureEnviron) AllInstances() ([]environs.Instance, error) {62func (env *azureEnviron) AllInstances() ([]instance.Instance, error) {
62 panic("unimplemented")63 panic("unimplemented")
63}64}
6465
@@ -73,7 +74,7 @@
73}74}
7475
75// Destroy is specified in the Environ interface.76// Destroy is specified in the Environ interface.
76func (env *azureEnviron) Destroy(insts []environs.Instance) error {77func (env *azureEnviron) Destroy(insts []instance.Instance) error {
77 panic("unimplemented")78 panic("unimplemented")
78}79}
7980
8081
=== modified file 'environs/azure/instance.go'
--- environs/azure/instance.go 2013-06-11 12:41:10 +0000
+++ environs/azure/instance.go 2013-06-17 04:13:30 +0000
@@ -4,7 +4,7 @@
4package azure4package azure
55
6import (6import (
7 "launchpad.net/juju-core/environs"7 "launchpad.net/juju-core/instance"
8 "launchpad.net/juju-core/state"8 "launchpad.net/juju-core/state"
9 "launchpad.net/juju-core/state/api/params"9 "launchpad.net/juju-core/state/api/params"
10)10)
@@ -12,7 +12,7 @@
12type azureInstance struct{}12type azureInstance struct{}
1313
14// azureInstance implements Instance.14// azureInstance implements Instance.
15var _ environs.Instance = (*azureInstance)(nil)15var _ instance.Instance = (*azureInstance)(nil)
1616
17// Id is specified in the Instance interface.17// Id is specified in the Instance interface.
18func (instance *azureInstance) Id() state.InstanceId {18func (instance *azureInstance) Id() state.InstanceId {
1919
=== modified file 'environs/dummy/environs.go'
--- environs/dummy/environs.go 2013-05-28 08:05:49 +0000
+++ environs/dummy/environs.go 2013-06-17 04:13:30 +0000
@@ -29,6 +29,7 @@
29 "launchpad.net/juju-core/environs"29 "launchpad.net/juju-core/environs"
30 "launchpad.net/juju-core/environs/config"30 "launchpad.net/juju-core/environs/config"
31 envtesting "launchpad.net/juju-core/environs/testing"31 envtesting "launchpad.net/juju-core/environs/testing"
32 "launchpad.net/juju-core/instance"
32 "launchpad.net/juju-core/log"33 "launchpad.net/juju-core/log"
33 "launchpad.net/juju-core/schema"34 "launchpad.net/juju-core/schema"
34 "launchpad.net/juju-core/state"35 "launchpad.net/juju-core/state"
@@ -75,7 +76,7 @@
75 Env string76 Env string
76 MachineId string77 MachineId string
77 MachineNonce string78 MachineNonce string
78 Instance environs.Instance79 Instance instance.Instance
79 Constraints constraints.Value80 Constraints constraints.Value
80 Info *state.Info81 Info *state.Info
81 APIInfo *api.Info82 APIInfo *api.Info
@@ -84,7 +85,7 @@
8485
85type OpStopInstances struct {86type OpStopInstances struct {
86 Env string87 Env string
87 Instances []environs.Instance88 Instances []instance.Instance
88}89}
8990
90type OpOpenPorts struct {91type OpOpenPorts struct {
@@ -122,7 +123,7 @@
122 ops chan<- Operation123 ops chan<- Operation
123 mu sync.Mutex124 mu sync.Mutex
124 maxId int // maximum instance id allocated so far.125 maxId int // maximum instance id allocated so far.
125 insts map[state.InstanceId]*instance126 insts map[state.InstanceId]*dummyInstance
126 globalPorts map[params.Port]bool127 globalPorts map[params.Port]bool
127 firewallMode config.FirewallMode128 firewallMode config.FirewallMode
128 bootstrapped bool129 bootstrapped bool
@@ -224,7 +225,7 @@
224 s := &environState{225 s := &environState{
225 name: name,226 name: name,
226 ops: ops,227 ops: ops,
227 insts: make(map[state.InstanceId]*instance),228 insts: make(map[state.InstanceId]*dummyInstance),
228 globalPorts: make(map[params.Port]bool),229 globalPorts: make(map[params.Port]bool),
229 firewallMode: fwmode,230 firewallMode: fwmode,
230 }231 }
@@ -515,7 +516,7 @@
515 return nil516 return nil
516}517}
517518
518func (e *environ) Destroy([]environs.Instance) error {519func (e *environ) Destroy([]instance.Instance) error {
519 defer delay()520 defer delay()
520 if err := e.checkBroken("Destroy"); err != nil {521 if err := e.checkBroken("Destroy"); err != nil {
521 return err522 return err
@@ -527,7 +528,7 @@
527 return nil528 return nil
528}529}
529530
530func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance, error) {531func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, error) {
531 defer delay()532 defer delay()
532 log.Infof("environs/dummy: dummy startinstance, machine %s", machineId)533 log.Infof("environs/dummy: dummy startinstance, machine %s", machineId)
533 if err := e.checkBroken("StartInstance"); err != nil {534 if err := e.checkBroken("StartInstance"); err != nil {
@@ -552,7 +553,7 @@
552 if apiInfo.Tag != state.MachineTag(machineId) {553 if apiInfo.Tag != state.MachineTag(machineId) {
553 return nil, fmt.Errorf("entity tag must match started machine")554 return nil, fmt.Errorf("entity tag must match started machine")
554 }555 }
555 i := &instance{556 i := &dummyInstance{
556 state: e.state,557 state: e.state,
557 id: state.InstanceId(fmt.Sprintf("%s-%d", e.state.name, e.state.maxId)),558 id: state.InstanceId(fmt.Sprintf("%s-%d", e.state.name, e.state.maxId)),
558 ports: make(map[params.Port]bool),559 ports: make(map[params.Port]bool),
@@ -574,7 +575,7 @@
574 return i, nil575 return i, nil
575}576}
576577
577func (e *environ) StopInstances(is []environs.Instance) error {578func (e *environ) StopInstances(is []instance.Instance) error {
578 defer delay()579 defer delay()
579 if err := e.checkBroken("StopInstance"); err != nil {580 if err := e.checkBroken("StopInstance"); err != nil {
580 return err581 return err
@@ -582,7 +583,7 @@
582 e.state.mu.Lock()583 e.state.mu.Lock()
583 defer e.state.mu.Unlock()584 defer e.state.mu.Unlock()
584 for _, i := range is {585 for _, i := range is {
585 delete(e.state.insts, i.(*instance).id)586 delete(e.state.insts, i.(*dummyInstance).id)
586 }587 }
587 e.state.ops <- OpStopInstances{588 e.state.ops <- OpStopInstances{
588 Env: e.state.name,589 Env: e.state.name,
@@ -591,7 +592,7 @@
591 return nil592 return nil
592}593}
593594
594func (e *environ) Instances(ids []state.InstanceId) (insts []environs.Instance, err error) {595func (e *environ) Instances(ids []state.InstanceId) (insts []instance.Instance, err error) {
595 defer delay()596 defer delay()
596 if err := e.checkBroken("Instances"); err != nil {597 if err := e.checkBroken("Instances"); err != nil {
597 return nil, err598 return nil, err
@@ -616,12 +617,12 @@
616 return617 return
617}618}
618619
619func (e *environ) AllInstances() ([]environs.Instance, error) {620func (e *environ) AllInstances() ([]instance.Instance, error) {
620 defer delay()621 defer delay()
621 if err := e.checkBroken("AllInstances"); err != nil {622 if err := e.checkBroken("AllInstances"); err != nil {
622 return nil, err623 return nil, err
623 }624 }
624 var insts []environs.Instance625 var insts []instance.Instance
625 e.state.mu.Lock()626 e.state.mu.Lock()
626 defer e.state.mu.Unlock()627 defer e.state.mu.Unlock()
627 for _, v := range e.state.insts {628 for _, v := range e.state.insts {
@@ -674,7 +675,7 @@
674 return &providerInstance675 return &providerInstance
675}676}
676677
677type instance struct {678type dummyInstance struct {
678 state *environState679 state *environState
679 ports map[params.Port]bool680 ports map[params.Port]bool
680 id state.InstanceId681 id state.InstanceId
@@ -682,20 +683,20 @@
682 series string683 series string
683}684}
684685
685func (inst *instance) Id() state.InstanceId {686func (inst *dummyInstance) Id() state.InstanceId {
686 return inst.id687 return inst.id
687}688}
688689
689func (inst *instance) DNSName() (string, error) {690func (inst *dummyInstance) DNSName() (string, error) {
690 defer delay()691 defer delay()
691 return string(inst.id) + ".dns", nil692 return string(inst.id) + ".dns", nil
692}693}
693694
694func (inst *instance) WaitDNSName() (string, error) {695func (inst *dummyInstance) WaitDNSName() (string, error) {
695 return inst.DNSName()696 return inst.DNSName()
696}697}
697698
698func (inst *instance) OpenPorts(machineId string, ports []params.Port) error {699func (inst *dummyInstance) OpenPorts(machineId string, ports []params.Port) error {
699 defer delay()700 defer delay()
700 log.Infof("environs/dummy: openPorts %s, %#v", machineId, ports)701 log.Infof("environs/dummy: openPorts %s, %#v", machineId, ports)
701 if inst.state.firewallMode != config.FwInstance {702 if inst.state.firewallMode != config.FwInstance {
@@ -719,7 +720,7 @@
719 return nil720 return nil
720}721}
721722
722func (inst *instance) ClosePorts(machineId string, ports []params.Port) error {723func (inst *dummyInstance) ClosePorts(machineId string, ports []params.Port) error {
723 defer delay()724 defer delay()
724 if inst.state.firewallMode != config.FwInstance {725 if inst.state.firewallMode != config.FwInstance {
725 return fmt.Errorf("invalid firewall mode for closing ports on instance: %q",726 return fmt.Errorf("invalid firewall mode for closing ports on instance: %q",
@@ -742,7 +743,7 @@
742 return nil743 return nil
743}744}
744745
745func (inst *instance) Ports(machineId string) (ports []params.Port, err error) {746func (inst *dummyInstance) Ports(machineId string) (ports []params.Port, err error) {
746 defer delay()747 defer delay()
747 if inst.state.firewallMode != config.FwInstance {748 if inst.state.firewallMode != config.FwInstance {
748 return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q",749 return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q",
749750
=== modified file 'environs/ec2/ec2.go'
--- environs/ec2/ec2.go 2013-06-10 10:30:13 +0000
+++ environs/ec2/ec2.go 2013-06-17 04:13:30 +0000
@@ -17,6 +17,7 @@
17 "launchpad.net/juju-core/environs/instances"17 "launchpad.net/juju-core/environs/instances"
18 "launchpad.net/juju-core/environs/tools"18 "launchpad.net/juju-core/environs/tools"
19 "launchpad.net/juju-core/errors"19 "launchpad.net/juju-core/errors"
20 "launchpad.net/juju-core/instance"
20 "launchpad.net/juju-core/log"21 "launchpad.net/juju-core/log"
21 "launchpad.net/juju-core/state"22 "launchpad.net/juju-core/state"
22 "launchpad.net/juju-core/state/api"23 "launchpad.net/juju-core/state/api"
@@ -65,22 +66,22 @@
6566
66var _ environs.Environ = (*environ)(nil)67var _ environs.Environ = (*environ)(nil)
6768
68type instance struct {69type ec2Instance struct {
69 e *environ70 e *environ
70 *ec2.Instance71 *ec2.Instance
71}72}
7273
73func (inst *instance) String() string {74func (inst *ec2Instance) String() string {
74 return inst.InstanceId75 return inst.InstanceId
75}76}
7677
77var _ environs.Instance = (*instance)(nil)78var _ instance.Instance = (*ec2Instance)(nil)
7879
79func (inst *instance) Id() state.InstanceId {80func (inst *ec2Instance) Id() state.InstanceId {
80 return state.InstanceId(inst.InstanceId)81 return state.InstanceId(inst.InstanceId)
81}82}
8283
83func (inst *instance) DNSName() (string, error) {84func (inst *ec2Instance) DNSName() (string, error) {
84 if inst.Instance.DNSName != "" {85 if inst.Instance.DNSName != "" {
85 return inst.Instance.DNSName, nil86 return inst.Instance.DNSName, nil
86 }87 }
@@ -90,18 +91,18 @@
90 if err != nil {91 if err != nil {
91 return "", err92 return "", err
92 }93 }
93 freshInst := insts[0].(*instance).Instance94 freshInst := insts[0].(*ec2Instance).Instance
94 if freshInst.DNSName == "" {95 if freshInst.DNSName == "" {
95 return "", environs.ErrNoDNSName96 return "", instance.ErrNoDNSName
96 }97 }
97 inst.Instance.DNSName = freshInst.DNSName98 inst.Instance.DNSName = freshInst.DNSName
98 return freshInst.DNSName, nil99 return freshInst.DNSName, nil
99}100}
100101
101func (inst *instance) WaitDNSName() (string, error) {102func (inst *ec2Instance) WaitDNSName() (string, error) {
102 for a := longAttempt.Start(); a.Next(); {103 for a := longAttempt.Start(); a.Next(); {
103 name, err := inst.DNSName()104 name, err := inst.DNSName()
104 if err == nil || err != environs.ErrNoDNSName {105 if err == nil || err != instance.ErrNoDNSName {
105 return name, err106 return name, err
106 }107 }
107 }108 }
@@ -278,7 +279,7 @@
278 if err != nil {279 if err != nil {
279 // ignore error on StopInstance because the previous error is280 // ignore error on StopInstance because the previous error is
280 // more important.281 // more important.
281 e.StopInstances([]environs.Instance{inst})282 e.StopInstances([]instance.Instance{inst})
282 return fmt.Errorf("cannot save state: %v", err)283 return fmt.Errorf("cannot save state: %v", err)
283 }284 }
284 // TODO make safe in the case of racing Bootstraps285 // TODO make safe in the case of racing Bootstraps
@@ -313,7 +314,7 @@
313 if inst == nil {314 if inst == nil {
314 continue315 continue
315 }316 }
316 name := inst.(*instance).Instance.DNSName317 name := inst.(*ec2Instance).Instance.DNSName
317 if name != "" {318 if name != "" {
318 statePortSuffix := fmt.Sprintf(":%d", config.StatePort())319 statePortSuffix := fmt.Sprintf(":%d", config.StatePort())
319 apiPortSuffix := fmt.Sprintf(":%d", config.APIPort())320 apiPortSuffix := fmt.Sprintf(":%d", config.APIPort())
@@ -340,7 +341,7 @@
340 return []string{imagemetadata.DefaultBaseURL}, nil341 return []string{imagemetadata.DefaultBaseURL}, nil
341}342}
342343
343func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance, error) {344func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, error) {
344 possibleTools, err := environs.FindInstanceTools(e, series, cons)345 possibleTools, err := environs.FindInstanceTools(e, series, cons)
345 if err != nil {346 if err != nil {
346 return nil, err347 return nil, err
@@ -397,7 +398,7 @@
397398
398// startInstance is the internal version of StartInstance, used by Bootstrap399// startInstance is the internal version of StartInstance, used by Bootstrap
399// as well as via StartInstance itself.400// as well as via StartInstance itself.
400func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, error) {401func (e *environ) startInstance(scfg *startInstanceParams) (instance.Instance, error) {
401 series := scfg.possibleTools.Series()402 series := scfg.possibleTools.Series()
402 if len(series) != 1 {403 if len(series) != 1 {
403 return nil, fmt.Errorf("expected single series, got %v", series)404 return nil, fmt.Errorf("expected single series, got %v", series)
@@ -455,15 +456,15 @@
455 if len(instances.Instances) != 1 {456 if len(instances.Instances) != 1 {
456 return nil, fmt.Errorf("expected 1 started instance, got %d", len(instances.Instances))457 return nil, fmt.Errorf("expected 1 started instance, got %d", len(instances.Instances))
457 }458 }
458 inst := &instance{e, &instances.Instances[0]}459 inst := &ec2Instance{e, &instances.Instances[0]}
459 log.Infof("environs/ec2: started instance %q", inst.Id())460 log.Infof("environs/ec2: started instance %q", inst.Id())
460 return inst, nil461 return inst, nil
461}462}
462463
463func (e *environ) StopInstances(insts []environs.Instance) error {464func (e *environ) StopInstances(insts []instance.Instance) error {
464 ids := make([]state.InstanceId, len(insts))465 ids := make([]state.InstanceId, len(insts))
465 for i, inst := range insts {466 for i, inst := range insts {
466 ids[i] = inst.(*instance).Id()467 ids[i] = inst.(*ec2Instance).Id()
467 }468 }
468 return e.terminateInstances(ids)469 return e.terminateInstances(ids)
469}470}
@@ -472,7 +473,7 @@
472// id whose corresponding insts slot is nil.473// id whose corresponding insts slot is nil.
473// It returns environs.ErrPartialInstances if the insts474// It returns environs.ErrPartialInstances if the insts
474// slice has not been completely filled.475// slice has not been completely filled.
475func (e *environ) gatherInstances(ids []state.InstanceId, insts []environs.Instance) error {476func (e *environ) gatherInstances(ids []state.InstanceId, insts []instance.Instance) error {
476 var need []string477 var need []string
477 for i, inst := range insts {478 for i, inst := range insts {
478 if inst == nil {479 if inst == nil {
@@ -502,7 +503,7 @@
502 for k := range r.Instances {503 for k := range r.Instances {
503 if r.Instances[k].InstanceId == string(id) {504 if r.Instances[k].InstanceId == string(id) {
504 inst := r.Instances[k]505 inst := r.Instances[k]
505 insts[i] = &instance{e, &inst}506 insts[i] = &ec2Instance{e, &inst}
506 n++507 n++
507 }508 }
508 }509 }
@@ -514,11 +515,11 @@
514 return nil515 return nil
515}516}
516517
517func (e *environ) Instances(ids []state.InstanceId) ([]environs.Instance, error) {518func (e *environ) Instances(ids []state.InstanceId) ([]instance.Instance, error) {
518 if len(ids) == 0 {519 if len(ids) == 0 {
519 return nil, nil520 return nil, nil
520 }521 }
521 insts := make([]environs.Instance, len(ids))522 insts := make([]instance.Instance, len(ids))
522 // Make a series of requests to cope with eventual consistency.523 // Make a series of requests to cope with eventual consistency.
523 // Each request will attempt to add more instances to the requested524 // Each request will attempt to add more instances to the requested
524 // set.525 // set.
@@ -543,7 +544,7 @@
543 return insts, nil544 return insts, nil
544}545}
545546
546func (e *environ) AllInstances() ([]environs.Instance, error) {547func (e *environ) AllInstances() ([]instance.Instance, error) {
547 filter := ec2.NewFilter()548 filter := ec2.NewFilter()
548 filter.Add("instance-state-name", "pending", "running")549 filter.Add("instance-state-name", "pending", "running")
549 filter.Add("group-name", e.jujuGroupName())550 filter.Add("group-name", e.jujuGroupName())
@@ -551,17 +552,17 @@
551 if err != nil {552 if err != nil {
552 return nil, err553 return nil, err
553 }554 }
554 var insts []environs.Instance555 var insts []instance.Instance
555 for _, r := range resp.Reservations {556 for _, r := range resp.Reservations {
556 for i := range r.Instances {557 for i := range r.Instances {
557 inst := r.Instances[i]558 inst := r.Instances[i]
558 insts = append(insts, &instance{e, &inst})559 insts = append(insts, &ec2Instance{e, &inst})
559 }560 }
560 }561 }
561 return insts, nil562 return insts, nil
562}563}
563564
564func (e *environ) Destroy(ensureInsts []environs.Instance) error {565func (e *environ) Destroy(ensureInsts []instance.Instance) error {
565 log.Infof("environs/ec2: destroying environment %q", e.name)566 log.Infof("environs/ec2: destroying environment %q", e.name)
566 insts, err := e.AllInstances()567 insts, err := e.AllInstances()
567 if err != nil {568 if err != nil {
@@ -577,7 +578,7 @@
577 // Add any instances we've been told about but haven't yet shown578 // Add any instances we've been told about but haven't yet shown
578 // up in the instance list.579 // up in the instance list.
579 for _, inst := range ensureInsts {580 for _, inst := range ensureInsts {
580 id := state.InstanceId(inst.(*instance).InstanceId)581 id := state.InstanceId(inst.(*ec2Instance).InstanceId)
581 if !found[id] {582 if !found[id] {
582 ids = append(ids, id)583 ids = append(ids, id)
583 found[id] = true584 found[id] = true
@@ -761,7 +762,7 @@
761 return "juju-" + e.name762 return "juju-" + e.name
762}763}
763764
764func (inst *instance) OpenPorts(machineId string, ports []params.Port) error {765func (inst *ec2Instance) OpenPorts(machineId string, ports []params.Port) error {
765 if inst.e.Config().FirewallMode() != config.FwInstance {766 if inst.e.Config().FirewallMode() != config.FwInstance {
766 return fmt.Errorf("invalid firewall mode for opening ports on instance: %q",767 return fmt.Errorf("invalid firewall mode for opening ports on instance: %q",
767 inst.e.Config().FirewallMode())768 inst.e.Config().FirewallMode())
@@ -774,7 +775,7 @@
774 return nil775 return nil
775}776}
776777
777func (inst *instance) ClosePorts(machineId string, ports []params.Port) error {778func (inst *ec2Instance) ClosePorts(machineId string, ports []params.Port) error {
778 if inst.e.Config().FirewallMode() != config.FwInstance {779 if inst.e.Config().FirewallMode() != config.FwInstance {
779 return fmt.Errorf("invalid firewall mode for closing ports on instance: %q",780 return fmt.Errorf("invalid firewall mode for closing ports on instance: %q",
780 inst.e.Config().FirewallMode())781 inst.e.Config().FirewallMode())
@@ -787,7 +788,7 @@
787 return nil788 return nil
788}789}
789790
790func (inst *instance) Ports(machineId string) ([]params.Port, error) {791func (inst *ec2Instance) Ports(machineId string) ([]params.Port, error) {
791 if inst.e.Config().FirewallMode() != config.FwInstance {792 if inst.e.Config().FirewallMode() != config.FwInstance {
792 return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q",793 return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q",
793 inst.e.Config().FirewallMode())794 inst.e.Config().FirewallMode())
794795
=== modified file 'environs/ec2/export_test.go'
--- environs/ec2/export_test.go 2013-05-30 05:39:33 +0000
+++ environs/ec2/export_test.go 2013-06-17 04:13:30 +0000
@@ -11,6 +11,7 @@
11 "launchpad.net/juju-core/environs"11 "launchpad.net/juju-core/environs"
12 "launchpad.net/juju-core/environs/imagemetadata"12 "launchpad.net/juju-core/environs/imagemetadata"
13 "launchpad.net/juju-core/environs/jujutest"13 "launchpad.net/juju-core/environs/jujutest"
14 "launchpad.net/juju-core/instance"
14 "launchpad.net/juju-core/state"15 "launchpad.net/juju-core/state"
15 "launchpad.net/juju-core/utils"16 "launchpad.net/juju-core/utils"
16 "net/http"17 "net/http"
@@ -48,8 +49,8 @@
48 return s.(*storage).deleteAll()49 return s.(*storage).deleteAll()
49}50}
5051
51func InstanceEC2(inst environs.Instance) *ec2.Instance {52func InstanceEC2(inst instance.Instance) *ec2.Instance {
52 return inst.(*instance).Instance53 return inst.(*ec2Instance).Instance
53}54}
5455
55// BucketStorage returns a storage instance addressing56// BucketStorage returns a storage instance addressing
@@ -147,9 +148,9 @@
147148
148// FabricateInstance creates a new fictitious instance149// FabricateInstance creates a new fictitious instance
149// given an existing instance and a new id.150// given an existing instance and a new id.
150func FabricateInstance(inst environs.Instance, newId string) environs.Instance {151func FabricateInstance(inst instance.Instance, newId string) instance.Instance {
151 oldi := inst.(*instance)152 oldi := inst.(*ec2Instance)
152 newi := &instance{oldi.e, &ec2.Instance{}}153 newi := &ec2Instance{oldi.e, &ec2.Instance{}}
153 *newi.Instance = *oldi.Instance154 *newi.Instance = *oldi.Instance
154 newi.InstanceId = newId155 newi.InstanceId = newId
155 return newi156 return newi
156157
=== modified file 'environs/ec2/live_test.go'
--- environs/ec2/live_test.go 2013-05-30 00:47:30 +0000
+++ environs/ec2/live_test.go 2013-06-17 04:13:30 +0000
@@ -17,6 +17,7 @@
17 "launchpad.net/juju-core/environs/jujutest"17 "launchpad.net/juju-core/environs/jujutest"
18 envtesting "launchpad.net/juju-core/environs/testing"18 envtesting "launchpad.net/juju-core/environs/testing"
19 "launchpad.net/juju-core/errors"19 "launchpad.net/juju-core/errors"
20 "launchpad.net/juju-core/instance"
20 "launchpad.net/juju-core/juju/testing"21 "launchpad.net/juju-core/juju/testing"
21 "launchpad.net/juju-core/state"22 "launchpad.net/juju-core/state"
22 coretesting "launchpad.net/juju-core/testing"23 coretesting "launchpad.net/juju-core/testing"
@@ -113,7 +114,7 @@
113114
114func (t *LiveTests) TestInstanceAttributes(c *C) {115func (t *LiveTests) TestInstanceAttributes(c *C) {
115 inst := testing.StartInstance(c, t.Env, "30")116 inst := testing.StartInstance(c, t.Env, "30")
116 defer t.Env.StopInstances([]environs.Instance{inst})117 defer t.Env.StopInstances([]instance.Instance{inst})
117 dns, err := inst.WaitDNSName()118 dns, err := inst.WaitDNSName()
118 // TODO(niemeyer): This assert sometimes fails with "no instances found"119 // TODO(niemeyer): This assert sometimes fails with "no instances found"
119 c.Assert(err, IsNil)120 c.Assert(err, IsNil)
@@ -132,7 +133,7 @@
132 cons := constraints.MustParse("mem=2G")133 cons := constraints.MustParse("mem=2G")
133 inst, err := t.Env.StartInstance("31", "fake_nonce", config.DefaultSeries, cons, testing.InvalidStateInfo("31"), testing.InvalidAPIInfo("31"))134 inst, err := t.Env.StartInstance("31", "fake_nonce", config.DefaultSeries, cons, testing.InvalidStateInfo("31"), testing.InvalidAPIInfo("31"))
134 c.Assert(err, IsNil)135 c.Assert(err, IsNil)
135 defer t.Env.StopInstances([]environs.Instance{inst})136 defer t.Env.StopInstances([]instance.Instance{inst})
136 ec2inst := ec2.InstanceEC2(inst)137 ec2inst := ec2.InstanceEC2(inst)
137 c.Assert(ec2inst.InstanceType, Equals, "m1.medium")138 c.Assert(ec2inst.InstanceType, Equals, "m1.medium")
138}139}
@@ -173,14 +174,14 @@
173 c.Assert(err, IsNil)174 c.Assert(err, IsNil)
174175
175 inst0 := testing.StartInstance(c, t.Env, "98")176 inst0 := testing.StartInstance(c, t.Env, "98")
176 defer t.Env.StopInstances([]environs.Instance{inst0})177 defer t.Env.StopInstances([]instance.Instance{inst0})
177178
178 // Create a same-named group for the second instance179 // Create a same-named group for the second instance
179 // before starting it, to check that it's reused correctly.180 // before starting it, to check that it's reused correctly.
180 oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")181 oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")
181182
182 inst1 := testing.StartInstance(c, t.Env, "99")183 inst1 := testing.StartInstance(c, t.Env, "99")
183 defer t.Env.StopInstances([]environs.Instance{inst1})184 defer t.Env.StopInstances([]instance.Instance{inst1})
184185
185 groupsResp, err := ec2conn.SecurityGroups(groups, nil)186 groupsResp, err := ec2conn.SecurityGroups(groups, nil)
186 c.Assert(err, IsNil)187 c.Assert(err, IsNil)
@@ -314,10 +315,10 @@
314 inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")315 inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")
315 inst2 := testing.StartInstance(c, t.Env, "41")316 inst2 := testing.StartInstance(c, t.Env, "41")
316317
317 err := t.Env.StopInstances([]environs.Instance{inst0, inst1, inst2})318 err := t.Env.StopInstances([]instance.Instance{inst0, inst1, inst2})
318 c.Check(err, IsNil)319 c.Check(err, IsNil)
319320
320 var insts []environs.Instance321 var insts []instance.Instance
321322
322 // We need the retry logic here because we are waiting323 // We need the retry logic here because we are waiting
323 // for Instances to return an error, and it will not retry324 // for Instances to return an error, and it will not retry
324325
=== modified file 'environs/interface.go'
--- environs/interface.go 2013-06-10 10:30:13 +0000
+++ environs/interface.go 2013-06-17 04:13:30 +0000
@@ -8,6 +8,7 @@
8 "io"8 "io"
9 "launchpad.net/juju-core/constraints"9 "launchpad.net/juju-core/constraints"
10 "launchpad.net/juju-core/environs/config"10 "launchpad.net/juju-core/environs/config"
11 "launchpad.net/juju-core/instance"
11 "launchpad.net/juju-core/state"12 "launchpad.net/juju-core/state"
12 "launchpad.net/juju-core/state/api"13 "launchpad.net/juju-core/state/api"
13 "launchpad.net/juju-core/state/api/params"14 "launchpad.net/juju-core/state/api/params"
@@ -49,36 +50,6 @@
49 InstanceId() (state.InstanceId, error)50 InstanceId() (state.InstanceId, error)
50}51}
5152
52var ErrNoDNSName = errors.New("DNS name not allocated")
53
54// Instance represents the provider-specific notion of a machine.
55type Instance interface {
56 // Id returns a provider-generated identifier for the Instance.
57 Id() state.InstanceId
58
59 // DNSName returns the DNS name for the instance.
60 // If the name is not yet allocated, it will return
61 // an ErrNoDNSName error.
62 DNSName() (string, error)
63
64 // WaitDNSName returns the DNS name for the instance,
65 // waiting until it is allocated if necessary.
66 WaitDNSName() (string, error)
67
68 // OpenPorts opens the given ports on the instance, which
69 // should have been started with the given machine id.
70 OpenPorts(machineId string, ports []params.Port) error
71
72 // ClosePorts closes the given ports on the instance, which
73 // should have been started with the given machine id.
74 ClosePorts(machineId string, ports []params.Port) error
75
76 // Ports returns the set of ports open on the instance, which
77 // should have been started with the given machine id.
78 // The ports are returned as sorted by state.SortPorts.
79 Ports(machineId string) ([]params.Port, error)
80}
81
82var ErrNoInstances = errors.New("no instances found")53var ErrNoInstances = errors.New("no instances found")
83var ErrPartialInstances = errors.New("only some instances were found")54var ErrPartialInstances = errors.New("only some instances were found")
8455
@@ -164,10 +135,10 @@
164 // which must be unique within an environment, is used by juju to135 // which must be unique within an environment, is used by juju to
165 // protect against the consequences of multiple instances being136 // protect against the consequences of multiple instances being
166 // started with the same machine id.137 // started with the same machine id.
167 StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (Instance, error)138 StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, error)
168139
169 // StopInstances shuts down the given instances.140 // StopInstances shuts down the given instances.
170 StopInstances([]Instance) error141 StopInstances([]instance.Instance) error
171142
172 // Instances returns a slice of instances corresponding to the143 // Instances returns a slice of instances corresponding to the
173 // given instance ids. If no instances were found, but there144 // given instance ids. If no instances were found, but there
@@ -175,11 +146,11 @@
175 // some but not all the instances were found, the returned slice146 // some but not all the instances were found, the returned slice
176 // will have some nil slots, and an ErrPartialInstances error147 // will have some nil slots, and an ErrPartialInstances error
177 // will be returned.148 // will be returned.
178 Instances(ids []state.InstanceId) ([]Instance, error)149 Instances(ids []state.InstanceId) ([]instance.Instance, error)
179150
180 // AllInstances returns all instances currently known to the151 // AllInstances returns all instances currently known to the
181 // environment.152 // environment.
182 AllInstances() ([]Instance, error)153 AllInstances() ([]instance.Instance, error)
183154
184 // Storage returns storage specific to the environment.155 // Storage returns storage specific to the environment.
185 Storage() Storage156 Storage() Storage
@@ -196,7 +167,7 @@
196 //167 //
197 // When Destroy has been called, any Environ referring to the168 // When Destroy has been called, any Environ referring to the
198 // same remote environment may become invalid169 // same remote environment may become invalid
199 Destroy(insts []Instance) error170 Destroy(insts []instance.Instance) error
200171
201 // OpenPorts opens the given ports for the whole environment.172 // OpenPorts opens the given ports for the whole environment.
202 // Must only be used if the environment was setup with the173 // Must only be used if the environment was setup with the
203174
=== modified file 'environs/jujutest/livetests.go'
--- environs/jujutest/livetests.go 2013-05-30 00:47:30 +0000
+++ environs/jujutest/livetests.go 2013-06-17 04:13:30 +0000
@@ -14,6 +14,7 @@
14 "launchpad.net/juju-core/environs/config"14 "launchpad.net/juju-core/environs/config"
15 "launchpad.net/juju-core/environs/tools"15 "launchpad.net/juju-core/environs/tools"
16 "launchpad.net/juju-core/errors"16 "launchpad.net/juju-core/errors"
17 "launchpad.net/juju-core/instance"
17 "launchpad.net/juju-core/juju"18 "launchpad.net/juju-core/juju"
18 "launchpad.net/juju-core/juju/testing"19 "launchpad.net/juju-core/juju/testing"
19 "launchpad.net/juju-core/state"20 "launchpad.net/juju-core/state"
@@ -142,7 +143,7 @@
142 c.Check(insts[0].Id(), Equals, id0)143 c.Check(insts[0].Id(), Equals, id0)
143 c.Check(insts[1], IsNil)144 c.Check(insts[1], IsNil)
144145
145 err = t.Env.StopInstances([]environs.Instance{inst})146 err = t.Env.StopInstances([]instance.Instance{inst})
146 c.Assert(err, IsNil)147 c.Assert(err, IsNil)
147148
148 // The machine may not be marked as shutting down149 // The machine may not be marked as shutting down
@@ -160,7 +161,7 @@
160func (t *LiveTests) TestPorts(c *C) {161func (t *LiveTests) TestPorts(c *C) {
161 inst1 := testing.StartInstance(c, t.Env, "1")162 inst1 := testing.StartInstance(c, t.Env, "1")
162 c.Assert(inst1, NotNil)163 c.Assert(inst1, NotNil)
163 defer t.Env.StopInstances([]environs.Instance{inst1})164 defer t.Env.StopInstances([]instance.Instance{inst1})
164 ports, err := inst1.Ports("1")165 ports, err := inst1.Ports("1")
165 c.Assert(err, IsNil)166 c.Assert(err, IsNil)
166 c.Assert(ports, HasLen, 0)167 c.Assert(ports, HasLen, 0)
@@ -170,7 +171,7 @@
170 ports, err = inst2.Ports("2")171 ports, err = inst2.Ports("2")
171 c.Assert(err, IsNil)172 c.Assert(err, IsNil)
172 c.Assert(ports, HasLen, 0)173 c.Assert(ports, HasLen, 0)
173 defer t.Env.StopInstances([]environs.Instance{inst2})174 defer t.Env.StopInstances([]instance.Instance{inst2})
174175
175 // Open some ports and check they're there.176 // Open some ports and check they're there.
176 err = inst1.OpenPorts("1", []params.Port{{"udp", 67}, {"tcp", 45}})177 err = inst1.OpenPorts("1", []params.Port{{"udp", 67}, {"tcp", 45}})
@@ -260,7 +261,7 @@
260261
261 // Create instances and check open ports on both instances.262 // Create instances and check open ports on both instances.
262 inst1 := testing.StartInstance(c, t.Env, "1")263 inst1 := testing.StartInstance(c, t.Env, "1")
263 defer t.Env.StopInstances([]environs.Instance{inst1})264 defer t.Env.StopInstances([]instance.Instance{inst1})
264 ports, err := t.Env.Ports()265 ports, err := t.Env.Ports()
265 c.Assert(err, IsNil)266 c.Assert(err, IsNil)
266 c.Assert(ports, HasLen, 0)267 c.Assert(ports, HasLen, 0)
@@ -269,7 +270,7 @@
269 ports, err = t.Env.Ports()270 ports, err = t.Env.Ports()
270 c.Assert(err, IsNil)271 c.Assert(err, IsNil)
271 c.Assert(ports, HasLen, 0)272 c.Assert(ports, HasLen, 0)
272 defer t.Env.StopInstances([]environs.Instance{inst2})273 defer t.Env.StopInstances([]instance.Instance{inst2})
273274
274 err = t.Env.OpenPorts([]params.Port{{"udp", 67}, {"tcp", 45}, {"tcp", 89}, {"tcp", 99}})275 err = t.Env.OpenPorts([]params.Port{{"udp", 67}, {"tcp", 45}, {"tcp", 89}, {"tcp", 99}})
275 c.Assert(err, IsNil)276 c.Assert(err, IsNil)
@@ -630,7 +631,7 @@
630// assertInstanceId asserts that the machine has an instance id631// assertInstanceId asserts that the machine has an instance id
631// that matches that of the given instance. If the instance is nil,632// that matches that of the given instance. If the instance is nil,
632// It asserts that the instance id is unset.633// It asserts that the instance id is unset.
633func assertInstanceId(c *C, m *state.Machine, inst environs.Instance) {634func assertInstanceId(c *C, m *state.Machine, inst instance.Instance) {
634 var wantId, gotId state.InstanceId635 var wantId, gotId state.InstanceId
635 var err error636 var err error
636 if inst != nil {637 if inst != nil {
@@ -700,7 +701,7 @@
700func (t *LiveTests) TestStartInstanceOnUnknownPlatform(c *C) {701func (t *LiveTests) TestStartInstanceOnUnknownPlatform(c *C) {
701 inst, err := t.Env.StartInstance("4", "fake_nonce", "unknownseries", constraints.Value{}, testing.InvalidStateInfo("4"), testing.InvalidAPIInfo("4"))702 inst, err := t.Env.StartInstance("4", "fake_nonce", "unknownseries", constraints.Value{}, testing.InvalidStateInfo("4"), testing.InvalidAPIInfo("4"))
702 if inst != nil {703 if inst != nil {
703 err := t.Env.StopInstances([]environs.Instance{inst})704 err := t.Env.StopInstances([]instance.Instance{inst})
704 c.Check(err, IsNil)705 c.Check(err, IsNil)
705 }706 }
706 c.Assert(inst, IsNil)707 c.Assert(inst, IsNil)
@@ -713,7 +714,7 @@
713func (t *LiveTests) TestStartInstanceWithEmptyNonceFails(c *C) {714func (t *LiveTests) TestStartInstanceWithEmptyNonceFails(c *C) {
714 inst, err := t.Env.StartInstance("4", "", config.DefaultSeries, constraints.Value{}, testing.InvalidStateInfo("4"), testing.InvalidAPIInfo("4"))715 inst, err := t.Env.StartInstance("4", "", config.DefaultSeries, constraints.Value{}, testing.InvalidStateInfo("4"), testing.InvalidAPIInfo("4"))
715 if inst != nil {716 if inst != nil {
716 err := t.Env.StopInstances([]environs.Instance{inst})717 err := t.Env.StopInstances([]instance.Instance{inst})
717 c.Check(err, IsNil)718 c.Check(err, IsNil)
718 }719 }
719 c.Assert(inst, IsNil)720 c.Assert(inst, IsNil)
720721
=== modified file 'environs/jujutest/tests.go'
--- environs/jujutest/tests.go 2013-05-30 00:47:30 +0000
+++ environs/jujutest/tests.go 2013-06-17 04:13:30 +0000
@@ -12,6 +12,7 @@
12 "launchpad.net/juju-core/environs"12 "launchpad.net/juju-core/environs"
13 envtesting "launchpad.net/juju-core/environs/testing"13 envtesting "launchpad.net/juju-core/environs/testing"
14 "launchpad.net/juju-core/errors"14 "launchpad.net/juju-core/errors"
15 "launchpad.net/juju-core/instance"
15 "launchpad.net/juju-core/juju/testing"16 "launchpad.net/juju-core/juju/testing"
16 "launchpad.net/juju-core/state"17 "launchpad.net/juju-core/state"
17 coretesting "launchpad.net/juju-core/testing"18 coretesting "launchpad.net/juju-core/testing"
@@ -107,7 +108,7 @@
107 c.Assert(insts, HasLen, 2)108 c.Assert(insts, HasLen, 2)
108 c.Assert(insts[0].Id(), Not(Equals), insts[1].Id())109 c.Assert(insts[0].Id(), Not(Equals), insts[1].Id())
109110
110 err = e.StopInstances([]environs.Instance{inst0})111 err = e.StopInstances([]instance.Instance{inst0})
111 c.Assert(err, IsNil)112 c.Assert(err, IsNil)
112113
113 insts, err = e.Instances([]state.InstanceId{id0, id1})114 insts, err = e.Instances([]state.InstanceId{id0, id1})
114115
=== modified file 'environs/maas/environ.go'
--- environs/maas/environ.go 2013-06-10 10:30:13 +0000
+++ environs/maas/environ.go 2013-06-17 04:13:30 +0000
@@ -13,6 +13,7 @@
13 "launchpad.net/juju-core/environs/cloudinit"13 "launchpad.net/juju-core/environs/cloudinit"
14 "launchpad.net/juju-core/environs/config"14 "launchpad.net/juju-core/environs/config"
15 "launchpad.net/juju-core/environs/tools"15 "launchpad.net/juju-core/environs/tools"
16 "launchpad.net/juju-core/instance"
16 "launchpad.net/juju-core/log"17 "launchpad.net/juju-core/log"
17 "launchpad.net/juju-core/state"18 "launchpad.net/juju-core/state"
18 "launchpad.net/juju-core/state/api"19 "launchpad.net/juju-core/state/api"
@@ -84,7 +85,7 @@
84}85}
8586
86// startBootstrapNode starts the juju bootstrap node for this environment.87// startBootstrapNode starts the juju bootstrap node for this environment.
87func (env *maasEnviron) startBootstrapNode(cons constraints.Value) (environs.Instance, error) {88func (env *maasEnviron) startBootstrapNode(cons constraints.Value) (instance.Instance, error) {
88 // The bootstrap instance gets machine id "0". This is not related to89 // The bootstrap instance gets machine id "0". This is not related to
89 // instance ids or MAAS system ids. Juju assigns the machine ID.90 // instance ids or MAAS system ids. Juju assigns the machine ID.
90 const machineID = "0"91 const machineID = "0"
@@ -352,7 +353,7 @@
352}353}
353354
354// StartInstance is specified in the Environ interface.355// StartInstance is specified in the Environ interface.
355func (environ *maasEnviron) StartInstance(machineID, machineNonce string, series string, cons constraints.Value, stateInfo *state.Info, apiInfo *api.Info) (environs.Instance, error) {356func (environ *maasEnviron) StartInstance(machineID, machineNonce string, series string, cons constraints.Value, stateInfo *state.Info, apiInfo *api.Info) (instance.Instance, error) {
356 possibleTools, err := environs.FindInstanceTools(environ, series, cons)357 possibleTools, err := environs.FindInstanceTools(environ, series, cons)
357 if err != nil {358 if err != nil {
358 return nil, err359 return nil, err
@@ -362,7 +363,7 @@
362}363}
363364
364// StopInstances is specified in the Environ interface.365// StopInstances is specified in the Environ interface.
365func (environ *maasEnviron) StopInstances(instances []environs.Instance) error {366func (environ *maasEnviron) StopInstances(instances []instance.Instance) error {
366 // Shortcut to exit quickly if 'instances' is an empty slice or nil.367 // Shortcut to exit quickly if 'instances' is an empty slice or nil.
367 if len(instances) == 0 {368 if len(instances) == 0 {
368 return nil369 return nil
@@ -381,7 +382,7 @@
381}382}
382383
383// releaseInstance releases a single instance.384// releaseInstance releases a single instance.
384func (environ *maasEnviron) releaseInstance(inst environs.Instance) error {385func (environ *maasEnviron) releaseInstance(inst instance.Instance) error {
385 maasInst := inst.(*maasInstance)386 maasInst := inst.(*maasInstance)
386 maasObj := maasInst.maasObject387 maasObj := maasInst.maasObject
387 _, err := maasObj.CallPost("release", nil)388 _, err := maasObj.CallPost("release", nil)
@@ -391,10 +392,10 @@
391 return err392 return err
392}393}
393394
394// Instances returns the environs.Instance objects corresponding to the given395// Instances returns the instance.Instance objects corresponding to the given
395// slice of state.InstanceId. Similar to what the ec2 provider does,396// slice of state.InstanceId. Similar to what the ec2 provider does,
396// Instances returns nil if the given slice is empty or nil.397// Instances returns nil if the given slice is empty or nil.
397func (environ *maasEnviron) Instances(ids []state.InstanceId) ([]environs.Instance, error) {398func (environ *maasEnviron) Instances(ids []state.InstanceId) ([]instance.Instance, error) {
398 if len(ids) == 0 {399 if len(ids) == 0 {
399 return nil, nil400 return nil, nil
400 }401 }
@@ -406,7 +407,7 @@
406// If the some of the intances could not be found, it returns the instance407// If the some of the intances could not be found, it returns the instance
407// that could be found plus the error environs.ErrPartialInstances in the error408// that could be found plus the error environs.ErrPartialInstances in the error
408// return.409// return.
409func (environ *maasEnviron) instances(ids []state.InstanceId) ([]environs.Instance, error) {410func (environ *maasEnviron) instances(ids []state.InstanceId) ([]instance.Instance, error) {
410 nodeListing := environ.getMAASClient().GetSubObject("nodes")411 nodeListing := environ.getMAASClient().GetSubObject("nodes")
411 filter := getSystemIdValues(ids)412 filter := getSystemIdValues(ids)
412 listNodeObjects, err := nodeListing.CallGet("list", filter)413 listNodeObjects, err := nodeListing.CallGet("list", filter)
@@ -417,7 +418,7 @@
417 if err != nil {418 if err != nil {
418 return nil, err419 return nil, err
419 }420 }
420 instances := make([]environs.Instance, len(listNodes))421 instances := make([]instance.Instance, len(listNodes))
421 for index, nodeObj := range listNodes {422 for index, nodeObj := range listNodes {
422 node, err := nodeObj.GetMAASObject()423 node, err := nodeObj.GetMAASObject()
423 if err != nil {424 if err != nil {
@@ -434,8 +435,8 @@
434 return instances, nil435 return instances, nil
435}436}
436437
437// AllInstances returns all the environs.Instance in this provider.438// AllInstances returns all the instance.Instance in this provider.
438func (environ *maasEnviron) AllInstances() ([]environs.Instance, error) {439func (environ *maasEnviron) AllInstances() ([]instance.Instance, error) {
439 return environ.instances(nil)440 return environ.instances(nil)
440}441}
441442
@@ -452,7 +453,7 @@
452 return environs.EmptyStorage453 return environs.EmptyStorage
453}454}
454455
455func (environ *maasEnviron) Destroy(ensureInsts []environs.Instance) error {456func (environ *maasEnviron) Destroy(ensureInsts []instance.Instance) error {
456 log.Debugf("environs/maas: destroying environment %q", environ.name)457 log.Debugf("environs/maas: destroying environment %q", environ.name)
457 insts, err := environ.AllInstances()458 insts, err := environ.AllInstances()
458 if err != nil {459 if err != nil {
459460
=== modified file 'environs/maas/environ_test.go'
--- environs/maas/environ_test.go 2013-06-10 10:30:13 +0000
+++ environs/maas/environ_test.go 2013-06-17 04:13:30 +0000
@@ -15,6 +15,7 @@
15 envtesting "launchpad.net/juju-core/environs/testing"15 envtesting "launchpad.net/juju-core/environs/testing"
16 "launchpad.net/juju-core/environs/tools"16 "launchpad.net/juju-core/environs/tools"
17 "launchpad.net/juju-core/errors"17 "launchpad.net/juju-core/errors"
18 "launchpad.net/juju-core/instance"
18 "launchpad.net/juju-core/state"19 "launchpad.net/juju-core/state"
19 "launchpad.net/juju-core/testing"20 "launchpad.net/juju-core/testing"
20 "launchpad.net/juju-core/utils"21 "launchpad.net/juju-core/utils"
@@ -344,7 +345,7 @@
344func (suite *EnvironSuite) TestStopInstancesReturnsIfParameterEmpty(c *C) {345func (suite *EnvironSuite) TestStopInstancesReturnsIfParameterEmpty(c *C) {
345 suite.getInstance("test1")346 suite.getInstance("test1")
346347
347 err := suite.environ.StopInstances([]environs.Instance{})348 err := suite.environ.StopInstances([]instance.Instance{})
348 c.Check(err, IsNil)349 c.Check(err, IsNil)
349 operations := suite.testMAASObject.TestServer.NodeOperations()350 operations := suite.testMAASObject.TestServer.NodeOperations()
350 c.Check(operations, DeepEquals, map[string][]string{})351 c.Check(operations, DeepEquals, map[string][]string{})
@@ -354,7 +355,7 @@
354 instance1 := suite.getInstance("test1")355 instance1 := suite.getInstance("test1")
355 instance2 := suite.getInstance("test2")356 instance2 := suite.getInstance("test2")
356 suite.getInstance("test3")357 suite.getInstance("test3")
357 instances := []environs.Instance{instance1, instance2}358 instances := []instance.Instance{instance1, instance2}
358359
359 err := suite.environ.StopInstances(instances)360 err := suite.environ.StopInstances(instances)
360361
@@ -394,12 +395,12 @@
394func (suite *EnvironSuite) TestDestroy(c *C) {395func (suite *EnvironSuite) TestDestroy(c *C) {
395 env := suite.makeEnviron()396 env := suite.makeEnviron()
396 suite.getInstance("test1")397 suite.getInstance("test1")
397 instance := suite.getInstance("test2")398 testInstance := suite.getInstance("test2")
398 data := makeRandomBytes(10)399 data := makeRandomBytes(10)
399 suite.testMAASObject.TestServer.NewFile("filename", data)400 suite.testMAASObject.TestServer.NewFile("filename", data)
400 storage := env.Storage()401 storage := env.Storage()
401402
402 err := env.Destroy([]environs.Instance{instance})403 err := env.Destroy([]instance.Instance{testInstance})
403404
404 c.Check(err, IsNil)405 c.Check(err, IsNil)
405 // Instances have been stopped.406 // Instances have been stopped.
406407
=== modified file 'environs/maas/instance.go'
--- environs/maas/instance.go 2013-05-02 15:55:42 +0000
+++ environs/maas/instance.go 2013-06-17 04:13:30 +0000
@@ -5,7 +5,7 @@
55
6import (6import (
7 "launchpad.net/gomaasapi"7 "launchpad.net/gomaasapi"
8 "launchpad.net/juju-core/environs"8 "launchpad.net/juju-core/instance"
9 "launchpad.net/juju-core/log"9 "launchpad.net/juju-core/log"
10 "launchpad.net/juju-core/state"10 "launchpad.net/juju-core/state"
11 "launchpad.net/juju-core/state/api/params"11 "launchpad.net/juju-core/state/api/params"
@@ -16,7 +16,7 @@
16 environ *maasEnviron16 environ *maasEnviron
17}17}
1818
19var _ environs.Instance = (*maasInstance)(nil)19var _ instance.Instance = (*maasInstance)(nil)
2020
21func (instance *maasInstance) Id() state.InstanceId {21func (instance *maasInstance) Id() state.InstanceId {
22 // Use the node's 'resource_uri' value.22 // Use the node's 'resource_uri' value.
2323
=== modified file 'environs/openstack/local_test.go'
--- environs/openstack/local_test.go 2013-05-28 08:05:49 +0000
+++ environs/openstack/local_test.go 2013-06-17 04:13:30 +0000
@@ -17,6 +17,7 @@
17 "launchpad.net/juju-core/environs/jujutest"17 "launchpad.net/juju-core/environs/jujutest"
18 "launchpad.net/juju-core/environs/openstack"18 "launchpad.net/juju-core/environs/openstack"
19 envtesting "launchpad.net/juju-core/environs/testing"19 envtesting "launchpad.net/juju-core/environs/testing"
20 "launchpad.net/juju-core/instance"
20 "launchpad.net/juju-core/juju/testing"21 "launchpad.net/juju-core/juju/testing"
21 "launchpad.net/juju-core/state"22 "launchpad.net/juju-core/state"
22 coretesting "launchpad.net/juju-core/testing"23 coretesting "launchpad.net/juju-core/testing"
@@ -281,7 +282,7 @@
281 err = environs.Bootstrap(env, constraints.Value{})282 err = environs.Bootstrap(env, constraints.Value{})
282 c.Assert(err, IsNil)283 c.Assert(err, IsNil)
283 inst := testing.StartInstance(c, env, "100")284 inst := testing.StartInstance(c, env, "100")
284 err = s.Env.StopInstances([]environs.Instance{inst})285 err = s.Env.StopInstances([]instance.Instance{inst})
285 c.Assert(err, IsNil)286 c.Assert(err, IsNil)
286}287}
287288
@@ -338,7 +339,7 @@
338 inst1 := testing.StartInstance(c, s.Env, "101")339 inst1 := testing.StartInstance(c, s.Env, "101")
339 id1 := inst1.Id()340 id1 := inst1.Id()
340 defer func() {341 defer func() {
341 err := s.Env.StopInstances([]environs.Instance{inst0, inst1})342 err := s.Env.StopInstances([]instance.Instance{inst0, inst1})
342 c.Assert(err, IsNil)343 c.Assert(err, IsNil)
343 }()344 }()
344345
345346
=== modified file 'environs/openstack/provider.go'
--- environs/openstack/provider.go 2013-06-10 10:30:13 +0000
+++ environs/openstack/provider.go 2013-06-17 04:13:30 +0000
@@ -23,6 +23,7 @@
23 "launchpad.net/juju-core/environs/instances"23 "launchpad.net/juju-core/environs/instances"
24 "launchpad.net/juju-core/environs/tools"24 "launchpad.net/juju-core/environs/tools"
25 coreerrors "launchpad.net/juju-core/errors"25 coreerrors "launchpad.net/juju-core/errors"
26 "launchpad.net/juju-core/instance"
26 "launchpad.net/juju-core/log"27 "launchpad.net/juju-core/log"
27 "launchpad.net/juju-core/state"28 "launchpad.net/juju-core/state"
28 "launchpad.net/juju-core/state/api"29 "launchpad.net/juju-core/state/api"
@@ -270,19 +271,19 @@
270271
271var _ environs.Environ = (*environ)(nil)272var _ environs.Environ = (*environ)(nil)
272273
273type instance struct {274type openstackInstance struct {
274 e *environ275 e *environ
275 *nova.ServerDetail276 *nova.ServerDetail
276 address string277 address string
277}278}
278279
279func (inst *instance) String() string {280func (inst *openstackInstance) String() string {
280 return inst.ServerDetail.Id281 return inst.ServerDetail.Id
281}282}
282283
283var _ environs.Instance = (*instance)(nil)284var _ instance.Instance = (*openstackInstance)(nil)
284285
285func (inst *instance) Id() state.InstanceId {286func (inst *openstackInstance) Id() state.InstanceId {
286 return state.InstanceId(inst.ServerDetail.Id)287 return state.InstanceId(inst.ServerDetail.Id)
287}288}
288289
@@ -319,12 +320,12 @@
319 public = private320 public = private
320 }321 }
321 if public == "" {322 if public == "" {
322 return "", environs.ErrNoDNSName323 return "", instance.ErrNoDNSName
323 }324 }
324 return public, nil325 return public, nil
325}326}
326327
327func (inst *instance) DNSName() (string, error) {328func (inst *openstackInstance) DNSName() (string, error) {
328 if inst.address != "" {329 if inst.address != "" {
329 return inst.address, nil330 return inst.address, nil
330 }331 }
@@ -341,10 +342,10 @@
341 return inst.address, nil342 return inst.address, nil
342}343}
343344
344func (inst *instance) WaitDNSName() (string, error) {345func (inst *openstackInstance) WaitDNSName() (string, error) {
345 for a := longAttempt.Start(); a.Next(); {346 for a := longAttempt.Start(); a.Next(); {
346 addr, err := inst.DNSName()347 addr, err := inst.DNSName()
347 if err == nil || err != environs.ErrNoDNSName {348 if err == nil || err != instance.ErrNoDNSName {
348 return addr, err349 return addr, err
349 }350 }
350 }351 }
@@ -353,7 +354,7 @@
353354
354// TODO: following 30 lines nearly verbatim from environs/ec2355// TODO: following 30 lines nearly verbatim from environs/ec2
355356
356func (inst *instance) OpenPorts(machineId string, ports []params.Port) error {357func (inst *openstackInstance) OpenPorts(machineId string, ports []params.Port) error {
357 if inst.e.Config().FirewallMode() != config.FwInstance {358 if inst.e.Config().FirewallMode() != config.FwInstance {
358 return fmt.Errorf("invalid firewall mode for opening ports on instance: %q",359 return fmt.Errorf("invalid firewall mode for opening ports on instance: %q",
359 inst.e.Config().FirewallMode())360 inst.e.Config().FirewallMode())
@@ -366,7 +367,7 @@
366 return nil367 return nil
367}368}
368369
369func (inst *instance) ClosePorts(machineId string, ports []params.Port) error {370func (inst *openstackInstance) ClosePorts(machineId string, ports []params.Port) error {
370 if inst.e.Config().FirewallMode() != config.FwInstance {371 if inst.e.Config().FirewallMode() != config.FwInstance {
371 return fmt.Errorf("invalid firewall mode for closing ports on instance: %q",372 return fmt.Errorf("invalid firewall mode for closing ports on instance: %q",
372 inst.e.Config().FirewallMode())373 inst.e.Config().FirewallMode())
@@ -379,7 +380,7 @@
379 return nil380 return nil
380}381}
381382
382func (inst *instance) Ports(machineId string) ([]params.Port, error) {383func (inst *openstackInstance) Ports(machineId string) ([]params.Port, error) {
383 if inst.e.Config().FirewallMode() != config.FwInstance {384 if inst.e.Config().FirewallMode() != config.FwInstance {
384 return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q",385 return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q",
385 inst.e.Config().FirewallMode())386 inst.e.Config().FirewallMode())
@@ -507,7 +508,7 @@
507 if err != nil {508 if err != nil {
508 // ignore error on StopInstance because the previous error is509 // ignore error on StopInstance because the previous error is
509 // more important.510 // more important.
510 e.StopInstances([]environs.Instance{inst})511 e.StopInstances([]instance.Instance{inst})
511 return fmt.Errorf("cannot save state: %v", err)512 return fmt.Errorf("cannot save state: %v", err)
512 }513 }
513 // TODO make safe in the case of racing Bootstraps514 // TODO make safe in the case of racing Bootstraps
@@ -545,7 +546,7 @@
545 if inst == nil {546 if inst == nil {
546 continue547 continue
547 }548 }
548 name, err := inst.(*instance).DNSName()549 name, err := inst.(*openstackInstance).DNSName()
549 if err != nil {550 if err != nil {
550 continue551 continue
551 }552 }
@@ -657,7 +658,7 @@
657 return e.imageBaseURLs, nil658 return e.imageBaseURLs, nil
658}659}
659660
660func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance, error) {661func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, error) {
661 possibleTools, err := environs.FindInstanceTools(e, series, cons)662 possibleTools, err := environs.FindInstanceTools(e, series, cons)
662 if err != nil {663 if err != nil {
663 return nil, err664 return nil, err
@@ -767,7 +768,7 @@
767768
768// startInstance is the internal version of StartInstance, used by Bootstrap769// startInstance is the internal version of StartInstance, used by Bootstrap
769// as well as via StartInstance itself.770// as well as via StartInstance itself.
770func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, error) {771func (e *environ) startInstance(scfg *startInstanceParams) (instance.Instance, error) {
771 series := scfg.possibleTools.Series()772 series := scfg.possibleTools.Series()
772 if len(series) != 1 {773 if len(series) != 1 {
773 return nil, fmt.Errorf("expected single series, got %v", series)774 return nil, fmt.Errorf("expected single series, got %v", series)
@@ -832,7 +833,7 @@
832 if err != nil {833 if err != nil {
833 return nil, fmt.Errorf("cannot get started instance: %v", err)834 return nil, fmt.Errorf("cannot get started instance: %v", err)
834 }835 }
835 inst := &instance{e, detail, ""}836 inst := &openstackInstance{e, detail, ""}
836 log.Infof("environs/openstack: started instance %q", inst.Id())837 log.Infof("environs/openstack: started instance %q", inst.Id())
837 if scfg.withPublicIP {838 if scfg.withPublicIP {
838 if err := e.assignPublicIP(publicIP, string(inst.Id())); err != nil {839 if err := e.assignPublicIP(publicIP, string(inst.Id())); err != nil {
@@ -847,12 +848,12 @@
847 return inst, nil848 return inst, nil
848}849}
849850
850func (e *environ) StopInstances(insts []environs.Instance) error {851func (e *environ) StopInstances(insts []instance.Instance) error {
851 ids := make([]state.InstanceId, len(insts))852 ids := make([]state.InstanceId, len(insts))
852 for i, inst := range insts {853 for i, inst := range insts {
853 instanceValue, ok := inst.(*instance)854 instanceValue, ok := inst.(*openstackInstance)
854 if !ok {855 if !ok {
855 return errors.New("Incompatible environs.Instance supplied")856 return errors.New("Incompatible instance.Instance supplied")
856 }857 }
857 ids[i] = instanceValue.Id()858 ids[i] = instanceValue.Id()
858 }859 }
@@ -863,7 +864,7 @@
863// collectInstances tries to get information on each instance id in ids.864// collectInstances tries to get information on each instance id in ids.
864// It fills the slots in the given map for known servers with status865// It fills the slots in the given map for known servers with status
865// either ACTIVE or BUILD. Returns a list of missing ids.866// either ACTIVE or BUILD. Returns a list of missing ids.
866func (e *environ) collectInstances(ids []state.InstanceId, out map[state.InstanceId]environs.Instance) []state.InstanceId {867func (e *environ) collectInstances(ids []state.InstanceId, out map[state.InstanceId]instance.Instance) []state.InstanceId {
867 var err error868 var err error
868 serversById := make(map[string]nova.ServerDetail)869 serversById := make(map[string]nova.ServerDetail)
869 if len(ids) == 1 {870 if len(ids) == 1 {
@@ -887,7 +888,7 @@
887 for _, id := range ids {888 for _, id := range ids {
888 if server, found := serversById[string(id)]; found {889 if server, found := serversById[string(id)]; found {
889 if server.Status == nova.StatusActive || server.Status == nova.StatusBuild {890 if server.Status == nova.StatusActive || server.Status == nova.StatusBuild {
890 out[id] = &instance{e, &server, ""}891 out[id] = &openstackInstance{e, &server, ""}
891 }892 }
892 continue893 continue
893 }894 }
@@ -896,12 +897,12 @@
896 return missing897 return missing
897}898}
898899
899func (e *environ) Instances(ids []state.InstanceId) ([]environs.Instance, error) {900func (e *environ) Instances(ids []state.InstanceId) ([]instance.Instance, error) {
900 if len(ids) == 0 {901 if len(ids) == 0 {
901 return nil, nil902 return nil, nil
902 }903 }
903 missing := ids904 missing := ids
904 found := make(map[state.InstanceId]environs.Instance)905 found := make(map[state.InstanceId]instance.Instance)
905 // Make a series of requests to cope with eventual consistency.906 // Make a series of requests to cope with eventual consistency.
906 // Each request will attempt to add more instances to the requested907 // Each request will attempt to add more instances to the requested
907 // set.908 // set.
@@ -913,7 +914,7 @@
913 if len(found) == 0 {914 if len(found) == 0 {
914 return nil, environs.ErrNoInstances915 return nil, environs.ErrNoInstances
915 }916 }
916 insts := make([]environs.Instance, len(ids))917 insts := make([]instance.Instance, len(ids))
917 var err error918 var err error
918 for i, id := range ids {919 for i, id := range ids {
919 if inst := found[id]; inst != nil {920 if inst := found[id]; inst != nil {
@@ -925,7 +926,7 @@
925 return insts, err926 return insts, err
926}927}
927928
928func (e *environ) AllInstances() (insts []environs.Instance, err error) {929func (e *environ) AllInstances() (insts []instance.Instance, err error) {
929 servers, err := e.nova().ListServersDetail(e.machinesFilter())930 servers, err := e.nova().ListServersDetail(e.machinesFilter())
930 if err != nil {931 if err != nil {
931 return nil, err932 return nil, err
@@ -933,13 +934,13 @@
933 for _, server := range servers {934 for _, server := range servers {
934 if server.Status == nova.StatusActive || server.Status == nova.StatusBuild {935 if server.Status == nova.StatusActive || server.Status == nova.StatusBuild {
935 var s = server936 var s = server
936 insts = append(insts, &instance{e, &s, ""})937 insts = append(insts, &openstackInstance{e, &s, ""})
937 }938 }
938 }939 }
939 return insts, err940 return insts, err
940}941}
941942
942func (e *environ) Destroy(ensureInsts []environs.Instance) error {943func (e *environ) Destroy(ensureInsts []instance.Instance) error {
943 log.Infof("environs/openstack: destroying environment %q", e.name)944 log.Infof("environs/openstack: destroying environment %q", e.name)
944 insts, err := e.AllInstances()945 insts, err := e.AllInstances()
945 if err != nil {946 if err != nil {
@@ -955,7 +956,7 @@
955 // Add any instances we've been told about but haven't yet shown956 // Add any instances we've been told about but haven't yet shown
956 // up in the instance list.957 // up in the instance list.
957 for _, inst := range ensureInsts {958 for _, inst := range ensureInsts {
958 id := state.InstanceId(inst.(*instance).Id())959 id := state.InstanceId(inst.(*openstackInstance).Id())
959 if !found[id] {960 if !found[id] {
960 ids = append(ids, id)961 ids = append(ids, id)
961 found[id] = true962 found[id] = true
962963
=== modified file 'environs/openstack/provider_test.go'
--- environs/openstack/provider_test.go 2013-05-20 00:10:30 +0000
+++ environs/openstack/provider_test.go 2013-06-17 04:13:30 +0000
@@ -8,8 +8,8 @@
8 . "launchpad.net/gocheck"8 . "launchpad.net/gocheck"
9 "launchpad.net/goose/identity"9 "launchpad.net/goose/identity"
10 "launchpad.net/goose/nova"10 "launchpad.net/goose/nova"
11 "launchpad.net/juju-core/environs"
12 "launchpad.net/juju-core/environs/openstack"11 "launchpad.net/juju-core/environs/openstack"
12 "launchpad.net/juju-core/instance"
13 "testing"13 "testing"
14)14)
1515
@@ -44,14 +44,14 @@
44 {44 {
45 summary: "missing",45 summary: "missing",
46 expected: "",46 expected: "",
47 failure: environs.ErrNoDNSName,47 failure: instance.ErrNoDNSName,
48 },48 },
49 {49 {
50 summary: "empty",50 summary: "empty",
51 private: []nova.IPAddress{},51 private: []nova.IPAddress{},
52 networks: []string{"private"},52 networks: []string{"private"},
53 expected: "",53 expected: "",
54 failure: environs.ErrNoDNSName,54 failure: instance.ErrNoDNSName,
55 },55 },
56 {56 {
57 summary: "private only",57 summary: "private only",
@@ -110,7 +110,7 @@
110 private: []nova.IPAddress{{6, "::dead:beef:f00d"}},110 private: []nova.IPAddress{{6, "::dead:beef:f00d"}},
111 networks: []string{"private"},111 networks: []string{"private"},
112 expected: "",112 expected: "",
113 failure: environs.ErrNoDNSName,113 failure: instance.ErrNoDNSName,
114 },114 },
115}115}
116116
117117
=== added directory 'instance'
=== added file 'instance/instance.go'
--- instance/instance.go 1970-01-01 00:00:00 +0000
+++ instance/instance.go 2013-06-17 04:13:30 +0000
@@ -0,0 +1,41 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package instance
5
6import (
7 "errors"
8
9 "launchpad.net/juju-core/state"
10 "launchpad.net/juju-core/state/api/params"
11)
12
13var ErrNoDNSName = errors.New("DNS name not allocated")
14
15// Instance represents the the realization of a machine in state.
16type Instance interface {
17 // Id returns a provider-generated identifier for the Instance.
18 Id() state.InstanceId
19
20 // DNSName returns the DNS name for the instance.
21 // If the name is not yet allocated, it will return
22 // an ErrNoDNSName error.
23 DNSName() (string, error)
24
25 // WaitDNSName returns the DNS name for the instance,
26 // waiting until it is allocated if necessary.
27 WaitDNSName() (string, error)
28
29 // OpenPorts opens the given ports on the instance, which
30 // should have been started with the given machine id.
31 OpenPorts(machineId string, ports []params.Port) error
32
33 // ClosePorts closes the given ports on the instance, which
34 // should have been started with the given machine id.
35 ClosePorts(machineId string, ports []params.Port) error
36
37 // Ports returns the set of ports open on the instance, which
38 // should have been started with the given machine id.
39 // The ports are returned as sorted by state.SortPorts.
40 Ports(machineId string) ([]params.Port, error)
41}
042
=== modified file 'juju/testing/conn.go'
--- juju/testing/conn.go 2013-05-02 15:55:42 +0000
+++ juju/testing/conn.go 2013-06-17 04:13:30 +0000
@@ -12,6 +12,7 @@
12 "launchpad.net/juju-core/environs"12 "launchpad.net/juju-core/environs"
13 "launchpad.net/juju-core/environs/config"13 "launchpad.net/juju-core/environs/config"
14 "launchpad.net/juju-core/environs/dummy"14 "launchpad.net/juju-core/environs/dummy"
15 "launchpad.net/juju-core/instance"
15 "launchpad.net/juju-core/juju"16 "launchpad.net/juju-core/juju"
16 "launchpad.net/juju-core/state"17 "launchpad.net/juju-core/state"
17 "launchpad.net/juju-core/state/api"18 "launchpad.net/juju-core/state/api"
@@ -77,7 +78,7 @@
7778
78// StartInstance is a test helper function that starts an instance on the79// StartInstance is a test helper function that starts an instance on the
79// environment using the current series and invalid info states.80// environment using the current series and invalid info states.
80func StartInstance(c *C, env environs.Environ, machineId string) environs.Instance {81func StartInstance(c *C, env environs.Environ, machineId string) instance.Instance {
81 series := config.DefaultSeries82 series := config.DefaultSeries
82 inst, err := env.StartInstance(83 inst, err := env.StartInstance(
83 machineId,84 machineId,
8485
=== modified file 'state/container.go'
--- state/container.go 2013-06-11 22:40:34 +0000
+++ state/container.go 2013-06-17 04:13:30 +0000
@@ -88,9 +88,9 @@
88 return ops88 return ops
89}89}
9090
91// parentId returns the id of the host machine if machineId a container id, or ""91// ParentId returns the id of the host machine if machineId a container id, or ""
92// if machineId is not for a container.92// if machineId is not for a container.
93func parentId(machineId string) string {93func ParentId(machineId string) string {
94 idParts := strings.Split(machineId, "/")94 idParts := strings.Split(machineId, "/")
95 if len(idParts) < 3 {95 if len(idParts) < 3 {
96 return ""96 return ""
@@ -98,6 +98,18 @@
98 return strings.Join(idParts[:len(idParts)-2], "/")98 return strings.Join(idParts[:len(idParts)-2], "/")
99}99}
100100
101// NestingLevel returns how many levels of nesting exist for a machine id.
102func NestingLevel(machineId string) int {
103 idParts := strings.Split(machineId, "/")
104 return (len(idParts) - 1) / 2
105}
106
107// TopParentId returns the id of the top level host machine for a container id.
108func TopParentId(machineId string) string {
109 idParts := strings.Split(machineId, "/")
110 return idParts[0]
111}
112
101// removeContainerRefOps returns the txn.Op's necessary to remove a machine container record.113// removeContainerRefOps returns the txn.Op's necessary to remove a machine container record.
102// These include removing the record itself and updating the host machine's children property.114// These include removing the record itself and updating the host machine's children property.
103func removeContainerRefOps(st *State, machineId string) []txn.Op {115func removeContainerRefOps(st *State, machineId string) []txn.Op {
@@ -108,7 +120,7 @@
108 Remove: true,120 Remove: true,
109 }121 }
110 // If the machine is a container, figure out it's parent host.122 // If the machine is a container, figure out it's parent host.
111 parentId := parentId(machineId)123 parentId := ParentId(machineId)
112 if parentId == "" {124 if parentId == "" {
113 return []txn.Op{removeRefOp}125 return []txn.Op{removeRefOp}
114 }126 }
115127
=== modified file 'state/export_test.go'
--- state/export_test.go 2013-05-02 15:55:42 +0000
+++ state/export_test.go 2013-06-17 04:13:30 +0000
@@ -93,6 +93,10 @@
93 return sch93 return sch
94}94}
9595
96func MachineIdLessThan(id1, id2 string) bool {
97 return machineIdLessThan(id1, id2)
98}
99
96func init() {100func init() {
97 logSize = logSizeTests101 logSize = logSizeTests
98}102}
99103
=== modified file 'state/machine.go'
--- state/machine.go 2013-06-13 14:39:31 +0000
+++ state/machine.go 2013-06-17 04:13:30 +0000
@@ -107,15 +107,26 @@
107 return machineGlobalKey(m.doc.Id)107 return machineGlobalKey(m.doc.Id)
108}108}
109109
110const machineTagPrefix = "machine-"
111
110// MachineTag returns the tag for the112// MachineTag returns the tag for the
111// machine with the given id.113// machine with the given id.
112func MachineTag(id string) string {114func MachineTag(id string) string {
113 tag := fmt.Sprintf("machine-%s", id)115 tag := fmt.Sprintf("%s%s", machineTagPrefix, id)
114 // Containers require "/" to be replaced by "-".116 // Containers require "/" to be replaced by "-".
115 tag = strings.Replace(tag, "/", "-", -1)117 tag = strings.Replace(tag, "/", "-", -1)
116 return tag118 return tag
117}119}
118120
121// MachineIdFromTag returns the machine id that was used to create the tag.
122func MachineIdFromTag(tag string) string {
123 // Strip off the "machine-" prefix.
124 id := tag[len(machineTagPrefix):]
125 // Put the slashes back.
126 id = strings.Replace(id, "-", "/", -1)
127 return id
128}
129
119// Tag returns a name identifying the machine that is safe to use130// Tag returns a name identifying the machine that is safe to use
120// as a file name. The returned name will be different from other131// as a file name. The returned name will be different from other
121// Tag values returned by any other entities from the same state.132// Tag values returned by any other entities from the same state.
@@ -240,7 +251,7 @@
240251
241// ParentId returns the Id of the host machine if this machine is a container.252// ParentId returns the Id of the host machine if this machine is a container.
242func (m *Machine) ParentId() (string, bool) {253func (m *Machine) ParentId() (string, bool) {
243 parentId := parentId(m.Id())254 parentId := ParentId(m.Id())
244 return parentId, parentId != ""255 return parentId, parentId != ""
245}256}
246257
247258
=== modified file 'state/machine_test.go'
--- state/machine_test.go 2013-06-12 00:01:40 +0000
+++ state/machine_test.go 2013-06-17 04:13:30 +0000
@@ -192,6 +192,15 @@
192 c.Assert(state.MachineTag("10/lxc/1"), Equals, "machine-10-lxc-1")192 c.Assert(state.MachineTag("10/lxc/1"), Equals, "machine-10-lxc-1")
193}193}
194194
195func (s *MachineSuite) TestMachineIdFromTag(c *C) {
196 c.Assert(state.MachineIdFromTag("machine-10"), Equals, "10")
197 // Check a container id.
198 c.Assert(state.MachineIdFromTag("machine-10-lxc-1"), Equals, "10/lxc/1")
199 // Check reversability.
200 nested := "2/kvm/0/lxc/3"
201 c.Assert(state.MachineIdFromTag(state.MachineTag(nested)), Equals, nested)
202}
203
195func (s *MachineSuite) TestSetMongoPassword(c *C) {204func (s *MachineSuite) TestSetMongoPassword(c *C) {
196 testSetMongoPassword(c, func(st *state.State) (entity, error) {205 testSetMongoPassword(c, func(st *state.State) (entity, error) {
197 return st.Machine(s.machine.Id())206 return st.Machine(s.machine.Id())
198207
=== modified file 'state/state.go'
--- state/state.go 2013-06-13 14:39:31 +0000
+++ state/state.go 2013-06-17 04:13:30 +0000
@@ -378,10 +378,49 @@
378func (ms machineDocSlice) Len() int { return len(ms) }378func (ms machineDocSlice) Len() int { return len(ms) }
379func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }379func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
380func (ms machineDocSlice) Less(i, j int) bool {380func (ms machineDocSlice) Less(i, j int) bool {
381 // There's nothing we can do with errors at this point.381 return machineIdLessThan(ms[i].Id, ms[j].Id)
382 m1, _ := strconv.Atoi(ms[i].Id)382}
383 m2, _ := strconv.Atoi(ms[j].Id)383
384 return m1 < m2384// machineIdLessThan returns true if id1 < id2, false otherwise.
385// Machine ids may include "/" separators if they are for a container so
386// the comparison is done by comparing the id component values from
387// left to right (most significant part to least significant). Ids for
388// host machines are always less than ids for their containers.
389func machineIdLessThan(id1, id2 string) bool {
390 // Most times, we are dealing with host machines and not containers, so we will
391 // try interpreting the ids as ints - this will be faster than dealing with the
392 // container ids below.
393 mint1, err1 := strconv.Atoi(id1)
394 mint2, err2 := strconv.Atoi(id2)
395 if err1 == nil && err2 == nil {
396 return mint1 < mint2
397 }
398 // We have at least one container id so it gets complicated.
399 idParts1 := strings.Split(id1, "/")
400 idParts2 := strings.Split(id2, "/")
401 nrParts1 := len(idParts1)
402 nrParts2 := len(idParts2)
403 minLen := nrParts1
404 if nrParts2 < minLen {
405 minLen = nrParts2
406 }
407 for x := 0; x < minLen; x++ {
408 m1 := idParts1[x]
409 m2 := idParts2[x]
410 if m1 == m2 {
411 continue
412 }
413 // See if the id part is a container type, and if so compare directly.
414 if x%2 == 1 {
415 return m1 < m2
416 }
417 // Compare the integer ids.
418 // There's nothing we can do with errors at this point.
419 mint1, _ := strconv.Atoi(m1)
420 mint2, _ := strconv.Atoi(m2)
421 return mint1 < mint2
422 }
423 return nrParts1 < nrParts2
385}424}
386425
387// Machine returns the machine with the given id.426// Machine returns the machine with the given id.
388427
=== modified file 'state/state_test.go'
--- state/state_test.go 2013-06-13 14:39:31 +0000
+++ state/state_test.go 2013-06-17 04:13:30 +0000
@@ -331,6 +331,39 @@
331 c.Assert(m.CheckProvisioned(state.BootstrapNonce), Equals, true)331 c.Assert(m.CheckProvisioned(state.BootstrapNonce), Equals, true)
332}332}
333333
334func (s *StateSuite) TestAddContainerToInjectedMachine(c *C) {
335 oneJob := []state.MachineJob{state.JobHostUnits}
336 m0, err := s.State.InjectMachine("series", emptyCons, state.InstanceId("i-mindustrious"), state.JobHostUnits, state.JobManageEnviron)
337 c.Assert(err, IsNil)
338
339 // Add first container.
340 params := state.AddMachineParams{
341 ParentId: "0",
342 ContainerType: state.LXC,
343 Series: "series",
344 Jobs: []state.MachineJob{state.JobHostUnits},
345 }
346 m, err := s.State.AddMachineWithConstraints(&params)
347 c.Assert(err, IsNil)
348 c.Assert(m.Id(), Equals, "0/lxc/0")
349 c.Assert(m.Series(), Equals, "series")
350 c.Assert(m.ContainerType(), Equals, state.LXC)
351 mcons, err := m.Constraints()
352 c.Assert(err, IsNil)
353 c.Assert(mcons, DeepEquals, emptyCons)
354 c.Assert(m.Jobs(), DeepEquals, oneJob)
355 s.assertMachineContainers(c, m0, []string{"0/lxc/0"})
356
357 // Add second container.
358 m, err = s.State.AddMachineWithConstraints(&params)
359 c.Assert(err, IsNil)
360 c.Assert(m.Id(), Equals, "0/lxc/1")
361 c.Assert(m.Series(), Equals, "series")
362 c.Assert(m.ContainerType(), Equals, state.LXC)
363 c.Assert(m.Jobs(), DeepEquals, oneJob)
364 s.assertMachineContainers(c, m0, []string{"0/lxc/0", "0/lxc/1"})
365}
366
334func (s *StateSuite) TestReadMachine(c *C) {367func (s *StateSuite) TestReadMachine(c *C) {
335 machine, err := s.State.AddMachine("series", state.JobHostUnits)368 machine, err := s.State.AddMachine("series", state.JobHostUnits)
336 c.Assert(err, IsNil)369 c.Assert(err, IsNil)
@@ -346,6 +379,19 @@
346 c.Assert(errors.IsNotFoundError(err), Equals, true)379 c.Assert(errors.IsNotFoundError(err), Equals, true)
347}380}
348381
382func (s *StateSuite) TestMachineIdLessThan(c *C) {
383 c.Assert(state.MachineIdLessThan("0", "0"), Equals, false)
384 c.Assert(state.MachineIdLessThan("0", "1"), Equals, true)
385 c.Assert(state.MachineIdLessThan("1", "0"), Equals, false)
386 c.Assert(state.MachineIdLessThan("10", "2"), Equals, false)
387 c.Assert(state.MachineIdLessThan("0", "0/lxc/0"), Equals, true)
388 c.Assert(state.MachineIdLessThan("0/lxc/0", "0"), Equals, false)
389 c.Assert(state.MachineIdLessThan("1", "0/lxc/0"), Equals, false)
390 c.Assert(state.MachineIdLessThan("0/lxc/0", "1"), Equals, true)
391 c.Assert(state.MachineIdLessThan("0/lxc/0/lxc/1", "0/lxc/0"), Equals, false)
392 c.Assert(state.MachineIdLessThan("0/kvm/0", "0/lxc/0"), Equals, true)
393}
394
349func (s *StateSuite) TestAllMachines(c *C) {395func (s *StateSuite) TestAllMachines(c *C) {
350 numInserts := 42396 numInserts := 42
351 for i := 0; i < numInserts; i++ {397 for i := 0; i < numInserts; i++ {
@@ -1559,3 +1605,21 @@
1559 }1605 }
1560 assertNoCleanupChange(c, st, cw)1606 assertNoCleanupChange(c, st, cw)
1561}1607}
1608
1609func (s *StateSuite) TestNestingLevel(c *C) {
1610 c.Assert(state.NestingLevel("0"), Equals, 0)
1611 c.Assert(state.NestingLevel("0/lxc/1"), Equals, 1)
1612 c.Assert(state.NestingLevel("0/lxc/1/kvm/0"), Equals, 2)
1613}
1614
1615func (s *StateSuite) TestTopParentId(c *C) {
1616 c.Assert(state.TopParentId("0"), Equals, "0")
1617 c.Assert(state.TopParentId("0/lxc/1"), Equals, "0")
1618 c.Assert(state.TopParentId("0/lxc/1/kvm/2"), Equals, "0")
1619}
1620
1621func (s *StateSuite) TestParentId(c *C) {
1622 c.Assert(state.ParentId("0"), Equals, "")
1623 c.Assert(state.ParentId("0/lxc/1"), Equals, "0")
1624 c.Assert(state.ParentId("0/lxc/1/kvm/0"), Equals, "0/lxc/1")
1625}
15621626
=== modified file 'worker/firewaller/firewaller_test.go'
--- worker/firewaller/firewaller_test.go 2013-05-27 03:00:31 +0000
+++ worker/firewaller/firewaller_test.go 2013-06-17 04:13:30 +0000
@@ -5,9 +5,9 @@
55
6import (6import (
7 . "launchpad.net/gocheck"7 . "launchpad.net/gocheck"
8 "launchpad.net/juju-core/environs"
9 "launchpad.net/juju-core/environs/config"8 "launchpad.net/juju-core/environs/config"
10 "launchpad.net/juju-core/environs/dummy"9 "launchpad.net/juju-core/environs/dummy"
10 "launchpad.net/juju-core/instance"
11 "launchpad.net/juju-core/juju/testing"11 "launchpad.net/juju-core/juju/testing"
12 "launchpad.net/juju-core/state"12 "launchpad.net/juju-core/state"
13 "launchpad.net/juju-core/state/api/params"13 "launchpad.net/juju-core/state/api/params"
@@ -33,7 +33,7 @@
3333
34// assertPorts retrieves the open ports of the instance and compares them34// assertPorts retrieves the open ports of the instance and compares them
35// to the expected.35// to the expected.
36func (s *FirewallerSuite) assertPorts(c *C, inst environs.Instance, machineId string, expected []params.Port) {36func (s *FirewallerSuite) assertPorts(c *C, inst instance.Instance, machineId string, expected []params.Port) {
37 s.State.StartSync()37 s.State.StartSync()
38 start := time.Now()38 start := time.Now()
39 for {39 for {
@@ -133,7 +133,7 @@
133}133}
134134
135// startInstance starts a new instance for the given machine.135// startInstance starts a new instance for the given machine.
136func (s *FirewallerSuite) startInstance(c *C, m *state.Machine) environs.Instance {136func (s *FirewallerSuite) startInstance(c *C, m *state.Machine) instance.Instance {
137 inst := testing.StartInstance(c, s.Conn.Environ, m.Id())137 inst := testing.StartInstance(c, s.Conn.Environ, m.Id())
138 err := m.SetProvisioned(inst.Id(), "fake_nonce")138 err := m.SetProvisioned(inst.Id(), "fake_nonce")
139 c.Assert(err, IsNil)139 c.Assert(err, IsNil)
140140
=== modified file 'worker/provisioner/broker.go'
--- worker/provisioner/broker.go 2013-06-07 01:36:48 +0000
+++ worker/provisioner/broker.go 2013-06-17 04:13:30 +0000
@@ -5,7 +5,7 @@
55
6import (6import (
7 "launchpad.net/juju-core/constraints"7 "launchpad.net/juju-core/constraints"
8 "launchpad.net/juju-core/environs"8 "launchpad.net/juju-core/instance"
9 "launchpad.net/juju-core/state"9 "launchpad.net/juju-core/state"
10 "launchpad.net/juju-core/state/api"10 "launchpad.net/juju-core/state/api"
11)11)
@@ -17,11 +17,11 @@
17 // unique within an environment, is used by juju to protect against the17 // unique within an environment, is used by juju to protect against the
18 // consequences of multiple instances being started with the same machine18 // consequences of multiple instances being started with the same machine
19 // id.19 // id.
20 StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance, error)20 StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, error)
2121
22 // StopInstances shuts down the given instances.22 // StopInstances shuts down the given instances.
23 StopInstances([]environs.Instance) error23 StopInstances([]instance.Instance) error
2424
25 // AllInstances returns all instances currently known to the broker.25 // AllInstances returns all instances currently known to the broker.
26 AllInstances() ([]environs.Instance, error)26 AllInstances() ([]instance.Instance, error)
27}27}
2828
=== modified file 'worker/provisioner/export_test.go'
--- worker/provisioner/export_test.go 2013-05-02 15:55:42 +0000
+++ worker/provisioner/export_test.go 2013-06-17 04:13:30 +0000
@@ -11,13 +11,13 @@
11// exported so we can manually close the Provisioners underlying11// exported so we can manually close the Provisioners underlying
12// state connection.12// state connection.
13func (p *Provisioner) CloseState() error {13func (p *Provisioner) CloseState() error {
14 return p.st.Close()14 return p.state.Close()
15}15}
1616
17// exported so we can discover all machines visible to the17// exported so we can discover all machines visible to the
18// Provisioners state connection.18// Provisioners state connection.
19func (p *Provisioner) AllMachines() ([]*state.Machine, error) {19func (p *Provisioner) AllMachines() ([]*state.Machine, error) {
20 return p.st.AllMachines()20 return p.state.AllMachines()
21}21}
2222
23func (o *configObserver) SetObserver(observer chan<- *config.Config) {23func (o *configObserver) SetObserver(observer chan<- *config.Config) {
2424
=== modified file 'worker/provisioner/provisioner.go'
--- worker/provisioner/provisioner.go 2013-06-11 02:15:31 +0000
+++ worker/provisioner/provisioner.go 2013-06-17 04:13:30 +0000
@@ -19,7 +19,7 @@
1919
20// Provisioner represents a running provisioning worker.20// Provisioner represents a running provisioning worker.
21type Provisioner struct {21type Provisioner struct {
22 st *state.State22 state *state.State
23 machineId string // Which machine runs the provisioner.23 machineId string // Which machine runs the provisioner.
24 environ environs.Environ24 environ environs.Environ
25 tomb tomb.Tomb25 tomb tomb.Tomb
@@ -46,7 +46,7 @@
46// and allocates them to the new machines.46// and allocates them to the new machines.
47func NewProvisioner(st *state.State, machineId string) *Provisioner {47func NewProvisioner(st *state.State, machineId string) *Provisioner {
48 p := &Provisioner{48 p := &Provisioner{
49 st: st,49 state: st,
50 machineId: machineId,50 machineId: machineId,
51 }51 }
52 go func() {52 go func() {
@@ -57,7 +57,7 @@
57}57}
5858
59func (p *Provisioner) loop() error {59func (p *Provisioner) loop() error {
60 environWatcher := p.st.WatchEnvironConfig()60 environWatcher := p.state.WatchEnvironConfig()
61 defer watcher.Stop(environWatcher, &p.tomb)61 defer watcher.Stop(environWatcher, &p.tomb)
6262
63 var err error63 var err error
@@ -66,27 +66,18 @@
66 return err66 return err
67 }67 }
6868
69 // Get a new StateInfo from the environment: the one used to
70 // launch the agent may refer to localhost, which will be
71 // unhelpful when attempting to run an agent on a new machine.
72 stateInfo, apiInfo, err := p.environ.StateInfo()
73 if err != nil {
74 return err
75 }
76
77 // Start a new worker for the environment provider.69 // Start a new worker for the environment provider.
7870
79 // Start responding to changes in machines, and to any further updates71 // Start responding to changes in machines, and to any further updates
80 // to the environment config.72 // to the environment config.
81 machinesWatcher := p.st.WatchEnvironMachines()73 machinesWatcher := p.state.WatchEnvironMachines()
82 environmentBroker := newEnvironBroker(p.environ)74 environmentBroker := newEnvironBroker(p.environ)
83 environmentProvisioner := newProvisionerTask(75 environmentProvisioner := newProvisionerTask(
84 p.machineId,76 p.machineId,
85 p.st,77 p.state,
86 machinesWatcher,78 machinesWatcher,
87 environmentBroker,79 environmentBroker,
88 stateInfo,80 )
89 apiInfo)
90 defer watcher.Stop(environmentProvisioner, &p.tomb)81 defer watcher.Stop(environmentProvisioner, &p.tomb)
9182
92 for {83 for {
@@ -103,6 +94,11 @@
103 }94 }
104 if err := p.setConfig(cfg); err != nil {95 if err := p.setConfig(cfg); err != nil {
105 logger.Errorf("loaded invalid environment configuration: %v", err)96 logger.Errorf("loaded invalid environment configuration: %v", err)
97 // We *shouldn't* ever get into a state here where we have an
98 // invalid config. If we do, stop the task and let jujud
99 // restart the provisioner, which will then wait for valid
100 // config.
101 return err
106 }102 }
107 }103 }
108 }104 }
109105
=== modified file 'worker/provisioner/provisioner_task.go'
--- worker/provisioner/provisioner_task.go 2013-06-10 04:25:51 +0000
+++ worker/provisioner/provisioner_task.go 2013-06-17 04:13:30 +0000
@@ -6,8 +6,8 @@
6import (6import (
7 "fmt"7 "fmt"
88
9 "launchpad.net/juju-core/environs"
10 "launchpad.net/juju-core/errors"9 "launchpad.net/juju-core/errors"
10 "launchpad.net/juju-core/instance"
11 "launchpad.net/juju-core/state"11 "launchpad.net/juju-core/state"
12 "launchpad.net/juju-core/state/api"12 "launchpad.net/juju-core/state/api"
13 "launchpad.net/juju-core/state/api/params"13 "launchpad.net/juju-core/state/api/params"
@@ -30,25 +30,17 @@
30 Changes() <-chan []string30 Changes() <-chan []string
31}31}
3232
33type MachineGetter interface {
34 Machine(id string) (*state.Machine, error)
35}
36
37func newProvisionerTask(33func newProvisionerTask(
38 machineId string,34 machineId string,
39 machineGetter MachineGetter,35 st *state.State,
40 watcher Watcher,36 watcher Watcher,
41 broker Broker,37 broker Broker,
42 stateInfo *state.Info,
43 apiInfo *api.Info,
44) ProvisionerTask {38) ProvisionerTask {
45 task := &provisionerTask{39 task := &provisionerTask{
46 machineId: machineId,40 machineId: machineId,
47 machineGetter: machineGetter,41 state: st,
48 machineWatcher: watcher,42 machineWatcher: watcher,
49 broker: broker,43 broker: broker,
50 stateInfo: stateInfo,
51 apiInfo: apiInfo,
52 machines: make(map[string]*state.Machine),44 machines: make(map[string]*state.Machine),
53 }45 }
54 go func() {46 go func() {
@@ -60,15 +52,13 @@
6052
61type provisionerTask struct {53type provisionerTask struct {
62 machineId string54 machineId string
63 machineGetter MachineGetter55 state *state.State
64 machineWatcher Watcher56 machineWatcher Watcher
65 broker Broker57 broker Broker
66 tomb tomb.Tomb58 tomb tomb.Tomb
67 stateInfo *state.Info
68 apiInfo *api.Info
6959
70 // instance id -> instance60 // instance id -> instance
71 instances map[state.InstanceId]environs.Instance61 instances map[state.InstanceId]instance.Instance
72 // machine id -> machine62 // machine id -> machine
73 machines map[string]*state.Machine63 machines map[string]*state.Machine
74}64}
@@ -159,7 +149,7 @@
159}149}
160150
161func (task *provisionerTask) populateMachineMaps(ids []string) error {151func (task *provisionerTask) populateMachineMaps(ids []string) error {
162 task.instances = make(map[state.InstanceId]environs.Instance)152 task.instances = make(map[state.InstanceId]instance.Instance)
163153
164 instances, err := task.broker.AllInstances()154 instances, err := task.broker.AllInstances()
165 if err != nil {155 if err != nil {
@@ -174,7 +164,7 @@
174 // change list.164 // change list.
175 // TODO(thumper): update for API server later to get all machines in one go.165 // TODO(thumper): update for API server later to get all machines in one go.
176 for _, id := range ids {166 for _, id := range ids {
177 machine, err := task.machineGetter.Machine(id)167 machine, err := task.state.Machine(id)
178 switch {168 switch {
179 case errors.IsNotFoundError(err):169 case errors.IsNotFoundError(err):
180 logger.Debugf("machine %q not found in state", id)170 logger.Debugf("machine %q not found in state", id)
@@ -238,9 +228,9 @@
238}228}
239229
240// findUnknownInstances finds instances which are not associated with a machine.230// findUnknownInstances finds instances which are not associated with a machine.
241func (task *provisionerTask) findUnknownInstances() ([]environs.Instance, error) {231func (task *provisionerTask) findUnknownInstances() ([]instance.Instance, error) {
242 // Make a copy of the instances we know about.232 // Make a copy of the instances we know about.
243 instances := make(map[state.InstanceId]environs.Instance)233 instances := make(map[state.InstanceId]instance.Instance)
244 for k, v := range task.instances {234 for k, v := range task.instances {
245 instances[k] = v235 instances[k] = v
246 }236 }
@@ -250,7 +240,7 @@
250 delete(instances, instId)240 delete(instances, instId)
251 }241 }
252 }242 }
253 var unknown []environs.Instance243 var unknown []instance.Instance
254 for _, i := range instances {244 for _, i := range instances {
255 unknown = append(unknown, i)245 unknown = append(unknown, i)
256 }246 }
@@ -258,11 +248,11 @@
258 return unknown, nil248 return unknown, nil
259}249}
260250
261// instancesForMachines returns a list of environs.Instance that represent251// instancesForMachines returns a list of instance.Instance that represent
262// the list of machines running in the provider. Missing machines are252// the list of machines running in the provider. Missing machines are
263// omitted from the list.253// omitted from the list.
264func (task *provisionerTask) instancesForMachines(machines []*state.Machine) []environs.Instance {254func (task *provisionerTask) instancesForMachines(machines []*state.Machine) []instance.Instance {
265 var instances []environs.Instance255 var instances []instance.Instance
266 for _, machine := range machines {256 for _, machine := range machines {
267 instId, ok := machine.InstanceId()257 instId, ok := machine.InstanceId()
268 if ok {258 if ok {
@@ -276,7 +266,7 @@
276 return instances266 return instances
277}267}
278268
279func (task *provisionerTask) stopInstances(instances []environs.Instance) error {269func (task *provisionerTask) stopInstances(instances []instance.Instance) error {
280 // Although calling StopInstance with an empty slice should produce no change in the270 // Although calling StopInstance with an empty slice should produce no change in the
281 // provider, environs like dummy do not consider this a noop.271 // provider, environs like dummy do not consider this a noop.
282 if len(instances) == 0 {272 if len(instances) == 0 {
@@ -306,7 +296,7 @@
306 // state.Info as the PA.296 // state.Info as the PA.
307 stateInfo, apiInfo, err := task.setupAuthentication(machine)297 stateInfo, apiInfo, err := task.setupAuthentication(machine)
308 if err != nil {298 if err != nil {
309 logger.Errorf("failed to setup authentication: %v", err)299 logger.Warningf("failed to setup authentication for machine %q: %v", machine.Id(), err)
310 return err300 return err
311 }301 }
312 cons, err := machine.Constraints()302 cons, err := machine.Constraints()
@@ -355,6 +345,22 @@
355}345}
356346
357func (task *provisionerTask) setupAuthentication(machine *state.Machine) (*state.Info, *api.Info, error) {347func (task *provisionerTask) setupAuthentication(machine *state.Machine) (*state.Info, *api.Info, error) {
348 // Grab a new list of state and api addresses each time along with the
349 // cert, as these can change during a processing loop. If for example the
350 // provisioner was starting 100 machines in one go, some of those may be
351 // new api servers. We should take advantage of those ASAP.
352 stateAddresses, err := task.state.Addresses()
353 if err != nil {
354 // This will only return an error if the config becomes invalid. In
355 // those situations, the provisioner bombs out and is restarted.
356 return nil, nil, fmt.Errorf("cannot get addresses from state: %v", err)
357 }
358 apiAddresses, err := task.state.APIAddresses()
359 if err != nil {
360 // Same for the api addresses. We technically shouldn't get here if
361 // we didn't fail before, but best to check.
362 return nil, nil, fmt.Errorf("cannot get api addresses from state: %v", err)
363 }
358 password, err := utils.RandomPassword()364 password, err := utils.RandomPassword()
359 if err != nil {365 if err != nil {
360 return nil, nil, fmt.Errorf("cannot make password for machine %v: %v", machine, err)366 return nil, nil, fmt.Errorf("cannot make password for machine %v: %v", machine, err)
@@ -362,11 +368,16 @@
362 if err := machine.SetMongoPassword(password); err != nil {368 if err := machine.SetMongoPassword(password); err != nil {
363 return nil, nil, fmt.Errorf("cannot set password for machine %v: %v", machine, err)369 return nil, nil, fmt.Errorf("cannot set password for machine %v: %v", machine, err)
364 }370 }
365 stateInfo := *task.stateInfo371 cert := task.state.CACert()
366 stateInfo.Tag = machine.Tag()372 return &state.Info{
367 stateInfo.Password = password373 Addrs: stateAddresses,
368 apiInfo := *task.apiInfo374 CACert: cert,
369 apiInfo.Tag = machine.Tag()375 Tag: machine.Tag(),
370 apiInfo.Password = password376 Password: password,
371 return &stateInfo, &apiInfo, nil377 }, &api.Info{
378 Addrs: apiAddresses,
379 CACert: cert,
380 Tag: machine.Tag(),
381 Password: password,
382 }, nil
372}383}
373384
=== modified file 'worker/provisioner/provisioner_test.go'
--- worker/provisioner/provisioner_test.go 2013-06-11 02:15:31 +0000
+++ worker/provisioner/provisioner_test.go 2013-06-17 04:13:30 +0000
@@ -12,10 +12,10 @@
12 "labix.org/v2/mgo/bson"12 "labix.org/v2/mgo/bson"
13 . "launchpad.net/gocheck"13 . "launchpad.net/gocheck"
14 "launchpad.net/juju-core/constraints"14 "launchpad.net/juju-core/constraints"
15 "launchpad.net/juju-core/environs"
16 "launchpad.net/juju-core/environs/config"15 "launchpad.net/juju-core/environs/config"
17 "launchpad.net/juju-core/environs/dummy"16 "launchpad.net/juju-core/environs/dummy"
18 "launchpad.net/juju-core/errors"17 "launchpad.net/juju-core/errors"
18 "launchpad.net/juju-core/instance"
19 "launchpad.net/juju-core/juju/testing"19 "launchpad.net/juju-core/juju/testing"
20 "launchpad.net/juju-core/state"20 "launchpad.net/juju-core/state"
21 "launchpad.net/juju-core/state/api/params"21 "launchpad.net/juju-core/state/api/params"
@@ -100,11 +100,11 @@
100 c.Assert(s.Stop(), IsNil)100 c.Assert(s.Stop(), IsNil)
101}101}
102102
103func (s *ProvisionerSuite) checkStartInstance(c *C, m *state.Machine) environs.Instance {103func (s *ProvisionerSuite) checkStartInstance(c *C, m *state.Machine) instance.Instance {
104 return s.checkStartInstanceCustom(c, m, "pork", constraints.Value{})104 return s.checkStartInstanceCustom(c, m, "pork", constraints.Value{})
105}105}
106106
107func (s *ProvisionerSuite) checkStartInstanceCustom(c *C, m *state.Machine, secret string, cons constraints.Value) (instance environs.Instance) {107func (s *ProvisionerSuite) checkStartInstanceCustom(c *C, m *state.Machine, secret string, cons constraints.Value) (instance instance.Instance) {
108 s.State.StartSync()108 s.State.StartSync()
109 for {109 for {
110 select {110 select {
@@ -160,7 +160,7 @@
160}160}
161161
162// checkStopInstances checks that an instance has been stopped.162// checkStopInstances checks that an instance has been stopped.
163func (s *ProvisionerSuite) checkStopInstances(c *C, instances ...environs.Instance) {163func (s *ProvisionerSuite) checkStopInstances(c *C, instances ...instance.Instance) {
164 s.State.StartSync()164 s.State.StartSync()
165 instanceIds := set.NewStrings()165 instanceIds := set.NewStrings()
166 for _, instance := range instances {166 for _, instance := range instances {
@@ -361,27 +361,6 @@
361 s.checkStartInstance(c, m)361 s.checkStartInstance(c, m)
362}362}
363363
364func (s *ProvisionerSuite) TestProvisioningDoesOccurAfterInvalidEnvironmentPublished(c *C) {
365 p := provisioner.NewProvisioner(s.State, "0")
366 defer stop(c, p)
367
368 // place a new machine into the state
369 m, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits)
370 c.Assert(err, IsNil)
371
372 s.checkStartInstance(c, m)
373
374 err = s.invalidateEnvironment(c)
375 c.Assert(err, IsNil)
376
377 // create a second machine
378 m, err = s.State.AddMachine(config.DefaultSeries, state.JobHostUnits)
379 c.Assert(err, IsNil)
380
381 // the PA should create it using the old environment
382 s.checkStartInstance(c, m)
383}
384
385func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *C) {364func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *C) {
386 p := provisioner.NewProvisioner(s.State, "0")365 p := provisioner.NewProvisioner(s.State, "0")
387 defer stop(c, p)366 defer stop(c, p)
@@ -466,55 +445,3 @@
466 c.Assert(err, IsNil)445 c.Assert(err, IsNil)
467 c.Assert(m0.Life(), Equals, state.Dying)446 c.Assert(m0.Life(), Equals, state.Dying)
468}447}
469
470func (s *ProvisionerSuite) TestProvisioningRecoversAfterInvalidEnvironmentPublished(c *C) {
471 p := provisioner.NewProvisioner(s.State, "0")
472 defer stop(c, p)
473
474 // place a new machine into the state
475 m, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits)
476 c.Assert(err, IsNil)
477 s.checkStartInstance(c, m)
478
479 err = s.invalidateEnvironment(c)
480 c.Assert(err, IsNil)
481 s.State.StartSync()
482
483 // create a second machine
484 m, err = s.State.AddMachine(config.DefaultSeries, state.JobHostUnits)
485 c.Assert(err, IsNil)
486
487 // the PA should create it using the old environment
488 s.checkStartInstance(c, m)
489
490 err = s.fixEnvironment()
491 c.Assert(err, IsNil)
492
493 // insert our observer
494 cfgObserver := make(chan *config.Config, 1)
495 p.SetObserver(cfgObserver)
496
497 cfg, err := s.State.EnvironConfig()
498 c.Assert(err, IsNil)
499 attrs := cfg.AllAttrs()
500 attrs["secret"] = "beef"
501 cfg, err = config.New(attrs)
502 c.Assert(err, IsNil)
503 err = s.State.SetEnvironConfig(cfg)
504
505 s.State.StartSync()
506
507 // wait for the PA to load the new configuration
508 select {
509 case <-cfgObserver:
510 case <-time.After(200 * time.Millisecond):
511 c.Fatalf("PA did not action config change")
512 }
513
514 // create a third machine
515 m, err = s.State.AddMachine(config.DefaultSeries, state.JobHostUnits)
516 c.Assert(err, IsNil)
517
518 // the PA should create it using the new environment
519 s.checkStartInstanceCustom(c, m, "beef", constraints.Value{})
520}

Subscribers

People subscribed via source and target branches