Merge lp:~thumper/juju-core/lxc-container into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 1328
Proposed branch: lp:~thumper/juju-core/lxc-container
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 740 lines (+689/-2)
7 files modified
container/lxc/export_test.go (+19/-0)
container/lxc/instance.go (+46/-0)
container/lxc/lxc.go (+283/-0)
container/lxc/lxc_test.go (+167/-0)
container/lxc/mock-lxc_test.go (+147/-0)
environs/cloudinit/cloudinit.go (+2/-2)
testing/instance.go (+25/-0)
To merge this branch: bzr merge lp:~thumper/juju-core/lxc-container
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+169980@code.launchpad.net

Commit message

Implement lxc containers

This branch adds a containers interface, and an lxc
implementation of them. A Container is-a Instance, and
uses the interface embedding to copy across the Instance
methods.

At this stage, the containers do nothing special with
networking, and just use the default set up.

/var/log/juju is mounted from the host to the inside of
the container for ease of access to log files.

Some new checks have been added, along with some testing
functions for files.

https://codereview.appspot.com/10370044/

Description of the change

Implement lxc containers

This branch adds a containers interface, and an lxc
implementation of them. A Container is-a Instance, and
uses the interface embedding to copy across the Instance
methods.

At this stage, the containers do nothing special with
networking, and just use the default set up.

/var/log/juju is mounted from the host to the inside of
the container for ease of access to log files.

Some new checks have been added, along with some testing
functions for files.

https://codereview.appspot.com/10370044/

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

Reviewers: mp+169980_code.launchpad.net,

Message:
Please take a look.

Description:
Implement lxc containers

This branch adds a containers interface, and an lxc
implementation of them. A Container is-a Instance, and
uses the interface embedding to copy across the Instance
methods.

At this stage, the containers do nothing special with
networking, and just use the default set up.

/var/log/juju is mounted from the host to the inside of
the container for ease of access to log files.

Some new checks have been added, along with some testing
functions for files.

https://code.launchpad.net/~thumper/juju-core/lxc-container/+merge/169980

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   A container/container.go
   A container/lxc/export_test.go
   A container/lxc/lxc.go
   A container/lxc/lxc_test.go
   A container/lxc/mock-lxc_test.go
   A testing/checkers/bool.go
   A testing/checkers/relop.go
   A testing/file.go

Revision history for this message
Ian Booth (wallyworld) wrote :
Download full text (4.4 KiB)

Looks nice but I have some questions.

https://codereview.appspot.com/10370044/diff/1/container/container.go
File container/container.go (right):

https://codereview.appspot.com/10370044/diff/1/container/container.go#newcode21
container/container.go:21: apiInfo *api.Info) error
It could just be my warped thinking, but I'm conceptually having an
issue with a Create() method being on the Container itself. Shouldn't a
foo factory create a foo? And then once created, the container returned
by the factory is started/stopped etc.

If the above is valid, perhaps a comment explaining the method would
help. All exported methods would ideally be commented, especially ones
like this that represent the API contract.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode33
container/lxc/lxc.go:33: type ContainerFactory interface {
Exported interfaces/structs should have some comments for doc purposes

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode39
container/lxc/lxc.go:39: lxc golxc.ContainerFactory
I don't see the need for the lxc attribute here. I think the container
factory can just be embedded like so:

type lxcFactory struct {
     golxc.ContainerFactory
}

Then the code that uses it will be:

factory.New(...)

instead of factory.lxc.New(...)

and the former reads much nicer IMHO. The extra lxc bit just confuses
it.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode126
container/lxc/lxc.go:126: // create time seems to work fine.
Is there a bug for this behaviour? If so reference it here. If not, do
we know why it's expected behaviour? Can we put the definitive reason
instead of speculation?

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode162
container/lxc/lxc.go:162: func (lxc *lxcContainer) InternalLogDir()
string {
Does this need to be exported? I would think not.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode174
container/lxc/lxc.go:174: func (lxc *lxcContainer) WriteConfig()
(string, error) {
Does this need to be exported? I would think not.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode183
container/lxc/lxc.go:183: func (lxc *lxcContainer) WriteUserData(
Does this need to be exported? I would think not.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc_test.go
File container/lxc/lxc_test.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc_test.go#newcode38
container/lxc/lxc_test.go:38: var _ = Suite(&LxcSuite{})
Where are the tests for Start() and Stop()?

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc_test.go#newcode101
container/lxc/lxc_test.go:101: c.Assert(err, IsNil)
Is there anything more that we can check besides just err is nil? I'd
ideally like to see checks using the params passed to Create. Can we
test that the cloud init stuff/user data is correctly generated etc.
Otherwise I can't see where that stuff is tested.
Typically in this case, the pattern in use it to call the method
assertContainerCreate and put th...

Read more...

Revision history for this message
Frank Mueller (themue) wrote :

Some more comments plus what written in
https://codereview.appspot.com/10361045/ (had that one before).

https://codereview.appspot.com/10370044/diff/1/container/container.go
File container/container.go (right):

https://codereview.appspot.com/10370044/diff/1/container/container.go#newcode21
container/container.go:21: apiInfo *api.Info) error
On 2013/06/18 05:29:55, wallyworld wrote:
> It could just be my warped thinking, but I'm conceptually having an
issue with a
> Create() method being on the Container itself. Shouldn't a foo factory
create a
> foo? And then once created, the container returned by the factory is
> started/stopped etc.

> If the above is valid, perhaps a comment explaining the method would
help. All
> exported methods would ideally be commented, especially ones like this
that
> represent the API contract.

Yes, looks like a problem with the naming. I would like

- ContainerManager as the interface to create or open containers
- lxc.ContainerManager/kvm.ContainerManager implementing that interface
- Container representing container instances to start/stop/destroy them
(again an interface)
- lxc.Container/kvm.Container implementing that interface

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode33
container/lxc/lxc.go:33: type ContainerFactory interface {
On 2013/06/18 05:29:55, wallyworld wrote:
> Exported interfaces/structs should have some comments for doc purposes

+1

https://codereview.appspot.com/10370044/diff/1/testing/checkers/bool.go
File testing/checkers/bool.go (right):

https://codereview.appspot.com/10370044/diff/1/testing/checkers/bool.go#newcode16
testing/checkers/bool.go:16: var IsTrue Checker = &isTrueChecker{
On 2013/06/18 05:29:55, wallyworld wrote:
> I know we could do Not(IsTrue), but how much beer for IsFalse as well?

+1, but as already mentioned, extra CL for testing extensions please.

https://codereview.appspot.com/10370044/

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

https://codereview.appspot.com/10370044/diff/1/container/container.go
File container/container.go (right):

https://codereview.appspot.com/10370044/diff/1/container/container.go#newcode21
container/container.go:21: apiInfo *api.Info) error
On 2013/06/18 12:47:04, mue wrote:
> On 2013/06/18 05:29:55, wallyworld wrote:
> > It could just be my warped thinking, but I'm conceptually having an
issue with
> a
> > Create() method being on the Container itself. Shouldn't a foo
factory create
> a
> > foo? And then once created, the container returned by the factory is
> > started/stopped etc.
> >
> > If the above is valid, perhaps a comment explaining the method would
help. All
> > exported methods would ideally be commented, especially ones like
this that
> > represent the API contract.

> Yes, looks like a problem with the naming. I would like

> - ContainerManager as the interface to create or open containers
> - lxc.ContainerManager/kvm.ContainerManager implementing that
interface
> - Container representing container instances to start/stop/destroy
them (again
> an interface)
> - lxc.Container/kvm.Container implementing that interface

Actually, after talking with william, I think this entire interface is
going to die :-)

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode33
container/lxc/lxc.go:33: type ContainerFactory interface {
On 2013/06/18 12:47:04, mue wrote:
> On 2013/06/18 05:29:55, wallyworld wrote:
> > Exported interfaces/structs should have some comments for doc
purposes

> +1

Yeah, agreed. I have a feeling that this is going to change somewhat :)

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode39
container/lxc/lxc.go:39: lxc golxc.ContainerFactory
On 2013/06/18 05:29:55, wallyworld wrote:
> I don't see the need for the lxc attribute here. I think the container
factory
> can just be embedded like so:

> type lxcFactory struct {
> golxc.ContainerFactory
> }

> Then the code that uses it will be:

> factory.New(...)

> instead of factory.lxc.New(...)

> and the former reads much nicer IMHO. The extra lxc bit just confuses
it.

*nod*

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode126
container/lxc/lxc.go:126: // create time seems to work fine.
On 2013/06/18 05:29:55, wallyworld wrote:
> Is there a bug for this behaviour? If so reference it here. If not, do
we know
> why it's expected behaviour? Can we put the definitive reason instead
of
> speculation?

It is due to the definition of the rootfs, as serge mentioned in an
email. Not quite sure how to fix it, or if it is a real problem. But I
can extend the comment.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode162
container/lxc/lxc.go:162: func (lxc *lxcContainer) InternalLogDir()
string {
On 2013/06/18 05:29:55, wallyworld wrote:
> Does this need to be exported? I would think not.

It needs to be exported to test it. And since no-one should really have
access to it, it *shouldn't* matter.

https://codereview.appspot.com/10370044/diff/1/testing/checkers/bool.go
File te...

Read more...

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

On 2013/06/19 04:01:42, thumper wrote:
> Please take a look.

Now also contains changes from https://codereview.appspot.com/10395044/
as I couldn't create the dependent branch after it already exists (LP
can...)

https://codereview.appspot.com/10370044/

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

In summary, looking pretty good but I have a couple of concerns.

https://codereview.appspot.com/10370044/diff/12001/container/lxc/instance.go
File container/lxc/instance.go (right):

https://codereview.appspot.com/10370044/diff/12001/container/lxc/instance.go#newcode51
container/lxc/instance.go:51: }
trunk now also defines a Metadata() method but this is being removed
asap. But for now, lxcInstance does not implement Instance

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode56
container/lxc/lxc.go:56: // NewContainerManager returns an manager
object that can start and stop lxc
an manager -> a manager

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode130
container/lxc/lxc.go:130: container := manager.New(name)
This confuses me. Can we change this to
container := manager.Get(name)

The above is much more aligned with expected semantics of how New() and
Get() would behave. The reader would see the above and reasonably expect
a new container instance to be created which doesn't make sense.

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode234
container/lxc/lxc.go:234: MachineContainerType: "lxc",
can we use the ContainerType const LXC here please

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc_test.go
File container/lxc/lxc_test.go (right):

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc_test.go#newcode86
container/lxc/lxc_test.go:86: testing.AssertNonEmptyFileExists(c,
filepath.Join(s.containerDir, name, "cloud-init"))
I do think we need to check the contents of the cloud init file like we
do in other cloud init related tests

https://codereview.appspot.com/10370044/

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

On 2013/06/20 02:01:23, wallyworld wrote:
> In summary, looking pretty good but I have a couple of concerns.

https://codereview.appspot.com/10370044/diff/12001/container/lxc/instance.go
> File container/lxc/instance.go (right):

https://codereview.appspot.com/10370044/diff/12001/container/lxc/instance.go#newcode51
> container/lxc/instance.go:51: }
> trunk now also defines a Metadata() method but this is being removed
asap. But
> for now, lxcInstance does not implement Instance

Why is it being removed? And what is replacing it?

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go
> File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode56
> container/lxc/lxc.go:56: // NewContainerManager returns an manager
object that
> can start and stop lxc
> an manager -> a manager

Fixed.

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode130
> container/lxc/lxc.go:130: container := manager.New(name)
> This confuses me. Can we change this to
> container := manager.Get(name)

This is in golxc and out of the scope of this branch. The go idiom is
that New will
return you a well defined object, and that it does. It does not however
actually
create you an lxc container.

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode234
> container/lxc/lxc.go:234: MachineContainerType: "lxc",
> can we use the ContainerType const LXC here please

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc_test.go
> File container/lxc/lxc_test.go (right):

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc_test.go#newcode86
> container/lxc/lxc_test.go:86: testing.AssertNonEmptyFileExists(c,
> filepath.Join(s.containerDir, name, "cloud-init"))
> I do think we need to check the contents of the cloud init file like
we do in
> other cloud init related tests

The contents of cloud-init is covered by the cloud-init tests. I didn't
feel any
need to do additional checks here over and above the actual generation
of the file.

This isn't really a cloud-init test, just a test that a file is written.

https://codereview.appspot.com/10370044/

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

On 2013/06/20 22:20:24, thumper wrote:
> On 2013/06/20 02:01:23, wallyworld wrote:
> > In summary, looking pretty good but I have a couple of concerns.
> >
> >
https://codereview.appspot.com/10370044/diff/12001/container/lxc/instance.go
> > File container/lxc/instance.go (right):
> >
> >

https://codereview.appspot.com/10370044/diff/12001/container/lxc/instance.go#newcode51
> > container/lxc/instance.go:51: }
> > trunk now also defines a Metadata() method but this is being removed
asap. But
> > for now, lxcInstance does not implement Instance

> Why is it being removed? And what is replacing it?

William didn't like it because it didn't always provide access to the
metadata. For Instance objects returned from startInstance, the metadata
is available (and this is the case where the metadata is actually
required). However, Instance objects returned from querying the running
instances eg via AllInstances() did not have the metadata available
since an extra RPC call for each distinct instancetype/flavour would be
required. And since the metadata is not needed here, I deferred having
to maike the API calls. But William didn't like the inconsistency. So
what is happening instead is that startInstance returns the Instance and
Metadata as separate result objects.

> >

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode130
> > container/lxc/lxc.go:130: container := manager.New(name)
> > This confuses me. Can we change this to
> > container := manager.Get(name)

> This is in golxc and out of the scope of this branch. The go idiom is
that New
> will
> return you a well defined object, and that it does. It does not
however
> actually
> create you an lxc container.

Which is very, very, very unintuitive :-(
Can we at least add a comment so the next person reading the code
understands what is going on.

> >

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc.go#newcode234
> > container/lxc/lxc.go:234: MachineContainerType: "lxc",
> > can we use the ContainerType const LXC here please
> >
> >
https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc_test.go
> > File container/lxc/lxc_test.go (right):
> >
> >

https://codereview.appspot.com/10370044/diff/12001/container/lxc/lxc_test.go#newcode86
> > container/lxc/lxc_test.go:86: testing.AssertNonEmptyFileExists(c,
> > filepath.Join(s.containerDir, name, "cloud-init"))
> > I do think we need to check the contents of the cloud init file like
we do in
> > other cloud init related tests

> The contents of cloud-init is covered by the cloud-init tests. I
didn't feel any
> need to do additional checks here over and above the actual generation
of the
> file.

> This isn't really a cloud-init test, just a test that a file is
written.

Understood. But there's a big assumption that the correct calls to the
cloud init apis have been made. What's to say that an empty or corrupt
file hasn't been written. Can we just add a sanity check that a key line
or two exists in the file contents to ensure the correct parameters have
been passed to cloudinit.MachineConfig, environs.FinishMachineConfig
etc?

https://codereview.appspot.com/10370044/

Revision history for this message
Roger Peppe (rogpeppe) wrote :
Download full text (5.1 KiB)

looks good, with some trivial issues and one significant concern around
the uniqueDirectory code.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode39
container/lxc/lxc.go:39: lxc golxc.ContainerFactory
On 2013/06/18 23:50:16, thumper wrote:
> On 2013/06/18 05:29:55, wallyworld wrote:
> > I don't see the need for the lxc attribute here. I think the
container factory
> > can just be embedded like so:
> >
> > type lxcFactory struct {
> > golxc.ContainerFactory
> > }
> >
> > Then the code that uses it will be:
> >
> > factory.New(...)
> >
> > instead of factory.lxc.New(...)
> >
> > and the former reads much nicer IMHO. The extra lxc bit just
confuses it.

> *nod*

depends if you actually want to export New as a method on the returned
ContainerFactory. i'd suggest you probably don't, and that using a field
to hide it is actually appropriate, but given that it can only be got to
by dynamic type conversion, it probably doesn't matter *that* much.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode73
container/lxc/lxc.go:73: apiInfo *api.Info,
this has enough arguments that i might consider making it take a struct
argument.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode149
container/lxc/lxc.go:149: if err := os.Rename(lxc.Directory(),
removedDir); err != nil {
can the Destroy method be called concurrently?

if so, i think you should try the rename each time
you come up with a new name, otherwise someone else
might be racing for the same name.

alternatively just use ioutil.TempDir and move into a known
name inside a directory created with that.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode162
container/lxc/lxc.go:162: func (lxc *lxcContainer) InternalLogDir()
string {
On 2013/06/18 23:50:16, thumper wrote:
> On 2013/06/18 05:29:55, wallyworld wrote:
> > Does this need to be exported? I would think not.

> It needs to be exported to test it. And since no-one should really
have access
> to it, it *shouldn't* matter.

you could always define it in export_test.go. i think it's nice
to export only those methods that actually need exporting, otherwise
the reader is left wondering whether there's a particular reason
for exporting them. ditto below, and +1 to wallyworld's queries.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode233
container/lxc/lxc.go:233: // Id returns a provider-generated identifier
for the Instance.
If the following methods are exported in order to satisfy an interface,
I'd suggest simply documenting as

// Id implements sompackage.SomeInterface.Id.

(potentially with extra comments that are specific to
this particular implementation). The method documentation
can be left to the interface definition, where it can
be seen with godoc.

If they're only exported for tests, I'd suggest exporting
them only in export_test.go.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode280
container/lxc/lxc.go:280: _, err := os.Stat(dir)
i'm concerned that if there's some filesystem
...

Read more...

Revision history for this message
William Reade (fwereade) wrote :
Download full text (4.3 KiB)

I still think it's a little bit smelly to be mixing Instance methods
into the lxc Container type, but I'm ok to explore this direction and
see how it works out. LGTM with the various other comments addressed,
acknowledged, or explained away, but please check with the other
reviewers before you land it.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode73
container/lxc/lxc.go:73: apiInfo *api.Info,
On 2013/06/21 07:13:52, rog wrote:
> this has enough arguments that i might consider making it take a
struct
> argument.

I think there's a little bit of settling still to do here. There's
definitely some sort of common params struct that'll be useful when
starting instances of whatever stripe... consider something similar to
MachineConfig, perhaps?

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode126
container/lxc/lxc.go:126: // create time seems to work fine.
On 2013/06/18 23:50:16, thumper wrote:
> On 2013/06/18 05:29:55, wallyworld wrote:
> > Is there a bug for this behaviour? If so reference it here. If not,
do we know
> > why it's expected behaviour? Can we put the definitive reason
instead of
> > speculation?

> It is due to the definition of the rootfs, as serge mentioned in an
email. Not
> quite sure how to fix it, or if it is a real problem. But I can
extend the
> comment.

+1

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode149
container/lxc/lxc.go:149: if err := os.Rename(lxc.Directory(),
removedDir); err != nil {
On 2013/06/21 07:13:52, rog wrote:
> can the Destroy method be called concurrently?

> if so, i think you should try the rename each time
> you come up with a new name, otherwise someone else
> might be racing for the same name.

> alternatively just use ioutil.TempDir and move into a known
> name inside a directory created with that.

In theory, I think, it can't be; but, well, probably one day it should
be possible to remove N containers in parallel if they're all unwanted.
Seems smart to consider the concurrent case today.

https://codereview.appspot.com/10370044/diff/21001/container/lxc/export_test.go
File container/lxc/export_test.go (right):

https://codereview.appspot.com/10370044/diff/21001/container/lxc/export_test.go#newcode8
container/lxc/export_test.go:8: containerDir = dir
fwiw, I find `x,y = y, z` quite readable in these situations. YMMV ofc

https://codereview.appspot.com/10370044/diff/21001/container/lxc/instance.go
File container/lxc/instance.go (right):

https://codereview.appspot.com/10370044/diff/21001/container/lxc/instance.go#newcode14
container/lxc/instance.go:14: }
var _ Instance = (*lxcInstance)(nil)?

https://codereview.appspot.com/10370044/diff/21001/container/lxc/instance.go#newcode16
container/lxc/instance.go:16: // Id returns a provider-generated
identifier for the Instance.
On 2013/06/21 07:13:52, rog wrote:
> // Id implements instance.Instance.Id.

> and etc below.

+1

https://codereview.appspot.com/10370044/diff/21001/container/lxc/instance.go#newcode36
container/lxc/instance.go:36: func (lxc *lxcInstance)
OpenP...

Read more...

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

Please take a look.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go
File container/lxc/lxc.go (right):

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode73
container/lxc/lxc.go:73: apiInfo *api.Info,
On 2013/06/21 10:01:00, fwereade wrote:
> On 2013/06/21 07:13:52, rog wrote:
> > this has enough arguments that i might consider making it take a
struct
> > argument.

> I think there's a little bit of settling still to do here. There's
definitely
> some sort of common params struct that'll be useful when starting
instances of
> whatever stripe... consider something similar to MachineConfig,
perhaps?

I'd rather wait and let the dust settle first.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode149
container/lxc/lxc.go:149: if err := os.Rename(lxc.Directory(),
removedDir); err != nil {
On 2013/06/21 10:01:00, fwereade wrote:
> On 2013/06/21 07:13:52, rog wrote:
> > can the Destroy method be called concurrently?
> >
> > if so, i think you should try the rename each time
> > you come up with a new name, otherwise someone else
> > might be racing for the same name.
> >
> > alternatively just use ioutil.TempDir and move into a known
> > name inside a directory created with that.

> In theory, I think, it can't be; but, well, probably one day it should
be
> possible to remove N containers in parallel if they're all unwanted.
Seems smart
> to consider the concurrent case today.

Even if we were going to remove N containers in parallel, the name is
still unique for any running container. The reason we have the .n
suffix is for when we had container 0, then destroyed it, then created
another then destroyed that one.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode162
container/lxc/lxc.go:162: func (lxc *lxcContainer) InternalLogDir()
string {
On 2013/06/21 07:13:52, rog wrote:
> On 2013/06/18 23:50:16, thumper wrote:
> > On 2013/06/18 05:29:55, wallyworld wrote:
> > > Does this need to be exported? I would think not.
> >
> > It needs to be exported to test it. And since no-one should really
have
> access
> > to it, it *shouldn't* matter.

> you could always define it in export_test.go. i think it's nice
> to export only those methods that actually need exporting, otherwise
> the reader is left wondering whether there's a particular reason
> for exporting them. ditto below, and +1 to wallyworld's queries.

understood, and not needed in this case.

https://codereview.appspot.com/10370044/diff/1/container/lxc/lxc.go#newcode233
container/lxc/lxc.go:233: // Id returns a provider-generated identifier
for the Instance.
On 2013/06/21 07:13:52, rog wrote:
> If the following methods are exported in order to satisfy an
interface, I'd
> suggest simply documenting as

> // Id implements sompackage.SomeInterface.Id.

> (potentially with extra comments that are specific to
> this particular implementation). The method documentation
> can be left to the interface definition, where it can
> be seen with godoc.

> If they're only exported for tests, I'd suggest exporting
> them only in export_test.go.

These were moved to a different file in a later change set attache...

Read more...

Revision history for this message
Ian Booth (wallyworld) wrote :
Revision history for this message
Go Bot (go-bot) wrote :

The attempt to merge lp:~thumper/juju-core/lxc-container into lp:juju-core failed. Below is the output from the failed tests.

container/lxc/lxc.go:13:2: import "launchpad.net/golxc": cannot find package

Revision history for this message
John A Meinel (jameinel) wrote :

I added 'launchpad.nett/golxc' on the bot and re-approved this.

Revision history for this message
Go Bot (go-bot) wrote :

The attempt to merge lp:~thumper/juju-core/lxc-container into lp:juju-core failed. Below is the output from the failed tests.

# launchpad.net/juju-core/container/lxc
container/lxc/instance.go:49: undefined: instance.Metadata

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'container'
2=== added directory 'container/lxc'
3=== added file 'container/lxc/export_test.go'
4--- container/lxc/export_test.go 1970-01-01 00:00:00 +0000
5+++ container/lxc/export_test.go 2013-06-25 21:33:28 +0000
6@@ -0,0 +1,19 @@
7+// Copyright 2013 Canonical Ltd.
8+// Licensed under the AGPLv3, see LICENCE file for details.
9+
10+package lxc
11+
12+func SetContainerDir(dir string) (old string) {
13+ old, containerDir = containerDir, dir
14+ return
15+}
16+
17+func SetLxcContainerDir(dir string) (old string) {
18+ old, lxcContainerDir = lxcContainerDir, dir
19+ return
20+}
21+
22+func SetRemovedContainerDir(dir string) (old string) {
23+ old, removedContainerDir = removedContainerDir, dir
24+ return
25+}
26
27=== added file 'container/lxc/instance.go'
28--- container/lxc/instance.go 1970-01-01 00:00:00 +0000
29+++ container/lxc/instance.go 2013-06-25 21:33:28 +0000
30@@ -0,0 +1,46 @@
31+// Copyright 2013 Canonical Ltd.
32+// Licensed under the AGPLv3, see LICENCE file for details.
33+
34+package lxc
35+
36+import (
37+ "fmt"
38+
39+ "launchpad.net/juju-core/instance"
40+)
41+
42+type lxcInstance struct {
43+ id string
44+}
45+
46+var _ instance.Instance = (*lxcInstance)(nil)
47+
48+// Id implements instance.Instance.Id.
49+func (lxc *lxcInstance) Id() instance.Id {
50+ return instance.Id(lxc.id)
51+}
52+
53+// DNSName implements instance.Instance.DNSName.
54+func (lxc *lxcInstance) DNSName() (string, error) {
55+ return "", instance.ErrNoDNSName
56+}
57+
58+// WaitDNSName implements instance.Instance.WaitDNSName.
59+func (lxc *lxcInstance) WaitDNSName() (string, error) {
60+ return "", instance.ErrNoDNSName
61+}
62+
63+// OpenPorts implements instance.Instance.OpenPorts.
64+func (lxc *lxcInstance) OpenPorts(machineId string, ports []instance.Port) error {
65+ return fmt.Errorf("not implemented")
66+}
67+
68+// ClosePorts implements instance.Instance.ClosePorts.
69+func (lxc *lxcInstance) ClosePorts(machineId string, ports []instance.Port) error {
70+ return fmt.Errorf("not implemented")
71+}
72+
73+// Ports implements instance.Instance.Ports.
74+func (lxc *lxcInstance) Ports(machineId string) ([]instance.Port, error) {
75+ return nil, fmt.Errorf("not implemented")
76+}
77
78=== added file 'container/lxc/lxc.go'
79--- container/lxc/lxc.go 1970-01-01 00:00:00 +0000
80+++ container/lxc/lxc.go 2013-06-25 21:33:28 +0000
81@@ -0,0 +1,283 @@
82+// Copyright 2013 Canonical Ltd.
83+// Licensed under the AGPLv3, see LICENCE file for details.
84+
85+package lxc
86+
87+import (
88+ "fmt"
89+ "io/ioutil"
90+ "os"
91+ "path/filepath"
92+ "strings"
93+
94+ "launchpad.net/golxc"
95+ "launchpad.net/juju-core/constraints"
96+ "launchpad.net/juju-core/environs"
97+ "launchpad.net/juju-core/environs/cloudinit"
98+ "launchpad.net/juju-core/environs/config"
99+ "launchpad.net/juju-core/instance"
100+ "launchpad.net/juju-core/state"
101+ "launchpad.net/juju-core/state/api"
102+ "launchpad.net/loggo"
103+)
104+
105+var logger = loggo.GetLogger("juju.container.lxc")
106+
107+var (
108+ defaultTemplate = "ubuntu-cloud"
109+ containerDir = "/var/lib/juju/containers"
110+ removedContainerDir = "/var/lib/juju/removed-containers"
111+ lxcContainerDir = "/var/lib/lxc"
112+)
113+
114+// ContainerManager is responsible for starting containers, and stopping and
115+// listing containers that it has started. The name of the manager is used to
116+// namespace the lxc containers on the machine.
117+type ContainerManager interface {
118+ // StartContainer creates and starts a new lxc container for the specified machine.
119+ StartContainer(
120+ machineId, series, nonce string,
121+ tools *state.Tools,
122+ environConfig *config.Config,
123+ stateInfo *state.Info,
124+ apiInfo *api.Info) (instance.Instance, error)
125+ // StopContainer stops and destroyes the lxc container identified by Instance.
126+ StopContainer(instance.Instance) error
127+ // ListContainers return a list of containers that have been started by
128+ // this manager.
129+ ListContainers() ([]instance.Instance, error)
130+}
131+
132+type containerManager struct {
133+ lxcObjectFactory golxc.ContainerFactory
134+ name string
135+}
136+
137+// NewContainerManager returns a manager object that can start and stop lxc
138+// containers. The containers that are created are namespaced by the name
139+// parameter.
140+func NewContainerManager(factory golxc.ContainerFactory, name string) ContainerManager {
141+ return &containerManager{factory, name}
142+}
143+
144+func (manager *containerManager) StartContainer(
145+ machineId, series, nonce string,
146+ tools *state.Tools,
147+ environConfig *config.Config,
148+ stateInfo *state.Info,
149+ apiInfo *api.Info) (instance.Instance, error) {
150+
151+ name := state.MachineTag(machineId)
152+ if manager.name != "" {
153+ name = fmt.Sprintf("%s-%s", manager.name, name)
154+ }
155+ // Note here that the lxcObjectFacotry only returns a valid container
156+ // object, and doesn't actually construct the underlying lxc container on
157+ // disk.
158+ container := manager.lxcObjectFactory.New(name)
159+
160+ // Create the cloud-init.
161+ directory := jujuContainerDirectory(name)
162+ logger.Tracef("create directory: %s", directory)
163+ if err := os.MkdirAll(directory, 0755); err != nil {
164+ logger.Errorf("failed to create container directory: %v", err)
165+ return nil, err
166+ }
167+ logger.Tracef("write cloud-init")
168+ userDataFilename, err := writeUserData(directory, machineId, nonce, tools, environConfig, stateInfo, apiInfo)
169+ if err != nil {
170+ logger.Errorf("failed to write user data: %v", err)
171+ return nil, err
172+ }
173+ logger.Tracef("write the lxc.conf file")
174+ configFile, err := writeLxcConfig(directory)
175+ if err != nil {
176+ logger.Errorf("failed to write config file: %v", err)
177+ return nil, err
178+ }
179+ templateParams := []string{
180+ "--debug", // Debug errors in the cloud image
181+ "--userdata", userDataFilename, // Our groovey cloud-init
182+ "--hostid", name, // Use the container name as the hostid
183+ "-r", series,
184+ }
185+ // Create the container.
186+ logger.Tracef("create the container")
187+ if err := container.Create(configFile, defaultTemplate, templateParams...); err != nil {
188+ logger.Errorf("lxc container creation failed: %v", err)
189+ return nil, err
190+ }
191+ // Make sure that the mount dir has been created.
192+ logger.Tracef("make the mount dir for the shard logs")
193+ if err := os.MkdirAll(internalLogDir(name), 0755); err != nil {
194+ logger.Errorf("failed to create internal /var/log/juju mount dir: %v", err)
195+ return nil, err
196+ }
197+ logger.Tracef("lxc container created")
198+
199+ // Start the lxc container with the appropriate settings for grabbing the
200+ // console output and a log file.
201+ consoleFile := filepath.Join(directory, "console.log")
202+ container.SetLogFile(filepath.Join(directory, "container.log"), golxc.LogDebug)
203+ logger.Tracef("start the container")
204+ // We explicitly don't pass through the config file to the container.Start
205+ // method as we have passed it through at container creation time. This
206+ // is necessary to get the appropriate rootfs reference without explicitly
207+ // setting it ourselves.
208+ if err = container.Start("", consoleFile); err != nil {
209+ logger.Errorf("container failed to start: %v", err)
210+ return nil, err
211+ }
212+ logger.Tracef("container started")
213+ return &lxcInstance{name}, nil
214+}
215+
216+func (manager *containerManager) StopContainer(instance instance.Instance) error {
217+ name := string(instance.Id())
218+ container := manager.lxcObjectFactory.New(name)
219+ if err := container.Stop(); err != nil {
220+ logger.Errorf("failed to stop lxc container: %v", err)
221+ return err
222+ }
223+ if err := container.Destroy(); err != nil {
224+ logger.Errorf("failed to destroy lxc container: %v", err)
225+ return err
226+ }
227+ // Move the directory.
228+ logger.Tracef("create old container dir: %s", removedContainerDir)
229+ if err := os.MkdirAll(removedContainerDir, 0755); err != nil {
230+ logger.Errorf("failed to create removed container directory: %v", err)
231+ return err
232+ }
233+ removedDir, err := uniqueDirectory(removedContainerDir, name)
234+ if err != nil {
235+ logger.Errorf("was not able to generate a unique directory: %v", err)
236+ return err
237+ }
238+ if err := os.Rename(jujuContainerDirectory(name), removedDir); err != nil {
239+ logger.Errorf("failed to rename container directory: %v", err)
240+ return err
241+ }
242+ return nil
243+}
244+
245+func (manager *containerManager) ListContainers() (result []instance.Instance, err error) {
246+ containers, err := manager.lxcObjectFactory.List()
247+ if err != nil {
248+ logger.Errorf("failed getting all instances: %v", err)
249+ return
250+ }
251+ managerPrefix := ""
252+ if manager.name != "" {
253+ managerPrefix = fmt.Sprintf("%s-", manager.name)
254+ }
255+
256+ for _, container := range containers {
257+ // Filter out those not starting with our name.
258+ name := container.Name()
259+ if !strings.HasPrefix(name, managerPrefix) {
260+ continue
261+ }
262+ if container.IsRunning() {
263+ result = append(result, &lxcInstance{name})
264+ }
265+ }
266+ return
267+}
268+
269+func jujuContainerDirectory(containerName string) string {
270+ return filepath.Join(containerDir, containerName)
271+}
272+
273+const internalLogDirTemplate = "%s/%s/rootfs/var/log/juju"
274+
275+func internalLogDir(containerName string) string {
276+ return fmt.Sprintf(internalLogDirTemplate, lxcContainerDir, containerName)
277+}
278+
279+const localConfig = `
280+lxc.network.type = veth
281+lxc.network.link = lxcbr0
282+lxc.network.flags = up
283+
284+lxc.mount.entry=/var/log/juju var/log/juju none defaults,bind 0 0
285+`
286+
287+func writeLxcConfig(directory string) (string, error) {
288+ // TODO(thumper): support different network settings.
289+ configFilename := filepath.Join(directory, "lxc.conf")
290+ if err := ioutil.WriteFile(configFilename, []byte(localConfig), 0644); err != nil {
291+ return "", err
292+ }
293+ return configFilename, nil
294+}
295+
296+func writeUserData(
297+ directory, machineId, nonce string,
298+ tools *state.Tools,
299+ environConfig *config.Config,
300+ stateInfo *state.Info,
301+ apiInfo *api.Info,
302+) (string, error) {
303+ userData, err := cloudInitUserData(machineId, nonce, tools, environConfig, stateInfo, apiInfo)
304+ if err != nil {
305+ logger.Errorf("failed to create user data: %v", err)
306+ return "", err
307+ }
308+ userDataFilename := filepath.Join(directory, "cloud-init")
309+ if err := ioutil.WriteFile(userDataFilename, userData, 0644); err != nil {
310+ logger.Errorf("failed to write user data: %v", err)
311+ return "", err
312+ }
313+ return userDataFilename, nil
314+}
315+
316+func cloudInitUserData(
317+ machineId, nonce string,
318+ tools *state.Tools,
319+ environConfig *config.Config,
320+ stateInfo *state.Info,
321+ apiInfo *api.Info,
322+) ([]byte, error) {
323+ machineConfig := &cloudinit.MachineConfig{
324+ MachineId: machineId,
325+ MachineNonce: nonce,
326+ MachineContainerType: state.LXC,
327+ StateInfo: stateInfo,
328+ APIInfo: apiInfo,
329+ DataDir: "/var/lib/juju",
330+ Tools: tools,
331+ }
332+ if err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{}); err != nil {
333+ return nil, err
334+ }
335+ cloudConfig, err := cloudinit.New(machineConfig)
336+ if err != nil {
337+ return nil, err
338+ }
339+ data, err := cloudConfig.Render()
340+ if err != nil {
341+ return nil, err
342+ }
343+ return data, nil
344+}
345+
346+// uniqueDirectory returns "path/name" if that directory doesn't exist. If it
347+// does, the method starts appending .1, .2, etc until a unique name is found.
348+func uniqueDirectory(path, name string) (string, error) {
349+ dir := filepath.Join(path, name)
350+ _, err := os.Stat(dir)
351+ if os.IsNotExist(err) {
352+ return dir, nil
353+ }
354+ for i := 1; ; i++ {
355+ dir := filepath.Join(path, fmt.Sprintf("%s.%d", name, i))
356+ _, err := os.Stat(dir)
357+ if os.IsNotExist(err) {
358+ return dir, nil
359+ } else if err != nil {
360+ return "", err
361+ }
362+ }
363+ panic("unreachable")
364+}
365
366=== added file 'container/lxc/lxc_test.go'
367--- container/lxc/lxc_test.go 1970-01-01 00:00:00 +0000
368+++ container/lxc/lxc_test.go 2013-06-25 21:33:28 +0000
369@@ -0,0 +1,167 @@
370+// Copyright 2013 Canonical Ltd.
371+// Licensed under the AGPLv3, see LICENCE file for details.
372+
373+package lxc_test
374+
375+import (
376+ "fmt"
377+ "io/ioutil"
378+ "os"
379+ "path/filepath"
380+ stdtesting "testing"
381+
382+ . "launchpad.net/gocheck"
383+ "launchpad.net/goyaml"
384+ "launchpad.net/juju-core/container/lxc"
385+ "launchpad.net/juju-core/instance"
386+ jujutesting "launchpad.net/juju-core/juju/testing"
387+ "launchpad.net/juju-core/state"
388+ "launchpad.net/juju-core/testing"
389+ . "launchpad.net/juju-core/testing/checkers"
390+ "launchpad.net/juju-core/version"
391+)
392+
393+func Test(t *stdtesting.T) {
394+ TestingT(t)
395+}
396+
397+type LxcSuite struct {
398+ testing.LoggingSuite
399+ containerDir string
400+ removedDir string
401+ lxcDir string
402+ oldContainerDir string
403+ oldRemovedDir string
404+ oldLxcContainerDir string
405+}
406+
407+var _ = Suite(&LxcSuite{})
408+
409+func (s *LxcSuite) SetUpSuite(c *C) {
410+ s.LoggingSuite.SetUpSuite(c)
411+}
412+
413+func (s *LxcSuite) TearDownSuite(c *C) {
414+ s.LoggingSuite.TearDownSuite(c)
415+}
416+
417+func (s *LxcSuite) SetUpTest(c *C) {
418+ s.LoggingSuite.SetUpTest(c)
419+ s.containerDir = c.MkDir()
420+ s.oldContainerDir = lxc.SetContainerDir(s.containerDir)
421+ s.removedDir = c.MkDir()
422+ s.oldRemovedDir = lxc.SetRemovedContainerDir(s.removedDir)
423+ s.lxcDir = c.MkDir()
424+ s.oldLxcContainerDir = lxc.SetLxcContainerDir(s.lxcDir)
425+}
426+
427+func (s *LxcSuite) TearDownTest(c *C) {
428+ lxc.SetContainerDir(s.oldContainerDir)
429+ lxc.SetLxcContainerDir(s.oldLxcContainerDir)
430+ lxc.SetRemovedContainerDir(s.oldRemovedDir)
431+ s.LoggingSuite.TearDownTest(c)
432+}
433+
434+func StartContainer(c *C, manager lxc.ContainerManager, machineId string) instance.Instance {
435+ config := testing.EnvironConfig(c)
436+ stateInfo := jujutesting.FakeStateInfo(machineId)
437+ apiInfo := jujutesting.FakeAPIInfo(machineId)
438+
439+ series := "series"
440+ nonce := "fake-nonce"
441+ tools := &state.Tools{
442+ Binary: version.MustParseBinary("2.3.4-foo-bar"),
443+ URL: "http://tools.example.com/2.3.4-foo-bar.tgz",
444+ }
445+
446+ inst, err := manager.StartContainer(machineId, series, nonce, tools, config, stateInfo, apiInfo)
447+ c.Assert(err, IsNil)
448+ return inst
449+}
450+
451+func (s *LxcSuite) TestStartContainer(c *C) {
452+ manager := lxc.NewContainerManager(MockFactory(), "")
453+ instance := StartContainer(c, manager, "1/lxc/0")
454+
455+ name := string(instance.Id())
456+ // Check our container config files.
457+ c.Assert(filepath.Join(s.containerDir, name, "lxc.conf"), IsNonEmptyFile)
458+ cloudInitFilename := filepath.Join(s.containerDir, name, "cloud-init")
459+ c.Assert(cloudInitFilename, IsNonEmptyFile)
460+ data, err := ioutil.ReadFile(cloudInitFilename)
461+ c.Assert(err, IsNil)
462+ c.Assert(string(data), HasPrefix, "#cloud-config\n")
463+
464+ x := make(map[interface{}]interface{})
465+ err = goyaml.Unmarshal(data, &x)
466+ c.Assert(err, IsNil)
467+
468+ var scripts []string
469+ for _, s := range x["runcmd"].([]interface{}) {
470+ scripts = append(scripts, s.(string))
471+ }
472+
473+ c.Assert(scripts[len(scripts)-1], Equals, "start jujud-machine-1-lxc-0")
474+
475+ // Check the mount point has been created inside the container.
476+ c.Assert(filepath.Join(s.lxcDir, name, "rootfs/var/log/juju"), IsDirectory)
477+}
478+
479+func (s *LxcSuite) TestStopContainer(c *C) {
480+ manager := lxc.NewContainerManager(MockFactory(), "")
481+ instance := StartContainer(c, manager, "1/lxc/0")
482+
483+ err := manager.StopContainer(instance)
484+ c.Assert(err, IsNil)
485+
486+ name := string(instance.Id())
487+ // Check that the container dir is no longer in the container dir
488+ c.Assert(filepath.Join(s.containerDir, name), DoesNotExist)
489+ // but instead, in the removed container dir
490+ c.Assert(filepath.Join(s.removedDir, name), IsDirectory)
491+}
492+
493+func (s *LxcSuite) TestStopContainerNameClash(c *C) {
494+ manager := lxc.NewContainerManager(MockFactory(), "")
495+ instance := StartContainer(c, manager, "1/lxc/0")
496+
497+ name := string(instance.Id())
498+ targetDir := filepath.Join(s.removedDir, name)
499+ err := os.MkdirAll(targetDir, 0755)
500+ c.Assert(err, IsNil)
501+
502+ err = manager.StopContainer(instance)
503+ c.Assert(err, IsNil)
504+
505+ // Check that the container dir is no longer in the container dir
506+ c.Assert(filepath.Join(s.containerDir, name), DoesNotExist)
507+ // but instead, in the removed container dir with a ".1" suffix as there was already a directory there.
508+ c.Assert(filepath.Join(s.removedDir, fmt.Sprintf("%s.1", name)), IsDirectory)
509+}
510+
511+func (s *LxcSuite) TestNamedManagerPrefix(c *C) {
512+ manager := lxc.NewContainerManager(MockFactory(), "eric")
513+ instance := StartContainer(c, manager, "1/lxc/0")
514+ c.Assert(string(instance.Id()), Equals, "eric-machine-1-lxc-0")
515+}
516+
517+func (s *LxcSuite) TestListContainers(c *C) {
518+ factory := MockFactory()
519+ foo := lxc.NewContainerManager(factory, "foo")
520+ bar := lxc.NewContainerManager(factory, "bar")
521+
522+ foo1 := StartContainer(c, foo, "1/lxc/0")
523+ foo2 := StartContainer(c, foo, "1/lxc/1")
524+ foo3 := StartContainer(c, foo, "1/lxc/2")
525+
526+ bar1 := StartContainer(c, bar, "1/lxc/0")
527+ bar2 := StartContainer(c, bar, "1/lxc/1")
528+
529+ result, err := foo.ListContainers()
530+ c.Assert(err, IsNil)
531+ testing.MatchInstances(c, result, foo1, foo2, foo3)
532+
533+ result, err = bar.ListContainers()
534+ c.Assert(err, IsNil)
535+ testing.MatchInstances(c, result, bar1, bar2)
536+}
537
538=== added file 'container/lxc/mock-lxc_test.go'
539--- container/lxc/mock-lxc_test.go 1970-01-01 00:00:00 +0000
540+++ container/lxc/mock-lxc_test.go 2013-06-25 21:33:28 +0000
541@@ -0,0 +1,147 @@
542+// Copyright 2013 Canonical Ltd.
543+// Licensed under the AGPLv3, see LICENCE file for details.
544+
545+package lxc_test
546+
547+import (
548+ "fmt"
549+
550+ "launchpad.net/golxc"
551+)
552+
553+// This file provides a mock implementation of the golxc interfaces
554+// ContainerFactory and Container.
555+
556+type mockFactory struct {
557+ instances map[string]golxc.Container
558+}
559+
560+func MockFactory() golxc.ContainerFactory {
561+ return &mockFactory{make(map[string]golxc.Container)}
562+}
563+
564+type mockContainer struct {
565+ factory *mockFactory
566+ name string
567+ state golxc.State
568+ logFile string
569+ logLevel golxc.LogLevel
570+}
571+
572+// Name returns the name of the container.
573+func (mock *mockContainer) Name() string {
574+ return mock.name
575+}
576+
577+// Create creates a new container based on the given template.
578+func (mock *mockContainer) Create(configFile, template string, templateArgs ...string) error {
579+ mock.state = golxc.StateStopped
580+ mock.factory.instances[mock.name] = mock
581+ return nil
582+}
583+
584+// Start runs the container as a daemon.
585+func (mock *mockContainer) Start(configFile, consoleFile string) error {
586+ mock.state = golxc.StateRunning
587+ return nil
588+}
589+
590+// Stop terminates the running container.
591+func (mock *mockContainer) Stop() error {
592+ mock.state = golxc.StateStopped
593+ return nil
594+}
595+
596+// Clone creates a copy of the container, giving the copy the specified name.
597+func (mock *mockContainer) Clone(name string) (golxc.Container, error) {
598+ container := &mockContainer{
599+ factory: mock.factory,
600+ name: name,
601+ state: golxc.StateStopped,
602+ logLevel: golxc.LogWarning,
603+ }
604+ mock.factory.instances[name] = container
605+ return container, nil
606+}
607+
608+// Freeze freezes all the container's processes.
609+func (mock *mockContainer) Freeze() error {
610+ return nil
611+}
612+
613+// Unfreeze thaws all frozen container's processes.
614+func (mock *mockContainer) Unfreeze() error {
615+ return nil
616+}
617+
618+// Destroy stops and removes the container.
619+func (mock *mockContainer) Destroy() error {
620+ mock.state = golxc.StateUnknown
621+ delete(mock.factory.instances, mock.name)
622+ return nil
623+}
624+
625+// Wait waits for one of the specified container states.
626+func (mock *mockContainer) Wait(states ...golxc.State) error {
627+ return nil
628+}
629+
630+// Info returns the status and the process id of the container.
631+func (mock *mockContainer) Info() (golxc.State, int, error) {
632+ pid := -1
633+ if mock.state == golxc.StateRunning {
634+ pid = 42
635+ }
636+ return mock.state, pid, nil
637+}
638+
639+// IsConstructed checks if the container image exists.
640+func (mock *mockContainer) IsConstructed() bool {
641+ return mock.state != golxc.StateUnknown
642+}
643+
644+// IsRunning checks if the state of the container is 'RUNNING'.
645+func (mock *mockContainer) IsRunning() bool {
646+ return mock.state == golxc.StateRunning
647+}
648+
649+// String returns information about the container, like the name, state,
650+// and process id.
651+func (mock *mockContainer) String() string {
652+ _, pid, _ := mock.Info()
653+ return fmt.Sprintf("<MockContainer %q, state: %s, pid %d>", mock.name, string(mock.state), pid)
654+}
655+
656+// LogFile returns the current filename used for the LogFile.
657+func (mock *mockContainer) LogFile() string {
658+ return mock.logFile
659+}
660+
661+// LogLevel returns the current logging level (only used if the
662+// LogFile is not "").
663+func (mock *mockContainer) LogLevel() golxc.LogLevel {
664+ return mock.logLevel
665+}
666+
667+// SetLogFile sets both the LogFile and LogLevel.
668+func (mock *mockContainer) SetLogFile(filename string, level golxc.LogLevel) {
669+ mock.logFile = filename
670+ mock.logLevel = level
671+}
672+
673+func (mock *mockFactory) New(name string) golxc.Container {
674+ container := &mockContainer{
675+ factory: mock,
676+ name: name,
677+ state: golxc.StateUnknown,
678+ logLevel: golxc.LogWarning,
679+ }
680+ return container
681+}
682+
683+func (mock *mockFactory) List() (result []golxc.Container, err error) {
684+ for _, container := range mock.instances {
685+ result = append(result, container)
686+ }
687+ return
688+}
689
690=== modified file 'environs/cloudinit/cloudinit.go'
691--- environs/cloudinit/cloudinit.go 2013-06-14 02:32:16 +0000
692+++ environs/cloudinit/cloudinit.go 2013-06-25 21:33:28 +0000
693@@ -76,7 +76,7 @@
694
695 // MachineContainerType specifies the type of container that the machine
696 // is. If the machine is not a container, then the type is "".
697- MachineContainerType string
698+ MachineContainerType state.ContainerType
699
700 // AuthorizedKeys specifies the keys that are allowed to
701 // connect to the machine (see cloudinit.SSHAddAuthorizedKeys)
702@@ -121,7 +121,7 @@
703 c.AddPackage("git")
704 // Perfectly reasonable to install lxc on environment instances and kvm
705 // containers.
706- if cfg.MachineContainerType != "lxc" {
707+ if cfg.MachineContainerType != state.LXC {
708 c.AddPackage("lxc")
709 }
710
711
712=== added file 'testing/instance.go'
713--- testing/instance.go 1970-01-01 00:00:00 +0000
714+++ testing/instance.go 2013-06-25 21:33:28 +0000
715@@ -0,0 +1,25 @@
716+// Copyright 2013 Canonical Ltd.
717+// Licensed under the AGPLv3, see LICENCE file for details.
718+
719+package testing
720+
721+import (
722+ . "launchpad.net/gocheck"
723+ "launchpad.net/juju-core/instance"
724+)
725+
726+// MatchInstances uses DeepEquals to check the instances returned. The lists
727+// are first put into a map, so the ordering of the result and expected values
728+// is not tested, and duplicates are ignored.
729+func MatchInstances(c *C, result []instance.Instance, expected ...instance.Instance) {
730+ resultMap := make(map[instance.Id]instance.Instance)
731+ for _, i := range result {
732+ resultMap[i.Id()] = i
733+ }
734+
735+ expectedMap := make(map[instance.Id]instance.Instance)
736+ for _, i := range expected {
737+ expectedMap[i.Id()] = i
738+ }
739+ c.Assert(resultMap, DeepEquals, expectedMap)
740+}

Subscribers

People subscribed via source and target branches

to status/vote changes: