Merge lp:~waigani/juju-core/add-start-stop-to-container-manager-interface into lp:~go-bot/juju-core/trunk

Proposed by Jesse Meek
Status: Work in progress
Proposed branch: lp:~waigani/juju-core/add-start-stop-to-container-manager-interface
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 849 lines (+384/-101)
14 files modified
container/interface.go (+9/-1)
container/kvm/container.go (+73/-22)
container/kvm/containerfactory.go (+36/-10)
container/kvm/instance.go (+1/-4)
container/kvm/interface.go (+11/-6)
container/kvm/kvm.go (+19/-3)
container/kvm/kvm_test.go (+12/-1)
container/kvm/libvirt.go (+13/-3)
container/kvm/mock/mock-kvm.go (+67/-17)
container/kvm/mock/mock-kvm_test.go (+74/-18)
container/lxc/lxc.go (+36/-14)
container/lxc/lxc_test.go (+23/-0)
container/testing/common.go (+8/-0)
worker/provisioner/kvm-broker_test.go (+2/-2)
To merge this branch: bzr merge lp:~waigani/juju-core/add-start-stop-to-container-manager-interface
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+211832@code.launchpad.net

Description of the change

Container Manager Interface

Add StartContainer and StopContainer to
containerManger interface and implement
for lxc and kvm.

https://codereview.appspot.com/77970043/

To post a comment you must log in.
Revision history for this message
Jesse Meek (waigani) wrote :

Reviewers: mp+211832_code.launchpad.net,

Message:
Please take a look.

Description:
Container Manager Interface

Add StartContainer and StopContainer to
containerManger interface and implement
for lxc and kvm.

https://code.launchpad.net/~waigani/juju-core/add-start-stop-to-container-manager-interface/+merge/211832

(do not edit description out of merge proposal)

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

Affected files (+88, -66 lines):
   A [revision details]
   M container/interface.go
   M container/kvm/kvm.go
   M container/kvm/kvm_test.go
   M container/kvm/live_test.go
   M container/lxc/lxc.go
   M container/lxc/lxc_test.go
   M container/testing/common.go
   M provider/local/environ.go
   M worker/provisioner/kvm-broker.go
   M worker/provisioner/lxc-broker.go

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

I'm a bit nervous about the consistency of start/stop vs create/destroy,
because the InstanceBroker interface has different meanings. Please make
that interface consistent -- assuming you can do so completely
mechanically, LGTM; the rest are just trivials.

https://codereview.appspot.com/77970043/diff/20001/container/interface.go
File container/interface.go (right):

https://codereview.appspot.com/77970043/diff/20001/container/interface.go#newcode31
container/interface.go:31: StartContainer(instance.Instance) error
Blank lines between methods, please, it's getting harder to read.

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go
File container/kvm/container.go (left):

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go#oldcode20
container/kvm/container.go:20: }
One concern about caching these values: how will they hold up if someone
messes with our containers out-of-band? I'm mainly worried about the
local provider here.

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go
File container/kvm/container.go (right):

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go#newcode19
container/kvm/container.go:19: exists *bool
and between fields as well, actually

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go#newcode96
container/kvm/container.go:96: logger.Debugf("Start %s", c.name)
s/Start/start/

https://codereview.appspot.com/77970043/

Revision history for this message
Jesse Meek (waigani) wrote :

Please take a look.

https://codereview.appspot.com/77970043/diff/20001/container/interface.go
File container/interface.go (right):

https://codereview.appspot.com/77970043/diff/20001/container/interface.go#newcode31
container/interface.go:31: StartContainer(instance.Instance) error
On 2014/03/24 09:51:56, fwereade wrote:
> Blank lines between methods, please, it's getting harder to read.

Done.

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go
File container/kvm/container.go (left):

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go#oldcode20
container/kvm/container.go:20: }
On 2014/03/24 09:51:56, fwereade wrote:
> One concern about caching these values: how will they hold up if
someone messes
> with our containers out-of-band? I'm mainly worried about the local
provider
> here.

Good point. What is the cache actually giving us? How often do we call
the container methods Start, Stop, Create and Destroy and does the
expense of calling down to virsh and uvt-kvm each time have any
significant consequence? <- Questions for thumper.

Currently the container methods reset the values to nil after a
successful cache check. We could also reset the cache to nil if the
cache is not as expected and then rerun the check. But really, this is
just circumventing the cache.

On the other hand, what is our policy if people do mess with containers
out-of-band? Is that something we need to seamlessly recover from? We
could reset the cache to nil if the container methods error out for any
reason, so the next time they are called it will work. BUT this is just
adding more complexity at the expense of user experience.

Sigh, thinking out loud here ... ditching the cache seems simplest to
me.

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go
File container/kvm/container.go (right):

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go#newcode19
container/kvm/container.go:19: exists *bool
On 2014/03/24 09:51:56, fwereade wrote:
> and between fields as well, actually

Done.

https://codereview.appspot.com/77970043/diff/20001/container/kvm/container.go#newcode96
container/kvm/container.go:96: logger.Debugf("Start %s", c.name)
On 2014/03/24 09:51:56, fwereade wrote:
> s/Start/start/

Done.

https://codereview.appspot.com/77970043/

Revision history for this message
Jesse Meek (waigani) wrote :
Revision history for this message
Jesse Meek (waigani) wrote :

https://codereview.appspot.com/77970043/diff/60001/container/kvm/mock/mock-kvm_test.go
File container/kvm/mock/mock-kvm_test.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/kvm/mock/mock-kvm_test.go#newcode181
container/kvm/mock/mock-kvm_test.go:181: // first.Start()
The commented lines cause a go routine timeout. I'm not sure why?

https://codereview.appspot.com/77970043/

Revision history for this message
Tim Penhey (thumper) wrote :
Download full text (5.3 KiB)

NOT LGTM yet

https://codereview.appspot.com/77970043/diff/60001/container/interface.go
File container/interface.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/interface.go#newcode35
container/interface.go:35: // StopCOntainer stops a container
s/StopCOntainer/StopContainer/

StopContainer stops a started container?
What if the container is already stopped? Do we error?

https://codereview.appspot.com/77970043/diff/60001/container/kvm/container.go
File container/kvm/container.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/kvm/container.go#newcode21
container/kvm/container.go:21: exists *bool
We should move from a tree state boolean (which was sufficient), to an
actual state.

type kvmState string
var (
   unknown kvmState = "unknown"
   created kvmState = "created"
   running kvmState = "running"
   ...
)

https://codereview.appspot.com/77970043/diff/60001/container/kvm/container.go#newcode41
container/kvm/container.go:41: c.exists = nil
wat?

https://codereview.appspot.com/77970043/diff/60001/container/kvm/interface.go
File container/kvm/interface.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/kvm/interface.go#newcode32
container/kvm/interface.go:32: Destroy() error
What happens if the container has stopped?

https://codereview.appspot.com/77970043/diff/60001/container/kvm/interface.go#newcode34
container/kvm/interface.go:34: Start() error
need at least minimal docstrings on the methods

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm.go
File container/kvm/kvm.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm.go#newcode82
container/kvm/kvm.go:82: return nil
pretty sure that start container shouldn't do nothing

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm.go#newcode88
container/kvm/kvm.go:88: return kvmContainer.Stop()
doc string should at least say what happens in the error conditions.

what if the container doesn't exist, what if it is already stopped, etc

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm.go#newcode131
container/kvm/kvm.go:131: if err := kvmContainer.Create(startParams);
err != nil {
Worth noting that that Create both creates and starts the container.

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm.go#newcode141
container/kvm/kvm.go:141: if err := kvmContainer.Destroy(); err != nil {
equally worth noting that Destroy both stops and destroys the container.

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm_test.go
File container/kvm/kvm_test.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/kvm/kvm_test.go#newcode113
container/kvm/kvm_test.go:113:
c.Assert(containertesting.StopContainer(c, s.manager, instance),
gc.IsNil)
What about starting a started container, or stopping a stopped
container? the mock should as closely as possible mirror what the virsh
commands return

https://codereview.appspot.com/77970043/diff/60001/container/kvm/libvirt.go
File container/kvm/libvirt.go (right):

https://codereview.appspot.com/77970043/diff/60001/container/kvm/libvirt.go#newcode112
container/kvm/...

Read more...

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

On 2014/03/24 09:51:56, fwereade wrote:
> I'm a bit nervous about the consistency of start/stop vs
create/destroy, because
> the InstanceBroker interface has different meanings. Please make that
interface
> consistent -- assuming you can do so completely mechanically, LGTM;
the rest are
> just trivials.

The InstanceBroker is aimed at the provisioner, and for the provider to
support the methods
that will be called by the provisioner.

We have the lxcBroker and kvmBroker to do this mapping for us in the
provisioner code.

At the container level, it makes sense to have Create, Start, Stop, and
Destroy. This is
quite independent from the Broker.

https://codereview.appspot.com/77970043/

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

On 2014/03/26 02:27:50, thumper wrote:
> On 2014/03/24 09:51:56, fwereade wrote:
> > I'm a bit nervous about the consistency of start/stop vs
create/destroy,
> because
> > the InstanceBroker interface has different meanings. Please make
that
> interface
> > consistent -- assuming you can do so completely mechanically, LGTM;
the rest
> are
> > just trivials.

> The InstanceBroker is aimed at the provisioner, and for the provider
to support
> the methods
> that will be called by the provisioner.

> We have the lxcBroker and kvmBroker to do this mapping for us in the
provisioner
> code.

> At the container level, it makes sense to have Create, Start, Stop,
and Destroy.
> This is
> quite independent from the Broker.

As discussed live: I'd like the Start/Stop/Create/Destroy vocabulary to
have a consistent meaning across the codebase -- at least when talking
about such similar things as different implementations of Instance. If
that means the Broker methods start talking Create/Destroy, that works
just fine. I'm not asking for any sort of rearchitecting, just naming
consistency :).

https://codereview.appspot.com/77970043/

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

WIP to address tim's comments.

Revision history for this message
Jesse Meek (waigani) wrote :
Revision history for this message
Jesse Meek (waigani) wrote :

On 2014/03/31 06:15:26, waigani wrote:
> Please take a look.

I missed container/lxc/lxc.go:238, passing in instance. I'll follow up
with that. Should I make a new branch for updating the Broker interface?

https://codereview.appspot.com/77970043/

2456. By Jesse Meek

Checked KVM states via virsh and uvt-kvm and matched them against KvmStatus

Revision history for this message
Jesse Meek (waigani) wrote :

Unmerged revisions

2456. By Jesse Meek

Checked KVM states via virsh and uvt-kvm and matched them against KvmStatus

2455. By Jesse Meek

Merge trunk.

2454. By Jesse Meek

Add KvmState and KvmStatus. Update container methods and tests to use them.

2453. By Jesse Meek

added tests to mock-kvm and fixed tests in kvm-broker

2452. By Jesse Meek

Code cleanup from review.

2451. By Jesse Meek

merge trunk, fix tests

2450. By Jesse Meek

implement lxc start/stop container, add tests

2449. By Jesse Meek

Added StartContainer and StopContainer

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'container/interface.go'
2--- container/interface.go 2014-03-19 21:08:58 +0000
3+++ container/interface.go 2014-03-31 22:42:50 +0000
4@@ -25,8 +25,16 @@
5 machineConfig *cloudinit.MachineConfig,
6 series string,
7 network *NetworkConfig) (instance.Instance, *instance.HardwareCharacteristics, error)
8- // DestroyContainer stops and destroyes the container identified by Instance.
9+
10+ // DestroyContainer stops and destroys the container identified by Instance.
11 DestroyContainer(instance.Instance) error
12+
13+ // StartContainer starts a stopped container
14+ StartContainer(instance.Instance) error
15+
16+ // StopContainer stops a started container
17+ StopContainer(instance.Instance) error
18+
19 // ListContainers return a list of containers that have been started by
20 // this manager.
21 ListContainers() ([]instance.Instance, error)
22
23=== modified file 'container/kvm/container.go'
24--- container/kvm/container.go 2013-12-05 22:32:47 +0000
25+++ container/kvm/container.go 2014-03-31 22:42:50 +0000
26@@ -10,13 +10,20 @@
27 "launchpad.net/juju-core/log"
28 )
29
30+// KvmStatus is a cached status of the container
31+type KvmStatus string
32+
33+const (
34+ Unknown KvmStatus = "unknown"
35+ Stopped KvmStatus = "stopped"
36+ Running KvmStatus = "running"
37+ Destroyed KvmStatus = "destroyed"
38+)
39+
40 type kvmContainer struct {
41- factory *containerFactory
42- name string
43- // started is a three state boolean, true, false, or unknown
44- // this allows for checking when we don't know, but using a
45- // value if we already know it (like in the list situation).
46- started *bool
47+ factory *containerFactory
48+ name string
49+ cachedStatus KvmStatus
50 }
51
52 var _ Container = (*kvmContainer)(nil)
53@@ -25,7 +32,11 @@
54 return c.name
55 }
56
57-func (c *kvmContainer) Start(params StartParams) error {
58+func (c *kvmContainer) Create(params StartParams) error {
59+ if c.Exists() {
60+ logger.Debugf("%s already exists", c.name)
61+ return nil
62+ }
63 logger.Debugf("Synchronise images for %s %s", params.Series, params.Arch)
64 if err := SyncImages(params.Series, params.Arch); err != nil {
65 return err
66@@ -49,34 +60,74 @@
67 CpuCores: params.CpuCores,
68 RootDisk: params.RootDisk,
69 }); err != nil {
70+ c.cachedStatus = Unknown
71 return err
72 }
73-
74+ c.cachedStatus = Running
75 logger.Debugf("Set machine %s to autostart", c.name)
76 return AutostartMachine(c.name)
77 }
78
79+func (c *kvmContainer) Destroy() error {
80+ if !c.Exists() {
81+ logger.Debugf("%s does not exist", c.name)
82+ return nil
83+ }
84+ logger.Debugf("Destroy %s", c.name)
85+ if err := DestroyMachine(c.name); err != nil {
86+ c.cachedStatus = Unknown
87+ return err
88+ }
89+ c.cachedStatus = Destroyed
90+ return nil
91+
92+}
93+
94 func (c *kvmContainer) Stop() error {
95- if !c.IsRunning() {
96- logger.Debugf("%s is already stopped", c.name)
97+ if c.Status() == Stopped {
98+ logger.Debugf("%s is already Stopped", c.name)
99 return nil
100 }
101- // Make started state unknown again.
102- c.started = nil
103 logger.Debugf("Stop %s", c.name)
104- return DestroyMachine(c.name)
105-}
106-
107-func (c *kvmContainer) IsRunning() bool {
108- if c.started != nil {
109- return *c.started
110- }
111+ if err := StopMachine(c.name); err != nil {
112+ c.cachedStatus = Unknown
113+ return err
114+ }
115+ c.cachedStatus = Stopped
116+ return nil
117+}
118+
119+func (c *kvmContainer) Start() error {
120+ if c.Status() == Running {
121+ logger.Debugf("%s is already Running", c.name)
122+ return nil
123+ }
124+ logger.Debugf("start %s", c.name)
125+ if err := StartMachine(c.name); err != nil {
126+ c.cachedStatus = Unknown
127+ return err
128+ }
129+ c.cachedStatus = Running
130+ return nil
131+}
132+
133+func (c *kvmContainer) Status() KvmStatus {
134+ if c.cachedStatus == Unknown {
135+ c.cachedStatus = GetStatusFromState(c.State())
136+ }
137+ return c.cachedStatus
138+}
139+
140+func (c *kvmContainer) State() KvmState {
141 machines, err := ListMachines()
142 if err != nil {
143- return false
144+ return KvmState("")
145 }
146- c.started = isRunning(machines[c.name])
147- return *c.started
148+ return KvmState(machines[c.name])
149+}
150+
151+func (c *kvmContainer) Exists() bool {
152+ return c.Status() == Running || c.Status() == Stopped
153 }
154
155 func (c *kvmContainer) String() string {
156
157=== modified file 'container/kvm/containerfactory.go'
158--- container/kvm/containerfactory.go 2013-11-18 22:26:36 +0000
159+++ container/kvm/containerfactory.go 2014-03-31 22:42:50 +0000
160@@ -15,12 +15,38 @@
161 }
162 }
163
164-func isRunning(value string) *bool {
165- var result *bool = new(bool)
166- if value == "running" {
167- *result = true
168- }
169- return result
170+// KvmState is the actual state of the kvm container, as returned by the cmd "uvt-kvm list" or
171+// "virsh list".
172+type KvmState string
173+
174+const (
175+ KvmRunning KvmState = "running"
176+ KvmShutoff KvmState = "shut off"
177+ KvmStopped KvmState = "stopped"
178+ KvmIdle KvmState = "idle"
179+ KvmPaused KvmState = "paused"
180+ KvmShutdown KvmState = "shutdown"
181+ KvmCrashed KvmState = "crashed"
182+ KvmDying KvmState = "dying"
183+ KvmPmSuspended KvmState = "pmsuspended"
184+)
185+
186+func GetStatusFromState(state KvmState) KvmStatus {
187+ stateStatus := map[KvmState]KvmStatus{
188+ KvmRunning: Running,
189+ KvmIdle: Running,
190+ KvmPaused: Running,
191+ KvmPmSuspended: Running,
192+ KvmStopped: Stopped,
193+ KvmShutdown: Stopped,
194+ KvmShutoff: Stopped,
195+ KvmCrashed: Stopped,
196+ KvmDying: Stopped,
197+ }
198+ if _, ok := stateStatus[state]; !ok {
199+ return Unknown
200+ }
201+ return stateStatus[state]
202 }
203
204 func (factory *containerFactory) List() (result []Container, err error) {
205@@ -28,11 +54,11 @@
206 if err != nil {
207 return nil, err
208 }
209- for hostname, status := range machines {
210+ for hostname, state := range machines {
211 result = append(result, &kvmContainer{
212- factory: factory,
213- name: hostname,
214- started: isRunning(status),
215+ factory: factory,
216+ name: hostname,
217+ cachedStatus: GetStatusFromState(state),
218 })
219
220 }
221
222=== modified file 'container/kvm/instance.go'
223--- container/kvm/instance.go 2013-12-16 09:53:26 +0000
224+++ container/kvm/instance.go 2014-03-31 22:42:50 +0000
225@@ -23,10 +23,7 @@
226
227 // Status implements instance.Instance.Status.
228 func (kvm *kvmInstance) Status() string {
229- if kvm.container.IsRunning() {
230- return "running"
231- }
232- return "stopped"
233+ return string(kvm.container.Status())
234 }
235
236 func (*kvmInstance) Refresh() error {
237
238=== modified file 'container/kvm/interface.go'
239--- container/kvm/interface.go 2013-12-05 03:38:24 +0000
240+++ container/kvm/interface.go 2014-03-31 22:42:50 +0000
241@@ -25,14 +25,19 @@
242 // Name returns the name of the container.
243 Name() string
244
245- // Start runs the container as a daemon.
246- Start(params StartParams) error
247-
248- // Stop terminates the running container.
249+ // Create runs the container as a daemon.
250+ Create(params StartParams) error
251+
252+ // Destroy terminates the container.
253+ Destroy() error
254+
255+ // Start starts the container.
256+ Start() error
257+
258+ // Stop stops the container.
259 Stop() error
260
261- // IsRunning returns wheter or not the container is running and active.
262- IsRunning() bool
263+ Status() KvmStatus
264
265 // String returns information about the container, like the name, state,
266 // and process id.
267
268=== modified file 'container/kvm/kvm.go'
269--- container/kvm/kvm.go 2014-03-19 21:08:58 +0000
270+++ container/kvm/kvm.go 2014-03-31 22:42:50 +0000
271@@ -78,6 +78,21 @@
272
273 var _ container.Manager = (*containerManager)(nil)
274
275+// StartContainer starts a container that exists but is stopped.
276+func (manager *containerManager) StartContainer(instance instance.Instance) error {
277+ name := string(instance.Id())
278+ kvmContainer := KvmObjectFactory.New(name)
279+ return kvmContainer.Start()
280+}
281+
282+// StopContainer stops an existing container that is running.
283+func (manager *containerManager) StopContainer(instance instance.Instance) error {
284+ name := string(instance.Id())
285+ kvmContainer := KvmObjectFactory.New(name)
286+ return kvmContainer.Stop()
287+}
288+
289+// CreateContainer creates and starts the container.
290 func (manager *containerManager) CreateContainer(
291 machineConfig *cloudinit.MachineConfig,
292 series string,
293@@ -118,17 +133,18 @@
294 }
295
296 logger.Tracef("create the container, constraints: %v", machineConfig.Constraints)
297- if err := kvmContainer.Start(startParams); err != nil {
298+ if err := kvmContainer.Create(startParams); err != nil {
299 return nil, nil, log.LoggedErrorf(logger, "kvm container creation failed: %v", err)
300 }
301 logger.Tracef("kvm container created")
302 return &kvmInstance{kvmContainer, name}, &hardware, nil
303 }
304
305+// DestroyContainer stops and destroys the container.
306 func (manager *containerManager) DestroyContainer(instance instance.Instance) error {
307 name := string(instance.Id())
308 kvmContainer := KvmObjectFactory.New(name)
309- if err := kvmContainer.Stop(); err != nil {
310+ if err := kvmContainer.Destroy(); err != nil {
311 logger.Errorf("failed to stop kvm container: %v", err)
312 return err
313 }
314@@ -148,7 +164,7 @@
315 if !strings.HasPrefix(name, managerPrefix) {
316 continue
317 }
318- if container.IsRunning() {
319+ if container.Status() == Running {
320 result = append(result, &kvmInstance{container, name})
321 }
322 }
323
324=== modified file 'container/kvm/kvm_test.go'
325--- container/kvm/kvm_test.go 2014-03-19 21:08:58 +0000
326+++ container/kvm/kvm_test.go 2014-03-31 22:42:50 +0000
327@@ -58,7 +58,7 @@
328 func (s *KVMSuite) createRunningContainer(c *gc.C, name string) kvm.Container {
329 kvmContainer := s.Factory.New(name)
330 network := container.BridgeNetworkConfig("testbr0")
331- c.Assert(kvmContainer.Start(kvm.StartParams{
332+ c.Assert(kvmContainer.Create(kvm.StartParams{
333 Series: "quantal",
334 Arch: version.Current.Arch,
335 UserDataFile: "userdata.txt",
336@@ -108,6 +108,17 @@
337 c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory)
338 }
339
340+func (s *KVMSuite) TestStartContainer(c *gc.C) {
341+ instance := containertesting.CreateContainer(c, s.manager, "1/kvm/0")
342+ c.Assert(containertesting.StopContainer(c, s.manager, instance), gc.IsNil)
343+ c.Assert(containertesting.StartContainer(c, s.manager, instance), gc.IsNil)
344+}
345+
346+func (s *KVMSuite) TestStopContainer(c *gc.C) {
347+ instance := containertesting.CreateContainer(c, s.manager, "1/kvm/0")
348+ c.Assert(containertesting.StopContainer(c, s.manager, instance), gc.IsNil)
349+}
350+
351 type ConstraintsSuite struct {
352 testbase.LoggingSuite
353 }
354
355=== modified file 'container/kvm/libvirt.go'
356--- container/kvm/libvirt.go 2013-12-05 22:32:47 +0000
357+++ container/kvm/libvirt.go 2014-03-31 22:42:50 +0000
358@@ -104,6 +104,16 @@
359 return err
360 }
361
362+func StopMachine(hostname string) error {
363+ _, err := run("virsh", "destroy", hostname)
364+ return err
365+}
366+
367+func StartMachine(hostname string) error {
368+ _, err := run("virsh", "start", hostname)
369+ return err
370+}
371+
372 // AutostartMachine indicates that the virtual machines should automatically
373 // restart when the host restarts.
374 func AutostartMachine(hostname string) error {
375@@ -113,7 +123,7 @@
376
377 // ListMachines returns a map of machine name to state, where state is one of:
378 // running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended.
379-func ListMachines() (map[string]string, error) {
380+func ListMachines() (map[string]KvmState, error) {
381 output, err := run("virsh", "-q", "list", "--all")
382 if err != nil {
383 return nil, err
384@@ -122,11 +132,11 @@
385 // Regex matching is the easiest way to match the lines.
386 // id hostname status
387 // separated by whitespace, with whitespace at the start too.
388- result := make(map[string]string)
389+ result := make(map[string]KvmState)
390 for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) {
391 hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s)
392 parts := strings.SplitN(string(hostnameAndStatus), " ", 2)
393- result[parts[0]] = parts[1]
394+ result[parts[0]] = KvmState(parts[1])
395 }
396 return result, nil
397 }
398
399=== modified file 'container/kvm/mock/mock-kvm.go'
400--- container/kvm/mock/mock-kvm.go 2013-12-05 03:38:24 +0000
401+++ container/kvm/mock/mock-kvm.go 2014-03-31 22:42:50 +0000
402@@ -15,14 +15,22 @@
403 type Action int
404
405 const (
406+ // A container has been created..
407+ Created Action = iota
408+ // A container has been destroyed.
409+ Destroyed
410 // A container has been started.
411- Started Action = iota
412+ Started
413 // A container has been stopped.
414 Stopped
415 )
416
417 func (action Action) String() string {
418 switch action {
419+ case Created:
420+ return "Created"
421+ case Destroyed:
422+ return "Destroyed"
423 case Started:
424 return "Started"
425 case Stopped:
426@@ -45,48 +53,77 @@
427 }
428
429 type mockFactory struct {
430- instances map[string]kvm.Container
431+ instances map[string]*mockContainer
432 listeners []chan<- Event
433 }
434
435-func MockFactory() ContainerFactory {
436+func MockFactory() *mockFactory {
437 return &mockFactory{
438- instances: make(map[string]kvm.Container),
439+ instances: make(map[string]*mockContainer),
440 }
441 }
442
443 type mockContainer struct {
444- factory *mockFactory
445- name string
446- started bool
447+ factory *mockFactory
448+ name string
449+ cachedStatus kvm.KvmStatus
450+ mockState kvm.KvmState
451 }
452
453+var _ kvm.Container = (*mockContainer)(nil)
454+var _ ContainerFactory = (*mockFactory)(nil)
455+
456 // Name returns the name of the container.
457 func (mock *mockContainer) Name() string {
458 return mock.name
459 }
460
461-func (mock *mockContainer) Start(params kvm.StartParams) error {
462- if mock.started {
463+func (mock *mockContainer) Create(params kvm.StartParams) error {
464+ if mock.Exists() {
465+ return fmt.Errorf("container already exists")
466+ }
467+ mock.factory.notify(Created, mock.name)
468+ mock.cachedStatus = kvm.Running
469+ return nil
470+}
471+
472+// Destroy terminates the running container.
473+func (mock *mockContainer) Destroy() error {
474+ if !mock.Exists() {
475+ return fmt.Errorf("container does not exist")
476+ }
477+ mock.factory.notify(Destroyed, mock.name)
478+ mock.cachedStatus = kvm.Destroyed
479+ return nil
480+}
481+
482+func (mock *mockContainer) Start() error {
483+ if mock.Status() == kvm.Running {
484 return fmt.Errorf("container is already running")
485 }
486- mock.started = true
487 mock.factory.notify(Started, mock.name)
488+ mock.cachedStatus = kvm.Running
489 return nil
490 }
491
492-// Stop terminates the running container.
493 func (mock *mockContainer) Stop() error {
494- if !mock.started {
495+ if mock.Status() != kvm.Running {
496 return fmt.Errorf("container is not running")
497 }
498- mock.started = false
499 mock.factory.notify(Stopped, mock.name)
500+ mock.cachedStatus = kvm.Stopped
501 return nil
502 }
503
504-func (mock *mockContainer) IsRunning() bool {
505- return mock.started
506+func (mock *mockContainer) Status() kvm.KvmStatus {
507+ if mock.cachedStatus == kvm.Unknown {
508+ mock.cachedStatus = kvm.GetStatusFromState(mock.State())
509+ }
510+ return mock.cachedStatus
511+}
512+
513+func (c *mockContainer) Exists() bool {
514+ return c.Status() == kvm.Running || c.Status() == kvm.Stopped
515 }
516
517 // String returns information about the container.
518@@ -103,9 +140,12 @@
519 if ok {
520 return container
521 }
522+ state := kvm.KvmState("")
523 container = &mockContainer{
524- factory: mock,
525- name: name,
526+ factory: mock,
527+ name: name,
528+ cachedStatus: kvm.GetStatusFromState(state),
529+ mockState: state,
530 }
531 mock.instances[name] = container
532 return container
533@@ -118,6 +158,16 @@
534 return
535 }
536
537+func (mock *mockContainer) State() kvm.KvmState {
538+ factory := MockFactory()
539+ for _, container := range factory.instances {
540+ if container.Name() == mock.name {
541+ return container.mockState
542+ }
543+ }
544+ return kvm.KvmState("")
545+}
546+
547 func (mock *mockFactory) notify(action Action, instanceId string) {
548 event := Event{action, instanceId}
549 for _, c := range mock.listeners {
550
551=== modified file 'container/kvm/mock/mock-kvm_test.go'
552--- container/kvm/mock/mock-kvm_test.go 2014-03-13 07:54:56 +0000
553+++ container/kvm/mock/mock-kvm_test.go 2014-03-31 22:42:50 +0000
554@@ -39,41 +39,83 @@
555 factory := mock.MockFactory()
556 container := factory.New("first")
557 c.Assert(container.Name(), gc.Equals, "first")
558- c.Assert(container.IsRunning(), jc.IsFalse)
559+ c.Assert(container.Status(), gc.Not(gc.Equals), kvm.Running)
560 }
561
562 func (*MockSuite) TestContainerStoppingStoppedErrors(c *gc.C) {
563 factory := mock.MockFactory()
564 container := factory.New("first")
565- err := container.Stop()
566+ err := container.Create(kvm.StartParams{})
567+ c.Assert(err, gc.IsNil)
568+ err = container.Stop()
569+ c.Assert(err, gc.IsNil)
570+ err = container.Stop()
571 c.Assert(err, gc.ErrorMatches, "container is not running")
572 }
573
574+func (*MockSuite) TestContainerDestroyDestroyedErrors(c *gc.C) {
575+ factory := mock.MockFactory()
576+ container := factory.New("first")
577+ err := container.Destroy()
578+ c.Assert(err, gc.ErrorMatches, "container does not exist")
579+}
580+
581+func (*MockSuite) TestContainerCreateStarts(c *gc.C) {
582+ factory := mock.MockFactory()
583+ container := factory.New("first")
584+ err := container.Create(kvm.StartParams{})
585+ c.Assert(err, gc.IsNil)
586+ c.Assert(container.Status(), gc.Equals, kvm.Running)
587+}
588+
589 func (*MockSuite) TestContainerStartStarts(c *gc.C) {
590 factory := mock.MockFactory()
591 container := factory.New("first")
592- err := container.Start(kvm.StartParams{})
593- c.Assert(err, gc.IsNil)
594- c.Assert(container.IsRunning(), jc.IsTrue)
595+ err := container.Create(kvm.StartParams{})
596+ c.Assert(err, gc.IsNil)
597+ err = container.Stop()
598+ c.Assert(err, gc.IsNil)
599+ err = container.Start()
600+ c.Assert(err, gc.IsNil)
601+ c.Assert(container.Status(), gc.Equals, kvm.Running)
602 }
603
604 func (*MockSuite) TestContainerStartingRunningErrors(c *gc.C) {
605 factory := mock.MockFactory()
606 container := factory.New("first")
607- err := container.Start(kvm.StartParams{})
608+ err := container.Create(kvm.StartParams{})
609 c.Assert(err, gc.IsNil)
610- err = container.Start(kvm.StartParams{})
611+ err = container.Start()
612 c.Assert(err, gc.ErrorMatches, "container is already running")
613 }
614
615+func (*MockSuite) TestContainerCreatingRunningErrors(c *gc.C) {
616+ factory := mock.MockFactory()
617+ container := factory.New("first")
618+ err := container.Create(kvm.StartParams{})
619+ c.Assert(err, gc.IsNil)
620+ err = container.Create(kvm.StartParams{})
621+ c.Assert(err, gc.ErrorMatches, "container already exists")
622+}
623+
624 func (*MockSuite) TestContainerStoppingRunningStops(c *gc.C) {
625 factory := mock.MockFactory()
626 container := factory.New("first")
627- err := container.Start(kvm.StartParams{})
628+ err := container.Create(kvm.StartParams{})
629 c.Assert(err, gc.IsNil)
630 err = container.Stop()
631 c.Assert(err, gc.IsNil)
632- c.Assert(container.IsRunning(), jc.IsFalse)
633+ c.Assert(container.Status(), gc.Not(gc.Equals), kvm.Running)
634+}
635+
636+func (*MockSuite) TestContainerDestroyingRunningStops(c *gc.C) {
637+ factory := mock.MockFactory()
638+ container := factory.New("first")
639+ err := container.Create(kvm.StartParams{})
640+ c.Assert(err, gc.IsNil)
641+ err = container.Destroy()
642+ c.Assert(err, gc.IsNil)
643+ c.Assert(container.Status(), gc.Not(gc.Equals), kvm.Running)
644 }
645
646 func (*MockSuite) TestAddListener(c *gc.C) {
647@@ -127,20 +169,28 @@
648
649 func (*MockSuite) TestEvents(c *gc.C) {
650 factory := mock.MockFactory()
651- listener := make(chan mock.Event, 5)
652+ listener := make(chan mock.Event, 15)
653 factory.AddListener(listener)
654
655 first := factory.New("first")
656 second := factory.New("second")
657- first.Start(kvm.StartParams{})
658- second.Start(kvm.StartParams{})
659+ first.Create(kvm.StartParams{})
660+ second.Create(kvm.StartParams{})
661+ first.Stop()
662 second.Stop()
663- first.Stop()
664+ first.Start()
665+ second.Start()
666+ second.Destroy()
667+ first.Destroy()
668
669+ c.Assert(<-listener, gc.Equals, mock.Event{mock.Created, "first"})
670+ c.Assert(<-listener, gc.Equals, mock.Event{mock.Created, "second"})
671+ c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "first"})
672+ c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "second"})
673 c.Assert(<-listener, gc.Equals, mock.Event{mock.Started, "first"})
674 c.Assert(<-listener, gc.Equals, mock.Event{mock.Started, "second"})
675- c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "second"})
676- c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "first"})
677+ c.Assert(<-listener, gc.Equals, mock.Event{mock.Destroyed, "second"})
678+ c.Assert(<-listener, gc.Equals, mock.Event{mock.Destroyed, "first"})
679 }
680
681 func (*MockSuite) TestEventsGoToAllListeners(c *gc.C) {
682@@ -151,11 +201,17 @@
683 factory.AddListener(second)
684
685 container := factory.New("container")
686- container.Start(kvm.StartParams{})
687+ container.Create(kvm.StartParams{})
688 container.Stop()
689+ container.Start()
690+ container.Destroy()
691
692+ c.Assert(<-first, gc.Equals, mock.Event{mock.Created, "container"})
693+ c.Assert(<-second, gc.Equals, mock.Event{mock.Created, "container"})
694+ c.Assert(<-first, gc.Equals, mock.Event{mock.Stopped, "container"})
695+ c.Assert(<-second, gc.Equals, mock.Event{mock.Stopped, "container"})
696 c.Assert(<-first, gc.Equals, mock.Event{mock.Started, "container"})
697 c.Assert(<-second, gc.Equals, mock.Event{mock.Started, "container"})
698- c.Assert(<-first, gc.Equals, mock.Event{mock.Stopped, "container"})
699- c.Assert(<-second, gc.Equals, mock.Event{mock.Stopped, "container"})
700+ c.Assert(<-first, gc.Equals, mock.Event{mock.Destroyed, "container"})
701+ c.Assert(<-second, gc.Equals, mock.Event{mock.Destroyed, "container"})
702 }
703
704=== modified file 'container/lxc/lxc.go'
705--- container/lxc/lxc.go 2014-03-19 21:08:58 +0000
706+++ container/lxc/lxc.go 2014-03-31 22:42:50 +0000
707@@ -112,6 +112,36 @@
708 }, nil
709 }
710
711+func (manager *containerManager) StartContainer(instance instance.Instance) error {
712+ name := string(instance.Id())
713+ lxcContainer := LxcObjectFactory.New(name)
714+ // Create the cloud-init.
715+ directory, err := container.NewDirectory(name)
716+ if err != nil {
717+ return err
718+ }
719+ // Start the lxc container with the appropriate settings for grabbing the
720+ // console output and a log file.
721+ consoleFile := filepath.Join(directory, "console.log")
722+ lxcContainer.SetLogFile(filepath.Join(directory, "container.log"), golxc.LogDebug)
723+ logger.Tracef("start the container")
724+ // We explicitly don't pass through the config file to the container.Start
725+ // method as we have passed it through at container creation time. This
726+ // is necessary to get the appropriate rootfs reference without explicitly
727+ // setting it ourselves.
728+ if err = lxcContainer.Start("", consoleFile); err != nil {
729+ logger.Errorf("container failed to start: %v", err)
730+ return err
731+ }
732+ return nil
733+}
734+
735+func (manager *containerManager) StopContainer(instance instance.Instance) error {
736+ name := string(instance.Id())
737+ lxcContainer := LxcObjectFactory.New(name)
738+ return lxcContainer.Stop()
739+}
740+
741 func (manager *containerManager) CreateContainer(
742 machineConfig *cloudinit.MachineConfig,
743 series string,
744@@ -204,25 +234,17 @@
745 if err := mountHostLogDir(name, manager.logdir); err != nil {
746 return nil, nil, err
747 }
748- // Start the lxc container with the appropriate settings for grabbing the
749- // console output and a log file.
750- consoleFile := filepath.Join(directory, "console.log")
751- lxcContainer.SetLogFile(filepath.Join(directory, "container.log"), golxc.LogDebug)
752- logger.Tracef("start the container")
753- // We explicitly don't pass through the config file to the container.Start
754- // method as we have passed it through at container creation time. This
755- // is necessary to get the appropriate rootfs reference without explicitly
756- // setting it ourselves.
757- if err = lxcContainer.Start("", consoleFile); err != nil {
758- logger.Errorf("container failed to start: %v", err)
759- return nil, nil, err
760- }
761+
762+ container := &lxcInstance{lxcContainer, name}
763+
764+ manager.StartContainer(container)
765+
766 arch := version.Current.Arch
767 hardware := &instance.HardwareCharacteristics{
768 Arch: &arch,
769 }
770 logger.Tracef("container %q started: %v", name, time.Now().Sub(start))
771- return &lxcInstance{lxcContainer, name}, hardware, nil
772+ return container, hardware, nil
773 }
774
775 func appendToContainerConfig(name, line string) error {
776
777=== modified file 'container/lxc/lxc_test.go'
778--- container/lxc/lxc_test.go 2014-03-19 21:08:58 +0000
779+++ container/lxc/lxc_test.go 2014-03-31 22:42:50 +0000
780@@ -113,6 +113,29 @@
781 c.Assert(c.GetTestLog(), jc.Contains, `WARNING juju.container unused config option: "shazam" -> "Captain Marvel"`)
782 }
783
784+func (s *LxcSuite) TestStopContainer(c *gc.C) {
785+ manager := s.makeManager(c, "test")
786+ instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
787+ err := containertesting.StopContainer(c, manager, instance)
788+ c.Assert(err, gc.IsNil)
789+}
790+
791+func (s *LxcSuite) TestStartStoppedContainer(c *gc.C) {
792+ manager := s.makeManager(c, "test")
793+ instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
794+ err := containertesting.StopContainer(c, manager, instance)
795+ c.Assert(err, gc.IsNil)
796+ err = containertesting.StartContainer(c, manager, instance)
797+ c.Assert(err, gc.IsNil)
798+}
799+
800+func (s *LxcSuite) TestStartRunningContainer(c *gc.C) {
801+ manager := s.makeManager(c, "test")
802+ instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
803+ err := containertesting.StartContainer(c, manager, instance)
804+ c.Assert(err, gc.ErrorMatches, "container is already running")
805+}
806+
807 func (s *LxcSuite) TestCreateContainer(c *gc.C) {
808 manager := s.makeManager(c, "test")
809 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
810
811=== modified file 'container/testing/common.go'
812--- container/testing/common.go 2014-03-19 20:54:52 +0000
813+++ container/testing/common.go 2014-03-31 22:42:50 +0000
814@@ -17,6 +17,14 @@
815 "launchpad.net/juju-core/version"
816 )
817
818+func StartContainer(c *gc.C, manager container.Manager, instance instance.Instance) error {
819+ return manager.StartContainer(instance)
820+}
821+
822+func StopContainer(c *gc.C, manager container.Manager, instance instance.Instance) error {
823+ return manager.StopContainer(instance)
824+}
825+
826 func CreateContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance {
827 stateInfo := jujutesting.FakeStateInfo(machineId)
828 apiInfo := jujutesting.FakeAPIInfo(machineId)
829
830=== modified file 'worker/provisioner/kvm-broker_test.go'
831--- worker/provisioner/kvm-broker_test.go 2014-03-14 02:19:35 +0000
832+++ worker/provisioner/kvm-broker_test.go 2014-03-31 22:42:50 +0000
833@@ -179,7 +179,7 @@
834 func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
835 s.State.StartSync()
836 event := <-s.events
837- c.Assert(event.Action, gc.Equals, mock.Started)
838+ c.Assert(event.Action, gc.Equals, mock.Created)
839 err := machine.Refresh()
840 c.Assert(err, gc.IsNil)
841 s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
842@@ -189,7 +189,7 @@
843 func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) {
844 s.State.StartSync()
845 event := <-s.events
846- c.Assert(event.Action, gc.Equals, mock.Stopped)
847+ c.Assert(event.Action, gc.Equals, mock.Destroyed)
848 c.Assert(event.InstanceId, gc.Equals, instId)
849 }
850

Subscribers

People subscribed via source and target branches

to status/vote changes: