Merge lp:~dimitern/juju-core/330-general-improvements into lp:~go-bot/juju-core/trunk

Proposed by Dimiter Naydenov
Status: Merged
Approved by: Dimiter Naydenov
Approved revision: no longer in the source branch.
Merged at revision: 2409
Proposed branch: lp:~dimitern/juju-core/330-general-improvements
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 1961 lines (+551/-446)
35 files modified
cmd/juju/destroyenvironment.go (+1/-1)
cmd/juju/destroyenvironment_test.go (+2/-2)
cmd/juju/help_topics.go (+31/-25)
environs/boilerplate_config.go (+20/-8)
environs/bootstrap/bootstrap.go (+3/-2)
environs/bootstrap/state.go (+2/-0)
environs/cloudinit/cloudinit.go (+2/-2)
environs/cloudinit/cloudinit_test.go (+4/-4)
environs/httpstorage/storage.go (+7/-0)
environs/jujutest/livetests.go (+2/-3)
environs/jujutest/tests.go (+5/-6)
environs/manual/bootstrap_test.go (+17/-16)
environs/manual/export_test.go (+7/-0)
environs/manual/fakessh.go (+0/-87)
environs/manual/fakessh_test.go (+156/-0)
environs/manual/init.go (+9/-7)
environs/manual/init_test.go (+26/-26)
environs/manual/provisioner_test.go (+10/-9)
environs/open.go (+22/-14)
environs/sshstorage/storage.go (+6/-0)
environs/testing/storage.go (+0/-17)
environs/utils.go (+22/-0)
juju/conn_test.go (+3/-3)
provider/azure/config.go (+19/-11)
provider/common/bootstrap.go (+0/-15)
provider/ec2/ec2.go (+16/-9)
provider/joyent/config.go (+40/-19)
provider/local/environprovider.go (+15/-9)
provider/maas/environprovider.go (+4/-2)
provider/manual/environ.go (+1/-0)
provider/openstack/provider.go (+89/-59)
state/apiserver/charms.go (+3/-3)
state/apiserver/charms_test.go (+3/-3)
state/apiserver/client/client_test.go (+4/-4)
utils/ssh/testing/fakessh.go (+0/-80)
To merge this branch: bzr merge lp:~dimitern/juju-core/330-general-improvements
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+210108@code.launchpad.net

Commit message

various: production code and logging improvements

Fixed 4 slightly annoying, unrelated minor issues:
1. Refactored production code not to depend on
gocheck (manual provider and apiserver/charms).
As a drive-by fix, I refactored environs/manual
tests to be proper black-box unit tests, rather
than white-box tests in the manual package.
2. Fixed and improved tools download output via
curl and better handling of errors.
3. Added debug logging to environs sshstorage and
httpstorage, and the manual provider.
4. Reformatted and unified generated boilerplate
config across all providers, fixing bug #1221134 in
the process (-e env not properly explained).

Changes tested live with a manual and local environs
with added manually provisioned machines.

While testing I found out and filed this bug #1291292
(basically manual bootstrap is broken with ssl-hostname-
verification set to false, but this is due to a deeper
issue).

I tried to unify and fix when all commands report
"environment not bootstrapped", consistently across
providers, but it turned out like more work than I
originally thought, so I'll do a follow-up on that
(which will entail dropping support for 1.14 environments
without a .jenv file).

https://codereview.appspot.com/72860045/

R=jameinel, rogpeppe

Description of the change

various: production code and logging improvements

Fixed 4 slightly annoying, unrelated minor issues:
1. Refactored production code not to depend on
gocheck (manual provider and apiserver/charms).
As a drive-by fix, I refactored environs/manual
tests to be proper black-box unit tests, rather
than white-box tests in the manual package.
2. Fixed and improved tools download output via
curl and better handling of errors.
3. Added debug logging to environs sshstorage and
httpstorage, and the manual provider.
4. Reformatted and unified generated boilerplate
config across all providers, fixing bug #1221134 in
the process (-e env not properly explained).

Changes tested live with a manual and local environs
with added manually provisioned machines.

While testing I found out and filed this bug #1291292
(basically manual bootstrap is broken with ssl-hostname-
verification set to false, but this is due to a deeper
issue).

I tried to unify and fix when all commands report
"environment not bootstrapped", consistently across
providers, but it turned out like more work than I
originally thought, so I'll do a follow-up on that
(which will entail dropping support for 1.14 environments
without a .jenv file).

https://codereview.appspot.com/72860045/

To post a comment you must log in.
Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Reviewers: mp+210108_code.launchpad.net,

Message:
Please take a look.

Description:
various: production code and logging improvements

Fixed 3 slightly annoying issues:
1. Refactored production code not to depend on
gocheck (manual provider and apiserver/charms).
2. Fixed and improved tools download output via
curl and better handling of errors.
3. Added debug logging to environs sshstorage and
httpstorage, and the manual provider.

Changes tested live with a manual and local environs
with added manually provisioned machines.

https://code.launchpad.net/~dimitern/juju-core/330-general-improvements/+merge/210108

(do not edit description out of merge proposal)

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

Affected files (+207, -177 lines):
   A [revision details]
   M environs/bootstrap/state.go
   M environs/cloudinit/cloudinit.go
   M environs/cloudinit/cloudinit_test.go
   M environs/httpstorage/storage.go
   M environs/manual/bootstrap_test.go
   D environs/manual/fakessh.go
   M environs/manual/init.go
   M environs/manual/init_test.go
   M environs/manual/provisioner_test.go
   M environs/sshstorage/storage.go
   M environs/testing/storage.go
   M provider/manual/environ.go
   M state/apiserver/charms.go
   M state/apiserver/charms_test.go
   M state/apiserver/client/client_test.go
   M utils/http.go
   M utils/ssh/ssh.go
   M utils/ssh/testing/fakessh.go
   A utils/storage/storage.go

Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/72860045/diff/1/environs/manual/init.go
File environs/manual/init.go (right):

https://codereview.appspot.com/72860045/diff/1/environs/manual/init.go#newcode27
environs/manual/init.go:27: cmd.Stdin =
strings.NewReader(ssh.CheckProvisionedScript)
utils/ssh is for *general ssh* things. These scripts do not belong
there. I see the case for exposing them, for the test code, but please
keep them out of utils/ssh.

https://codereview.appspot.com/72860045/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Please take a look.

https://codereview.appspot.com/72860045/diff/1/environs/manual/init.go
File environs/manual/init.go (right):

https://codereview.appspot.com/72860045/diff/1/environs/manual/init.go#newcode27
environs/manual/init.go:27: cmd.Stdin =
strings.NewReader(ssh.CheckProvisionedScript)
On 2014/03/10 01:08:52, axw wrote:
> utils/ssh is for *general ssh* things. These scripts do not belong
there. I see
> the case for exposing them, for the test code, but please keep them
out of
> utils/ssh.

Thanks for the explanation! As discussed on IRC, moved fakessh to
environs/manual/testing and the scripts in environs/manual.

https://codereview.appspot.com/72860045/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Thanks for the changes, and cleaning the code up. Looks much better now
:)

Just a few little things...

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go
File environs/bootstrap/state.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go#newcode43
environs/bootstrap/state.go:43: logger.Debugf("putting %q to boostrap
storage %#v", StateFile, storage)
s/boostrap/bootstrap/

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go
File environs/configstore/disk.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go#newcode29
environs/configstore/disk.go:29: // Exists returns if the default
disk-based environment config storage
returns true iff?

https://codereview.appspot.com/72860045/diff/2/environs/open.go
File environs/open.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/open.go#newcode93
environs/open.go:93: if source != ConfigFromInfo {
Is this right? You're changing behaviour here; the old code only updated
the error if the existing error was non-nil.

https://codereview.appspot.com/72860045/

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

overall LGTM, though I did have some comments.

https://codereview.appspot.com/72860045/diff/2/environs/open.go
File environs/open.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/open.go#newcode93
environs/open.go:93: if source != ConfigFromInfo {
On 2014/03/11 04:43:22, axw wrote:
> Is this right? You're changing behaviour here; the old code only
updated the
> error if the existing error was non-nil.

So I think this means that if an environ was bootstrapped with 1.14 then
you wouldn't have the .jenv. However, we don't need to preserve compat
with 1.14 if it makes things cleaner.
1.16+ should have always created a .jenv when it bootstrapped.

https://codereview.appspot.com/72860045/diff/2/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/72860045/diff/2/errors/errors.go#newcode6
errors/errors.go:6: import "fmt"
I actually prefer the other form.
Because as soon as we need 2 imports, we want to do it anyway, and that
avoids having the noise of what actually changed confused with the extra
lines.

Certainly not worth going around again, just wanted to give my feeling
that multiline imports are still reasonable to use when you only have
one.

https://codereview.appspot.com/72860045/diff/2/juju/api.go
File juju/api.go (right):

https://codereview.appspot.com/72860045/diff/2/juju/api.go#newcode107
juju/api.go:107: }
I didn't see a change in apiconn_test.go that would actually exercise
this. Can you add one so we don't break it by accident?

https://codereview.appspot.com/72860045/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

review so far.

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go
File environs/bootstrap/state.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go#newcode43
environs/bootstrap/state.go:43: logger.Debugf("putting %q to boostrap
storage %#v", StateFile, storage)
I'd be tempted lose this line entirely - it would be nicer to have
better debugging on the storage put methods. But if we keep it, I'd lose
the %#v - it could be very noisy. %T might be better.

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go
File environs/configstore/disk.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go#newcode29
environs/configstore/disk.go:29: // Exists returns if the default
disk-based environment config storage
On 2014/03/11 04:43:22, axw wrote:
> returns true iff?

The canonical form is:

// Exists reports whether ...

I'm having difficulty understanding why this function exists though.
For a start, the name "Exists" doesn't imply in which configstore
implementation the environment info exists.

The only place that it's used in the production code, it seems that it
could be trivially replaced with a call to ReadInfo.

It's easy to "mock" the existence of the environ info without resorting
to replacing this function - just create the info. That way our tests
don't become unnecessarily dependent on internal details of the code.

I suggest removing this functon.

https://codereview.appspot.com/72860045/

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

A few more suggestions. It's great that we've lost the testing
dependencies from the code, but there are a few pieces I'm concerned
about.

https://codereview.appspot.com/72860045/diff/2/environs/manual/init.go
File environs/manual/init.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/manual/init.go#newcode21
environs/manual/init.go:21: const DetectionScript = `#!/bin/bash
I don't quite see why this and CheckProvisionedScript now need to be
exported.

The only reason that I can see is that manual/testing is a separate
package. Given that it's only imported from environs/manual, I don't see
what value that adds. I'd prefer to avoid unnecessary twisty
dependencies.

https://codereview.appspot.com/72860045/diff/2/environs/open.go
File environs/open.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/open.go#newcode93
environs/open.go:93: if source != ConfigFromInfo {
I think andrew has a point here - if ConfigForName returns a useful
error message, it will be lost.

If we're going to deprecate reading config from environments.yaml,
that deserves its own CL, as there is a substantial amount
of cleanup that can and should happen then.

https://codereview.appspot.com/72860045/diff/2/errors/errors.go
File errors/errors.go (right):

https://codereview.appspot.com/72860045/diff/2/errors/errors.go#newcode6
errors/errors.go:6: import "fmt"
On 2014/03/11 10:06:33, jameinel wrote:
> I actually prefer the other form.
> Because as soon as we need 2 imports, we want to do it anyway, and
that avoids
> having the noise of what actually changed confused with the extra
lines.

> Certainly not worth going around again, just wanted to give my feeling
that
> multiline imports are still reasonable to use when you only have one.

I think we shouldn't care too much. If goimports inserts a single
import, it does so without the ( ), and that's a useful enough tool that
I think it's reasonable to allow its default output through.

https://codereview.appspot.com/72860045/diff/2/juju/api.go
File juju/api.go (right):

https://codereview.appspot.com/72860045/diff/2/juju/api.go#newcode104
juju/api.go:104: if !configstore.Exists(envName) {
As I mentioned in earlier comments, I don't think this is right.

If we want to make it so that we can't open the API without a .jenv
file, that should be a deeper change in this file - there is a fair
amount of logic that can be simplified here.

https://codereview.appspot.com/72860045/diff/2/juju/apiconn_test.go
File juju/apiconn_test.go (right):

https://codereview.appspot.com/72860045/diff/2/juju/apiconn_test.go#newcode257
juju/apiconn_test.go:257: bootstrapEnv(c, &cs.CleanupSuite,
coretesting.SampleEnvName, store)
This is changed only because of the unnecessary Exists patching AFAICS.

https://codereview.appspot.com/72860045/diff/2/juju/conn_test.go
File juju/conn_test.go (right):

https://codereview.appspot.com/72860045/diff/2/juju/conn_test.go#newcode88
juju/conn_test.go:88: s.PatchValue(&configstore.Exists, func(name
string) bool {
This seems wrong, as I've mentioned earlier.
It is not at all clear how patching this value will
affect any of the lines of code below - it's truly
action-at-a-distan...

Read more...

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Download full text (11.6 KiB)

Please take a look.

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go
File environs/bootstrap/state.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go#newcode43
environs/bootstrap/state.go:43: logger.Debugf("putting %q to boostrap
storage %#v", StateFile, storage)
On 2014/03/11 04:43:22, axw wrote:
> s/boostrap/bootstrap/

Done.

https://codereview.appspot.com/72860045/diff/2/environs/bootstrap/state.go#newcode43
environs/bootstrap/state.go:43: logger.Debugf("putting %q to boostrap
storage %#v", StateFile, storage)
On 2014/03/11 11:57:46, rog wrote:
> I'd be tempted lose this line entirely - it would be nicer to have
better
> debugging on the storage put methods. But if we keep it, I'd lose the
%#v - it
> could be very noisy. %T might be better.

Changed to %T as suggested.

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go
File environs/configstore/disk.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go#newcode29
environs/configstore/disk.go:29: // Exists returns if the default
disk-based environment config storage
On 2014/03/11 04:43:22, axw wrote:
> returns true iff?

I dropped this entirely.

https://codereview.appspot.com/72860045/diff/2/environs/configstore/disk.go#newcode29
environs/configstore/disk.go:29: // Exists returns if the default
disk-based environment config storage
On 2014/03/11 11:57:46, rog wrote:
> On 2014/03/11 04:43:22, axw wrote:
> > returns true iff?

> The canonical form is:

> // Exists reports whether ...

> I'm having difficulty understanding why this function exists though.
> For a start, the name "Exists" doesn't imply in which configstore
implementation
> the environment info exists.

> The only place that it's used in the production code, it seems that it
could be
> trivially replaced with a call to ReadInfo.

> It's easy to "mock" the existence of the environ info without
resorting to
> replacing this function - just create the info. That way our tests
don't become
> unnecessarily dependent on internal details of the code.

> I suggest removing this functon.

I dropped this entirely.

https://codereview.appspot.com/72860045/diff/2/environs/manual/init.go
File environs/manual/init.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/manual/init.go#newcode21
environs/manual/init.go:21: const DetectionScript = `#!/bin/bash
On 2014/03/11 12:59:34, rog wrote:
> I don't quite see why this and CheckProvisionedScript now need to be
exported.

> The only reason that I can see is that manual/testing is a separate
package.
> Given that it's only imported from environs/manual, I don't see what
value that
> adds. I'd prefer to avoid unnecessary twisty dependencies.

DetectionScript and CheckProvisioned scripts need to be exported,
because they are used by both production code and tests.

https://codereview.appspot.com/72860045/diff/2/environs/open.go
File environs/open.go (right):

https://codereview.appspot.com/72860045/diff/2/environs/open.go#newcode93
environs/open.go:93: if source != ConfigFromInfo {
On 2014/03/11 10:06:33, jameinel wrote:
> On 2014/03/11 04:43:22, axw wrote:...

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

LGTM, though you should probably wait for Roger since he had the most
comments on it.

https://codereview.appspot.com/72860045/

Revision history for this message
Roger Peppe (rogpeppe) wrote :

On 2014/03/12 12:13:00, jameinel wrote:
> LGTM, though you should probably wait for Roger since he had the most
comments
> on it.

LGTM, thanks!

https://codereview.appspot.com/72860045/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/juju/destroyenvironment.go'
2--- cmd/juju/destroyenvironment.go 2014-02-11 01:35:20 +0000
3+++ cmd/juju/destroyenvironment.go 2014-03-12 12:33:26 +0000
4@@ -67,7 +67,7 @@
5 }
6 answer := strings.ToLower(scanner.Text())
7 if answer != "y" && answer != "yes" {
8- return errors.New("Environment destruction aborted")
9+ return errors.New("environment destruction aborted")
10 }
11 }
12 // If --force is supplied, then don't attempt to use the API.
13
14=== modified file 'cmd/juju/destroyenvironment_test.go'
15--- cmd/juju/destroyenvironment_test.go 2014-02-13 02:46:58 +0000
16+++ cmd/juju/destroyenvironment_test.go 2014-03-12 12:33:26 +0000
17@@ -130,7 +130,7 @@
18 // Ensure confirmation is requested if "-y" is not specified.
19 stdin.WriteString("n")
20 opc, errc := runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv")
21- c.Check(<-errc, gc.ErrorMatches, "Environment destruction aborted")
22+ c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted")
23 c.Check(<-opc, gc.IsNil)
24 c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*")
25 assertEnvironNotDestroyed(c, env, s.ConfigStore)
26@@ -140,7 +140,7 @@
27 stdout.Reset()
28 opc, errc = runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv")
29 c.Check(<-opc, gc.IsNil)
30- c.Check(<-errc, gc.ErrorMatches, "Environment destruction aborted")
31+ c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted")
32 assertEnvironNotDestroyed(c, env, s.ConfigStore)
33
34 // "--yes" passed: no confirmation request.
35
36=== modified file 'cmd/juju/help_topics.go'
37--- cmd/juju/help_topics.go 2014-03-04 14:33:43 +0000
38+++ cmd/juju/help_topics.go 2014-03-12 12:33:26 +0000
39@@ -47,8 +47,8 @@
40
41 `
42
43-const helpLocalProvider = `
44-The local provider is a linux-only Juju environment that uses LXC containers as
45+const helpLocalProvider = `
46+The local provider is a Linux-only Juju environment that uses LXC containers as
47 a virtual cloud on the local machine. Because of this, lxc and mongodb are
48 required for the local provider to work. If you don't already have lxc and
49 mongodb installed, run the following commands:
50@@ -64,14 +64,14 @@
51 Now you need to tell Juju to use the local provider and then bootstrap:
52
53 juju switch local
54- sudo juju bootstrap
55+ juju bootstrap
56
57 The first time this runs it might take a bit, as it's doing a netinstall for
58 the container, it's around a 300 megabyte download. Subsequent bootstraps
59-should be much quicker. 'sudo' is needed because only root can create LXC
60-containers. After the initial bootstrap, you do not need 'sudo' anymore,
61-except to 'sudo juju destroy-environment' when you want to tear everything
62-down.
63+should be much quicker. You'll be asked for your 'sudo' password, which is
64+needed because only root can create LXC containers. When you need to destroy
65+the environment, do 'juju destroy-environment local' and you could be asked
66+for your 'sudo' password again.
67
68 You deploy charms from the charm store using the following commands:
69
70@@ -90,31 +90,35 @@
71
72 sample_openstack:
73 type: openstack
74+
75 # Specifies whether the use of a floating IP address is required to
76 # give the nodes a public IP address. Some installations assign public
77 # IP addresses by default without requiring a floating IP address.
78 # use-floating-ip: false
79+
80 # Specifies whether new machine instances should have the "default"
81 # Openstack security group assigned.
82 # use-default-secgroup: false
83- admin-secret: 13850d1b9786065cadd0f477e8c97cd3
84- # Globally unique swift bucket name
85- control-bucket: juju-fd6ab8d02393af742bfbe8b9629707ee
86+
87 # Usually set via the env variable OS_AUTH_URL, but can be specified here
88 # auth-url: https://yourkeystoneurl:443/v2.0/
89- # override if your workstation is running a different series to which
90- # you are deploying
91+
92 # The following are used for userpass authentication (the default)
93- auth-mode: userpass
94+ # auth-mode: userpass
95+
96 # Usually set via the env variable OS_USERNAME, but can be specified here
97 # username: <your username>
98+
99 # Usually set via the env variable OS_PASSWORD, but can be specified here
100 # password: <secret>
101+
102 # Usually set via the env variable OS_TENANT_NAME, but can be specified here
103 # tenant-name: <your tenant name>
104+
105 # Usually set via the env variable OS_REGION_NAME, but can be specified here
106 # region: <your region>
107
108+If you have set the described OS_* environment variables, you only need "type:".
109 References:
110
111 http://juju.ubuntu.com/docs/provider-configuration-openstack.html
112@@ -125,7 +129,7 @@
113 This answer is for generic OpenStack support, if you're using an OpenStack-based
114 provider check these questions out for provider-specific information:
115
116- https://juju.ubuntu.com/docs/config-hpcloud.html
117+ https://juju.ubuntu.com/docs/config-hpcloud.html
118
119 `
120
121@@ -141,11 +145,8 @@
122 type: ec2
123 # access-key: YOUR-ACCESS-KEY-GOES-HERE
124 # secret-key: YOUR-SECRET-KEY-GOES-HERE
125- control-bucket: juju-faefb490d69a41f0a3616a4808e0766b
126- admin-secret: 81a1e7429e6847c4941fda7591246594
127
128-See the EC2 provider documentation[2] for more options. The S3 bucket does not
129-need to exist already.
130+See the EC2 provider documentation[2] for more options.
131
132 Note If you already have an AWS account, you can determine your access key by
133 visiting your account page[3], clicking "Security Credentials" and then clicking
134@@ -183,7 +184,7 @@
135
136 See the online help for more information:
137
138- https://juju.ubuntu.com/docs/config-hpcloud.html
139+ https://juju.ubuntu.com/docs/config-hpcloud.html
140 `
141
142 const helpAzureProvider = `
143@@ -191,17 +192,22 @@
144
145 sample_azure:
146 type: azure
147+
148 # Location for instances, e.g. West US, North Europe.
149 location: West US
150+
151 # http://msdn.microsoft.com/en-us/library/windowsazure
152 # Windows Azure Management info.
153 management-subscription-id: 886413e1-3b8a-5382-9b90-0c9aee199e5d
154 management-certificate-path: /home/me/azure.pem
155+
156 # Windows Azure Storage info.
157 storage-account-name: juju0useast0
158+
159 # Override OS image selection with a fixed image for all deployments.
160 # Most useful for developers.
161 # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB
162+
163 # Pick a simplestreams stream to select OS images from: daily or released
164 # images, or any other stream available on simplestreams. Leave blank for
165 # released images.
166@@ -219,7 +225,7 @@
167 https://juju.ubuntu.com/docs/config-azure.html
168 `
169
170-const helpConstraints = `
171+const helpConstraints = `
172 Constraints constrain the possible instances that may be started by juju
173 commands. They are usually passed as a flag to commands that provision a new
174 machine (such as bootstrap, deploy, and add-machine).
175@@ -272,7 +278,7 @@
176 T terabytes (1024 gigabytes)
177 P petabytes (1024 terabytes)
178
179-root-disk
180+root-disk
181 Root-Disk is a float that defines the amount of space in megabytes that must
182 be available in the machine's root partition. For providers that have
183 configurable root disk sizes (such as EC2) an instance with the specified
184@@ -280,8 +286,8 @@
185 defaults to megabytes and may be specified in the same manner as the mem
186 constraint.
187
188-container
189- Container defines that the machine must be a container of the specified type.
190+container
191+ Container defines that the machine must be a container of the specified type.
192 A container of that type may be created by juju to fulfill the request.
193 Currently supported containers:
194 none - (default) no container
195@@ -294,7 +300,7 @@
196 roughly, a single 2007-era Xeon). Cpu-power is currently only supported by
197 the Amazon EC2 environment.
198
199-tags
200+tags
201 Tags defines the list of tags that the machine must have applied to it.
202 Multiple tags must be delimited by a comma. Tags are currently only supported
203 by the MaaS environment.
204@@ -374,7 +380,7 @@
205 other, and the way in which the topology of Services is assembled. The Charm
206 defines which Relations a given Service may establish, and what kind of
207 interface these Relations require.
208-
209+
210 In many cases, the establishment of a Relation will result into an actual TCP
211 connection being created between the Service Units, but that's not necessarily
212 the case. Relations may also be established to inform Services of
213
214=== modified file 'environs/boilerplate_config.go'
215--- environs/boilerplate_config.go 2013-10-11 14:05:04 +0000
216+++ environs/boilerplate_config.go 2014-03-12 12:33:26 +0000
217@@ -12,24 +12,36 @@
218 )
219
220 var configHeader = `
221-# This is the Juju config file, which you can use to specify multiple environments in which to deploy.
222-# By default Juju ships AWS (default), HP Cloud, OpenStack.
223-# See https://juju.ubuntu.com/docs for more information
224+# This is the Juju config file, which you can use to specify multiple
225+# environments in which to deploy. By default Juju ships with AWS
226+# (default), HP Cloud, OpenStack, Azure, MaaS, Local and Manual
227+# providers. See https://juju.ubuntu.com/docs for more information
228
229-# An environment configuration must always specify at least the following information:
230-#
231+# An environment configuration must always specify at least the
232+# following information:
233 # - name (to identify the environment)
234 # - type (to specify the provider)
235+# In the following example the name is "myenv" and type is "ec2".
236+# myenv:
237+# type: ec2
238
239 # Values in <brackets> below need to be filled in by the user.
240 # Optional attributes are shown commented out, with
241 # a sample value or a value in <brackets>.
242
243+# There are several settings supported by all environments, all of which
244+# are optional and have specified default values. For more info, see the
245+# Juju documentation.
246+
247 # The default environment is chosen when an environment is not
248 # specified using any of the following, in descending order of precedence:
249-# -e or --environment command line parameter.
250-# JUJU_ENV environment variable.
251-# the juju switch command.
252+# 1. -e or --environment command line parameter, passed after the command, e.g.
253+# $ juju add-unit -e myenv myservice
254+# 2. By setting JUJU_ENV environment variable.
255+# 3. Using the juju switch command like this:
256+# $ juju switch myenv
257+#
258+
259 default: amazon
260
261 environments:
262
263=== modified file 'environs/bootstrap/bootstrap.go'
264--- environs/bootstrap/bootstrap.go 2014-03-05 19:41:34 +0000
265+++ environs/bootstrap/bootstrap.go 2014-03-12 12:33:26 +0000
266@@ -71,8 +71,9 @@
267 return toolsList, nil
268 }
269
270-// EnsureNotBootstrapped returns null if the environment is not bootstrapped,
271-// and an error if it is or if the function was not able to tell.
272+// EnsureNotBootstrapped returns nil if the environment is not
273+// bootstrapped, and an error if it is or if the function was not able
274+// to tell.
275 func EnsureNotBootstrapped(env environs.Environ) error {
276 _, err := LoadState(env.Storage())
277 // If there is no error loading the bootstrap state, then we are
278
279=== modified file 'environs/bootstrap/state.go'
280--- environs/bootstrap/state.go 2014-02-25 22:19:30 +0000
281+++ environs/bootstrap/state.go 2014-03-12 12:33:26 +0000
282@@ -40,6 +40,7 @@
283 // putState writes the given data to the state file on the given storage.
284 // The file's name is as defined in StateFile.
285 func putState(storage storage.StorageWriter, data []byte) error {
286+ logger.Debugf("putting %q to bootstrap storage %T", StateFile, storage)
287 return storage.Put(StateFile, bytes.NewBuffer(data), int64(len(data)))
288 }
289
290@@ -69,6 +70,7 @@
291
292 // LoadStateFromURL reads state from the given URL.
293 func LoadStateFromURL(url string, disableSSLHostnameVerification bool) (*BootstrapState, error) {
294+ logger.Debugf("loading %q from %q", StateFile, url)
295 client := http.DefaultClient
296 if disableSSLHostnameVerification {
297 logger.Infof("hostname SSL verification disabled")
298
299=== modified file 'environs/cloudinit/cloudinit.go'
300--- environs/cloudinit/cloudinit.go 2014-03-11 18:33:39 +0000
301+++ environs/cloudinit/cloudinit.go 2014-03-12 12:33:26 +0000
302@@ -299,9 +299,9 @@
303 if strings.HasPrefix(cfg.Tools.URL, fileSchemePrefix) {
304 copyCmd = fmt.Sprintf("cp %s $bin/tools.tar.gz", shquote(cfg.Tools.URL[len(fileSchemePrefix):]))
305 } else {
306- curlCommand := "curl"
307+ curlCommand := "curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s '"
308 if cfg.DisableSSLHostnameVerification {
309- curlCommand = "curl --insecure"
310+ curlCommand += " --insecure"
311 }
312 copyCmd = fmt.Sprintf("%s -o $bin/tools.tar.gz %s", curlCommand, shquote(cfg.Tools.URL))
313 c.AddRunCmd(cloudinit.LogProgressCmd("Fetching tools: %s", copyCmd))
314
315=== modified file 'environs/cloudinit/cloudinit_test.go'
316--- environs/cloudinit/cloudinit_test.go 2014-03-11 18:33:39 +0000
317+++ environs/cloudinit/cloudinit_test.go 2014-03-12 12:33:26 +0000
318@@ -124,7 +124,7 @@
319 echo 'Fetching tools.*
320 bin='/var/lib/juju/tools/1\.2\.3-precise-amd64'
321 mkdir -p \$bin
322-curl -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-precise-amd64\.tgz'
323+curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-precise-amd64\.tgz'
324 sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-precise-amd64\.sha256
325 grep '1234' \$bin/juju1\.2\.3-precise-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)
326 tar zxf \$bin/tools.tar.gz -C \$bin
327@@ -195,7 +195,7 @@
328 inexactMatch: true,
329 expectScripts: `
330 bin='/var/lib/juju/tools/1\.2\.3-raring-amd64'
331-curl -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz'
332+curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz'
333 sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-raring-amd64\.sha256
334 grep '1234' \$bin/juju1\.2\.3-raring-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)
335 rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-raring-amd64\.sha256
336@@ -243,7 +243,7 @@
337 echo 'Fetching tools.*
338 bin='/var/lib/juju/tools/1\.2\.3-linux-amd64'
339 mkdir -p \$bin
340-curl -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz'
341+curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz'
342 sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-linux-amd64\.sha256
343 grep '1234' \$bin/juju1\.2\.3-linux-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)
344 tar zxf \$bin/tools.tar.gz -C \$bin
345@@ -324,7 +324,7 @@
346 },
347 inexactMatch: true,
348 expectScripts: `
349-curl --insecure -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz'
350+curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' --insecure -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz'
351 `,
352 }, {
353 // empty contraints.
354
355=== modified file 'environs/httpstorage/storage.go'
356--- environs/httpstorage/storage.go 2014-02-25 22:19:30 +0000
357+++ environs/httpstorage/storage.go 2014-03-12 12:33:26 +0000
358@@ -16,11 +16,15 @@
359 "strings"
360 "sync"
361
362+ "github.com/juju/loggo"
363+
364 "launchpad.net/juju-core/environs/storage"
365 coreerrors "launchpad.net/juju-core/errors"
366 "launchpad.net/juju-core/utils"
367 )
368
369+var logger = loggo.GetLogger("juju.environs.httpstorage")
370+
371 // storage implements the storage.Storage interface.
372 type localStorage struct {
373 addr string
374@@ -46,6 +50,7 @@
375 // using TLS. The client is given an authentication key,
376 // which the server will verify for Put and Remove* operations.
377 func ClientTLS(addr string, caCertPEM []byte, authkey string) (storage.Storage, error) {
378+ logger.Debugf("using https storage at %q", addr)
379 caCerts := x509.NewCertPool()
380 if caCertPEM != nil {
381 if !caCerts.AppendCertsFromPEM(caCertPEM) {
382@@ -83,6 +88,7 @@
383 // responsibility to close it after use. If the name does not
384 // exist, it should return a *NotFoundError.
385 func (s *localStorage) Get(name string) (io.ReadCloser, error) {
386+ logger.Debugf("getting %q from storage", name)
387 url, err := s.URL(name)
388 if err != nil {
389 return nil, err
390@@ -167,6 +173,7 @@
391 // Put reads from r and writes to the given storage file.
392 // The length must be set to the total length of the file.
393 func (s *localStorage) Put(name string, r io.Reader, length int64) error {
394+ logger.Debugf("putting %q (len %d) to storage", name, length)
395 url, err := s.modURL(name)
396 if err != nil {
397 return err
398
399=== modified file 'environs/jujutest/livetests.go'
400--- environs/jujutest/livetests.go 2014-02-20 08:23:40 +0000
401+++ environs/jujutest/livetests.go 2014-03-12 12:33:26 +0000
402@@ -27,7 +27,6 @@
403 "launchpad.net/juju-core/instance"
404 "launchpad.net/juju-core/juju"
405 "launchpad.net/juju-core/juju/testing"
406- "launchpad.net/juju-core/provider/common"
407 "launchpad.net/juju-core/provider/dummy"
408 "launchpad.net/juju-core/state"
409 "launchpad.net/juju-core/state/api"
410@@ -138,7 +137,7 @@
411 c.Assert(err, gc.IsNil)
412 }
413 envtesting.UploadFakeTools(c, t.Env.Storage())
414- err := common.EnsureNotBootstrapped(t.Env)
415+ err := bootstrap.EnsureNotBootstrapped(t.Env)
416 c.Assert(err, gc.IsNil)
417 err = bootstrap.Bootstrap(coretesting.Context(c), t.Env, cons)
418 c.Assert(err, gc.IsNil)
419@@ -382,7 +381,7 @@
420 // already up, this has been moved into the bootstrap command.
421 t.BootstrapOnce(c)
422
423- err := common.EnsureNotBootstrapped(t.Env)
424+ err := bootstrap.EnsureNotBootstrapped(t.Env)
425 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
426
427 c.Logf("destroy env")
428
429=== modified file 'environs/jujutest/tests.go'
430--- environs/jujutest/tests.go 2014-02-13 02:46:58 +0000
431+++ environs/jujutest/tests.go 2014-03-12 12:33:26 +0000
432@@ -21,7 +21,6 @@
433 "launchpad.net/juju-core/errors"
434 "launchpad.net/juju-core/instance"
435 "launchpad.net/juju-core/juju/testing"
436- "launchpad.net/juju-core/provider/common"
437 coretesting "launchpad.net/juju-core/testing"
438 jc "launchpad.net/juju-core/testing/checkers"
439 "launchpad.net/juju-core/testing/testbase"
440@@ -132,7 +131,7 @@
441 func (t *Tests) TestBootstrap(c *gc.C) {
442 e := t.Prepare(c)
443 envtesting.UploadFakeTools(c, e.Storage())
444- err := common.EnsureNotBootstrapped(e)
445+ err := bootstrap.EnsureNotBootstrapped(e)
446 c.Assert(err, gc.IsNil)
447 err = bootstrap.Bootstrap(coretesting.Context(c), e, constraints.Value{})
448 c.Assert(err, gc.IsNil)
449@@ -141,12 +140,12 @@
450 c.Check(info.Addrs, gc.Not(gc.HasLen), 0)
451 c.Check(apiInfo.Addrs, gc.Not(gc.HasLen), 0)
452
453- err = common.EnsureNotBootstrapped(e)
454+ err = bootstrap.EnsureNotBootstrapped(e)
455 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
456
457 e2 := t.Open(c)
458 envtesting.UploadFakeTools(c, e2.Storage())
459- err = common.EnsureNotBootstrapped(e2)
460+ err = bootstrap.EnsureNotBootstrapped(e2)
461 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
462
463 info2, apiInfo2, err := e2.StateInfo()
464@@ -160,12 +159,12 @@
465 e3 := t.Prepare(c)
466 envtesting.UploadFakeTools(c, e3.Storage())
467
468- err = common.EnsureNotBootstrapped(e3)
469+ err = bootstrap.EnsureNotBootstrapped(e3)
470 c.Assert(err, gc.IsNil)
471 err = bootstrap.Bootstrap(coretesting.Context(c), e3, constraints.Value{})
472 c.Assert(err, gc.IsNil)
473
474- err = common.EnsureNotBootstrapped(e3)
475+ err = bootstrap.EnsureNotBootstrapped(e3)
476 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
477 }
478
479
480=== modified file 'environs/manual/bootstrap_test.go'
481--- environs/manual/bootstrap_test.go 2014-03-04 16:55:38 +0000
482+++ environs/manual/bootstrap_test.go 2014-03-12 12:33:26 +0000
483@@ -1,7 +1,7 @@
484 // Copyright 2013 Canonical Ltd.
485 // Licensed under the AGPLv3, see LICENCE file for details.
486
487-package manual
488+package manual_test
489
490 import (
491 "fmt"
492@@ -14,6 +14,7 @@
493 "launchpad.net/juju-core/environs/bootstrap"
494 "launchpad.net/juju-core/environs/cloudinit"
495 "launchpad.net/juju-core/environs/filestorage"
496+ "launchpad.net/juju-core/environs/manual"
497 "launchpad.net/juju-core/environs/storage"
498 "launchpad.net/juju-core/environs/tools"
499 "launchpad.net/juju-core/instance"
500@@ -59,13 +60,13 @@
501 s.env.storage = storage
502 }
503
504-func (s *bootstrapSuite) getArgs(c *gc.C) BootstrapArgs {
505+func (s *bootstrapSuite) getArgs(c *gc.C) manual.BootstrapArgs {
506 hostname, err := os.Hostname()
507 c.Assert(err, gc.IsNil)
508 toolsList, err := tools.FindBootstrapTools(s.Conn.Environ, tools.BootstrapToolsParams{})
509 c.Assert(err, gc.IsNil)
510 arch := "amd64"
511- return BootstrapArgs{
512+ return manual.BootstrapArgs{
513 Host: hostname,
514 DataDir: "/var/lib/juju",
515 Environ: s.env,
516@@ -83,7 +84,7 @@
517 args.Host = "ubuntu@" + args.Host
518
519 defer fakeSSH{SkipDetection: true}.install(c).Restore()
520- err := Bootstrap(args)
521+ err := manual.Bootstrap(args)
522 c.Assert(err, gc.IsNil)
523
524 bootstrapState, err := bootstrap.LoadState(s.env.Storage())
525@@ -91,14 +92,14 @@
526 c.Assert(
527 bootstrapState.StateInstances,
528 gc.DeepEquals,
529- []instance.Id{BootstrapInstanceId},
530+ []instance.Id{manual.BootstrapInstanceId},
531 )
532
533 // Do it all again; this should work, despite the fact that
534 // there's a bootstrap state file. Existence for that is
535 // checked in general bootstrap code (environs/bootstrap).
536 defer fakeSSH{SkipDetection: true}.install(c).Restore()
537- err = Bootstrap(args)
538+ err = manual.Bootstrap(args)
539 c.Assert(err, gc.IsNil)
540
541 // We *do* check that the machine has no juju* upstart jobs, though.
542@@ -107,15 +108,15 @@
543 SkipDetection: true,
544 SkipProvisionAgent: true,
545 }.install(c).Restore()
546- err = Bootstrap(args)
547- c.Assert(err, gc.Equals, ErrProvisioned)
548+ err = manual.Bootstrap(args)
549+ c.Assert(err, gc.Equals, manual.ErrProvisioned)
550 }
551
552 func (s *bootstrapSuite) TestBootstrapScriptFailure(c *gc.C) {
553 args := s.getArgs(c)
554 args.Host = "ubuntu@" + args.Host
555 defer fakeSSH{SkipDetection: true, ProvisionAgentExitCode: 1}.install(c).Restore()
556- err := Bootstrap(args)
557+ err := manual.Bootstrap(args)
558 c.Assert(err, gc.NotNil)
559
560 // Since the script failed, the state file should have been
561@@ -127,19 +128,19 @@
562 func (s *bootstrapSuite) TestBootstrapEmptyDataDir(c *gc.C) {
563 args := s.getArgs(c)
564 args.DataDir = ""
565- c.Assert(Bootstrap(args), gc.ErrorMatches, "data-dir argument is empty")
566+ c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "data-dir argument is empty")
567 }
568
569 func (s *bootstrapSuite) TestBootstrapEmptyHost(c *gc.C) {
570 args := s.getArgs(c)
571 args.Host = ""
572- c.Assert(Bootstrap(args), gc.ErrorMatches, "host argument is empty")
573+ c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "host argument is empty")
574 }
575
576 func (s *bootstrapSuite) TestBootstrapNilEnviron(c *gc.C) {
577 args := s.getArgs(c)
578 args.Environ = nil
579- c.Assert(Bootstrap(args), gc.ErrorMatches, "environ argument is nil")
580+ c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "environ argument is nil")
581 }
582
583 func (s *bootstrapSuite) TestBootstrapNoMatchingTools(c *gc.C) {
584@@ -147,13 +148,13 @@
585 args := s.getArgs(c)
586 args.PossibleTools = nil
587 defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore()
588- c.Assert(Bootstrap(args), gc.ErrorMatches, "possible tools is empty")
589+ c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "possible tools is empty")
590
591 // Non-empty list, but none that match the series/arch.
592 args = s.getArgs(c)
593 args.Series = "edgy"
594 defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore()
595- c.Assert(Bootstrap(args), gc.ErrorMatches, "no matching tools available")
596+ c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "no matching tools available")
597 }
598
599 func (s *bootstrapSuite) TestBootstrapToolsFileURL(c *gc.C) {
600@@ -170,13 +171,13 @@
601 }
602
603 func (s *bootstrapSuite) testBootstrapToolsURL(c *gc.C, toolsURL, expectedURL string) {
604- s.PatchValue(&provisionMachineAgent, func(host string, mcfg *cloudinit.MachineConfig, w io.Writer) error {
605+ s.PatchValue(manual.ProvisionMachineAgent, func(host string, mcfg *cloudinit.MachineConfig, w io.Writer) error {
606 c.Assert(mcfg.Tools.URL, gc.Equals, expectedURL)
607 return nil
608 })
609 args := s.getArgs(c)
610 args.PossibleTools[0].URL = toolsURL
611 defer fakeSSH{SkipDetection: true}.install(c).Restore()
612- err := Bootstrap(args)
613+ err := manual.Bootstrap(args)
614 c.Assert(err, gc.IsNil)
615 }
616
617=== modified file 'environs/manual/export_test.go'
618--- environs/manual/export_test.go 2014-01-30 03:18:20 +0000
619+++ environs/manual/export_test.go 2014-03-12 12:33:26 +0000
620@@ -5,4 +5,11 @@
621
622 var (
623 InstanceHostAddresses = &instanceHostAddresses
624+ ProvisionMachineAgent = &provisionMachineAgent
625+ CheckProvisioned = checkProvisioned
626+)
627+
628+const (
629+ DetectionScript = detectionScript
630+ CheckProvisionedScript = checkProvisionedScript
631 )
632
633=== removed file 'environs/manual/fakessh.go'
634--- environs/manual/fakessh.go 2014-01-13 06:25:28 +0000
635+++ environs/manual/fakessh.go 1970-01-01 00:00:00 +0000
636@@ -1,87 +0,0 @@
637-// Copyright 2013 Canonical Ltd.
638-// Licensed under the AGPLv3, see LICENCE file for details.
639-
640-package manual
641-
642-import (
643- "strings"
644-
645- gc "launchpad.net/gocheck"
646-
647- "launchpad.net/juju-core/testing/testbase"
648- sshtesting "launchpad.net/juju-core/utils/ssh/testing"
649-)
650-
651-// installDetectionFakeSSH installs a fake SSH command, which will respond
652-// to the series/hardware detection script with the specified
653-// series/arch.
654-func installDetectionFakeSSH(c *gc.C, series, arch string) testbase.Restorer {
655- if series == "" {
656- series = "precise"
657- }
658- if arch == "" {
659- arch = "amd64"
660- }
661- detectionoutput := strings.Join([]string{
662- series,
663- arch,
664- "MemTotal: 4096 kB",
665- "processor: 0",
666- }, "\n")
667- return sshtesting.InstallFakeSSH(c, detectionScript, detectionoutput, 0)
668-}
669-
670-// FakeSSH wraps the invocation of InstallFakeSSH based on the parameters.
671-type fakeSSH struct {
672- Series string
673- Arch string
674-
675- // Provisioned should be set to true if the fakeSSH script
676- // should respond to checkProvisioned with a non-empty result.
677- Provisioned bool
678-
679- // exit code for the checkProvisioned script.
680- CheckProvisionedExitCode int
681-
682- // exit code for the machine agent provisioning script.
683- ProvisionAgentExitCode int
684-
685- // InitUbuntuUser should be set to true if the fakeSSH script
686- // should respond to an attempt to initialise the ubuntu user.
687- InitUbuntuUser bool
688-
689- // there are conditions other than error in the above
690- // that might cause provisioning to not go ahead, such
691- // as tools being missing.
692- SkipProvisionAgent bool
693-
694- // detection will be skipped if the series/hardware were
695- // detected ahead of time. This should always be set to
696- // true when testing Bootstrap.
697- SkipDetection bool
698-}
699-
700-// install installs fake SSH commands, which will respond to
701-// manual provisioning/bootstrapping commands with the specified
702-// output and exit codes.
703-func (r fakeSSH) install(c *gc.C) testbase.Restorer {
704- var restore testbase.Restorer
705- add := func(input, output interface{}, rc int) {
706- restore = restore.Add(sshtesting.InstallFakeSSH(c, input, output, rc))
707- }
708- if !r.SkipProvisionAgent {
709- add(nil, nil, r.ProvisionAgentExitCode)
710- }
711- if !r.SkipDetection {
712- restore.Add(installDetectionFakeSSH(c, r.Series, r.Arch))
713- }
714- var checkProvisionedOutput interface{}
715- if r.Provisioned {
716- checkProvisionedOutput = "/etc/init/jujud-machine-0.conf"
717- }
718- add(checkProvisionedScript, checkProvisionedOutput, r.CheckProvisionedExitCode)
719- if r.InitUbuntuUser {
720- add("", nil, 0)
721- }
722- return restore
723-}
724
725=== added file 'environs/manual/fakessh_test.go'
726--- environs/manual/fakessh_test.go 1970-01-01 00:00:00 +0000
727+++ environs/manual/fakessh_test.go 2014-03-12 12:33:26 +0000
728@@ -0,0 +1,156 @@
729+// Copyright 2013 Canonical Ltd.
730+// Licensed under the AGPLv3, see LICENCE file for details.
731+
732+package manual_test
733+
734+import (
735+ "fmt"
736+ "io/ioutil"
737+ "path/filepath"
738+ "strings"
739+
740+ gc "launchpad.net/gocheck"
741+
742+ "launchpad.net/juju-core/environs/manual"
743+ "launchpad.net/juju-core/testing/testbase"
744+)
745+
746+// sshscript should only print the result on the first execution,
747+// to handle the case where it's called multiple times. On
748+// subsequent executions, it should find the next 'ssh' in $PATH
749+// and exec that.
750+var sshscript = `#!/bin/bash --norc
751+if [ ! -e "$0.run" ]; then
752+ touch "$0.run"
753+ if [ -e "$0.expected-input" ]; then
754+ diff "$0.expected-input" -
755+ exitcode=$?
756+ if [ $exitcode -ne 0 ]; then
757+ echo "ERROR: did not match expected input" >&2
758+ exit $exitcode
759+ fi
760+ else
761+ head >/dev/null
762+ fi
763+ # stdout
764+ %s
765+ # stderr
766+ %s
767+ exit %d
768+else
769+ export PATH=${PATH#*:}
770+ exec ssh $*
771+fi`
772+
773+// installFakeSSH creates a fake "ssh" command in a new $PATH,
774+// updates $PATH, and returns a function to reset $PATH to its
775+// original value when called.
776+//
777+// input may be:
778+// - nil (ignore input)
779+// - a string (match input exactly)
780+// output may be:
781+// - nil (no output)
782+// - a string (stdout)
783+// - a slice of strings, of length two (stdout, stderr)
784+func installFakeSSH(c *gc.C, input, output interface{}, rc int) testbase.Restorer {
785+ fakebin := c.MkDir()
786+ ssh := filepath.Join(fakebin, "ssh")
787+ switch input := input.(type) {
788+ case nil:
789+ case string:
790+ sshexpectedinput := ssh + ".expected-input"
791+ err := ioutil.WriteFile(sshexpectedinput, []byte(input), 0644)
792+ c.Assert(err, gc.IsNil)
793+ default:
794+ c.Errorf("input has invalid type: %T", input)
795+ }
796+ var stdout, stderr string
797+ switch output := output.(type) {
798+ case nil:
799+ case string:
800+ stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output)
801+ case []string:
802+ c.Assert(output, gc.HasLen, 2)
803+ stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output[0])
804+ stderr = fmt.Sprintf("cat>&2<<EOF\n%s\nEOF", output[1])
805+ }
806+ script := fmt.Sprintf(sshscript, stdout, stderr, rc)
807+ err := ioutil.WriteFile(ssh, []byte(script), 0777)
808+ c.Assert(err, gc.IsNil)
809+ return testbase.PatchEnvPathPrepend(fakebin)
810+}
811+
812+// installDetectionFakeSSH installs a fake SSH command, which will respond
813+// to the series/hardware detection script with the specified
814+// series/arch.
815+func installDetectionFakeSSH(c *gc.C, series, arch string) testbase.Restorer {
816+ if series == "" {
817+ series = "precise"
818+ }
819+ if arch == "" {
820+ arch = "amd64"
821+ }
822+ detectionoutput := strings.Join([]string{
823+ series,
824+ arch,
825+ "MemTotal: 4096 kB",
826+ "processor: 0",
827+ }, "\n")
828+ return installFakeSSH(c, manual.DetectionScript, detectionoutput, 0)
829+}
830+
831+// fakeSSH wraps the invocation of InstallFakeSSH based on the parameters.
832+type fakeSSH struct {
833+ Series string
834+ Arch string
835+
836+ // Provisioned should be set to true if the fakeSSH script
837+ // should respond to checkProvisioned with a non-empty result.
838+ Provisioned bool
839+
840+ // exit code for the checkProvisioned script.
841+ CheckProvisionedExitCode int
842+
843+ // exit code for the machine agent provisioning script.
844+ ProvisionAgentExitCode int
845+
846+ // InitUbuntuUser should be set to true if the fakeSSH script
847+ // should respond to an attempt to initialise the ubuntu user.
848+ InitUbuntuUser bool
849+
850+ // there are conditions other than error in the above
851+ // that might cause provisioning to not go ahead, such
852+ // as tools being missing.
853+ SkipProvisionAgent bool
854+
855+ // detection will be skipped if the series/hardware were
856+ // detected ahead of time. This should always be set to
857+ // true when testing Bootstrap.
858+ SkipDetection bool
859+}
860+
861+// install installs fake SSH commands, which will respond to
862+// manual provisioning/bootstrapping commands with the specified
863+// output and exit codes.
864+func (r fakeSSH) install(c *gc.C) testbase.Restorer {
865+ var restore testbase.Restorer
866+ add := func(input, output interface{}, rc int) {
867+ restore = restore.Add(installFakeSSH(c, input, output, rc))
868+ }
869+ if !r.SkipProvisionAgent {
870+ add(nil, nil, r.ProvisionAgentExitCode)
871+ }
872+ if !r.SkipDetection {
873+ restore.Add(installDetectionFakeSSH(c, r.Series, r.Arch))
874+ }
875+ var checkProvisionedOutput interface{}
876+ if r.Provisioned {
877+ checkProvisionedOutput = "/etc/init/jujud-machine-0.conf"
878+ }
879+ add(manual.CheckProvisionedScript, checkProvisionedOutput, r.CheckProvisionedExitCode)
880+ if r.InitUbuntuUser {
881+ add("", nil, 0)
882+ }
883+ return restore
884+}
885
886=== modified file 'environs/manual/init.go'
887--- environs/manual/init.go 2014-03-11 05:55:35 +0000
888+++ environs/manual/init.go 2014-03-12 12:33:26 +0000
889@@ -16,6 +16,15 @@
890 "launchpad.net/juju-core/utils/ssh"
891 )
892
893+// detectionScript is the script to run on the remote machine to
894+// detect the OS series and hardware characteristics.
895+const detectionScript = `#!/bin/bash
896+set -e
897+lsb_release -cs
898+uname -m
899+grep MemTotal /proc/meminfo
900+cat /proc/cpuinfo`
901+
902 // checkProvisionedScript is the script to run on the remote machine
903 // to check if a machine has already been provisioned.
904 //
905@@ -132,13 +141,6 @@
906 {regexp.MustCompile("ppc64el|ppc64le"), "ppc64"},
907 }
908
909-const detectionScript = `#!/bin/bash
910-set -e
911-lsb_release -cs
912-uname -m
913-grep MemTotal /proc/meminfo
914-cat /proc/cpuinfo`
915-
916 // InitUbuntuUser adds the ubuntu user if it doesn't
917 // already exist, updates its ~/.ssh/authorized_keys,
918 // and enables passwordless sudo for it.
919
920=== modified file 'environs/manual/init_test.go'
921--- environs/manual/init_test.go 2014-02-07 13:58:06 +0000
922+++ environs/manual/init_test.go 2014-03-12 12:33:26 +0000
923@@ -1,16 +1,16 @@
924 // Copyright 2013 Canonical Ltd.
925 // Licensed under the AGPLv3, see LICENCE file for details.
926
927-package manual
928+package manual_test
929
930 import (
931 "strings"
932
933 gc "launchpad.net/gocheck"
934
935+ "launchpad.net/juju-core/environs/manual"
936 jc "launchpad.net/juju-core/testing/checkers"
937 "launchpad.net/juju-core/testing/testbase"
938- sshtesting "launchpad.net/juju-core/utils/ssh/testing"
939 )
940
941 type initialisationSuite struct {
942@@ -26,8 +26,8 @@
943 "MemTotal: 4096 kB",
944 "processor: 0",
945 }, "\n")
946- defer sshtesting.InstallFakeSSH(c, detectionScript, response, 0)()
947- _, series, err := DetectSeriesAndHardwareCharacteristics("whatever")
948+ defer installFakeSSH(c, manual.DetectionScript, response, 0)()
949+ _, series, err := manual.DetectSeriesAndHardwareCharacteristics("whatever")
950 c.Assert(err, gc.IsNil)
951 c.Assert(series, gc.Equals, "edgy")
952 }
953@@ -41,12 +41,12 @@
954 }, "\n")
955 // if the script fails for whatever reason, then checkProvisioned
956 // will return an error. stderr will be included in the error message.
957- defer sshtesting.InstallFakeSSH(c, detectionScript, []string{scriptResponse, "oh noes"}, 33)()
958- hc, _, err := DetectSeriesAndHardwareCharacteristics("hostname")
959+ defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "oh noes"}, 33)()
960+ hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname")
961 c.Assert(err, gc.ErrorMatches, "rc: 33 \\(oh noes\\)")
962 // if the script doesn't fail, stderr is simply ignored.
963- defer sshtesting.InstallFakeSSH(c, detectionScript, []string{scriptResponse, "non-empty-stderr"}, 0)()
964- hc, _, err = DetectSeriesAndHardwareCharacteristics("hostname")
965+ defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "non-empty-stderr"}, 0)()
966+ hc, _, err = manual.DetectSeriesAndHardwareCharacteristics("hostname")
967 c.Assert(err, gc.IsNil)
968 c.Assert(hc.String(), gc.Equals, "arch=arm cpu-cores=1 mem=4M")
969 }
970@@ -106,52 +106,52 @@
971 for i, test := range tests {
972 c.Logf("test %d: %s", i, test.summary)
973 scriptResponse := strings.Join(test.scriptResponse, "\n")
974- defer sshtesting.InstallFakeSSH(c, detectionScript, scriptResponse, 0)()
975- hc, _, err := DetectSeriesAndHardwareCharacteristics("hostname")
976+ defer installFakeSSH(c, manual.DetectionScript, scriptResponse, 0)()
977+ hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname")
978 c.Assert(err, gc.IsNil)
979 c.Assert(hc.String(), gc.Equals, test.expectedHc)
980 }
981 }
982
983 func (s *initialisationSuite) TestCheckProvisioned(c *gc.C) {
984- defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, "", 0)()
985- provisioned, err := checkProvisioned("example.com")
986+ defer installFakeSSH(c, manual.CheckProvisionedScript, "", 0)()
987+ provisioned, err := manual.CheckProvisioned("example.com")
988 c.Assert(err, gc.IsNil)
989 c.Assert(provisioned, jc.IsFalse)
990
991- defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, "non-empty", 0)()
992- provisioned, err = checkProvisioned("example.com")
993+ defer installFakeSSH(c, manual.CheckProvisionedScript, "non-empty", 0)()
994+ provisioned, err = manual.CheckProvisioned("example.com")
995 c.Assert(err, gc.IsNil)
996 c.Assert(provisioned, jc.IsTrue)
997
998 // stderr should not affect result.
999- defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, []string{"", "non-empty-stderr"}, 0)()
1000- provisioned, err = checkProvisioned("example.com")
1001+ defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"", "non-empty-stderr"}, 0)()
1002+ provisioned, err = manual.CheckProvisioned("example.com")
1003 c.Assert(err, gc.IsNil)
1004 c.Assert(provisioned, jc.IsFalse)
1005
1006 // if the script fails for whatever reason, then checkProvisioned
1007 // will return an error. stderr will be included in the error message.
1008- defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, []string{"non-empty-stdout", "non-empty-stderr"}, 255)()
1009- _, err = checkProvisioned("example.com")
1010+ defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"non-empty-stdout", "non-empty-stderr"}, 255)()
1011+ _, err = manual.CheckProvisioned("example.com")
1012 c.Assert(err, gc.ErrorMatches, "rc: 255 \\(non-empty-stderr\\)")
1013 }
1014
1015 func (s *initialisationSuite) TestInitUbuntuUserNonExisting(c *gc.C) {
1016- defer sshtesting.InstallFakeSSH(c, "", "", 0)() // successful creation of ubuntu user
1017- defer sshtesting.InstallFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
1018- err := InitUbuntuUser("testhost", "testuser", "", nil, nil)
1019+ defer installFakeSSH(c, "", "", 0)() // successful creation of ubuntu user
1020+ defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
1021+ err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
1022 c.Assert(err, gc.IsNil)
1023 }
1024
1025 func (s *initialisationSuite) TestInitUbuntuUserExisting(c *gc.C) {
1026- defer sshtesting.InstallFakeSSH(c, "", nil, 0)()
1027- InitUbuntuUser("testhost", "testuser", "", nil, nil)
1028+ defer installFakeSSH(c, "", nil, 0)()
1029+ manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
1030 }
1031
1032 func (s *initialisationSuite) TestInitUbuntuUserError(c *gc.C) {
1033- defer sshtesting.InstallFakeSSH(c, "", []string{"", "failed to create ubuntu user"}, 123)()
1034- defer sshtesting.InstallFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
1035- err := InitUbuntuUser("testhost", "testuser", "", nil, nil)
1036+ defer installFakeSSH(c, "", []string{"", "failed to create ubuntu user"}, 123)()
1037+ defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
1038+ err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
1039 c.Assert(err, gc.ErrorMatches, "rc: 123 \\(failed to create ubuntu user\\)")
1040 }
1041
1042=== modified file 'environs/manual/provisioner_test.go'
1043--- environs/manual/provisioner_test.go 2014-02-07 13:58:06 +0000
1044+++ environs/manual/provisioner_test.go 2014-03-12 12:33:26 +0000
1045@@ -1,7 +1,7 @@
1046 // Copyright 2013 Canonical Ltd.
1047 // Licensed under the AGPLv3, see LICENCE file for details.
1048
1049-package manual
1050+package manual_test
1051
1052 import (
1053 "fmt"
1054@@ -9,6 +9,7 @@
1055
1056 gc "launchpad.net/gocheck"
1057
1058+ "launchpad.net/juju-core/environs/manual"
1059 envtesting "launchpad.net/juju-core/environs/testing"
1060 "launchpad.net/juju-core/instance"
1061 "launchpad.net/juju-core/juju/testing"
1062@@ -25,10 +26,10 @@
1063
1064 var _ = gc.Suite(&provisionerSuite{})
1065
1066-func (s *provisionerSuite) getArgs(c *gc.C) ProvisionMachineArgs {
1067+func (s *provisionerSuite) getArgs(c *gc.C) manual.ProvisionMachineArgs {
1068 hostname, err := os.Hostname()
1069 c.Assert(err, gc.IsNil)
1070- return ProvisionMachineArgs{
1071+ return manual.ProvisionMachineArgs{
1072 Host: hostname,
1073 EnvName: "dummyenv",
1074 }
1075@@ -50,7 +51,7 @@
1076 SkipProvisionAgent: true,
1077 }.install(c).Restore()
1078 // Attempt to provision a machine with no tools available, expect it to fail.
1079- machineId, err := ProvisionMachine(args)
1080+ machineId, err := manual.ProvisionMachine(args)
1081 c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
1082 c.Assert(machineId, gc.Equals, "")
1083
1084@@ -68,7 +69,7 @@
1085 InitUbuntuUser: true,
1086 ProvisionAgentExitCode: errorCode,
1087 }.install(c).Restore()
1088- machineId, err = ProvisionMachine(args)
1089+ machineId, err = manual.ProvisionMachine(args)
1090 if errorCode != 0 {
1091 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("rc: %d", errorCode))
1092 c.Assert(machineId, gc.Equals, "")
1093@@ -94,8 +95,8 @@
1094 SkipDetection: true,
1095 SkipProvisionAgent: true,
1096 }.install(c).Restore()
1097- _, err = ProvisionMachine(args)
1098- c.Assert(err, gc.Equals, ErrProvisioned)
1099+ _, err = manual.ProvisionMachine(args)
1100+ c.Assert(err, gc.Equals, manual.ErrProvisioned)
1101 defer fakeSSH{
1102 Provisioned: true,
1103 CheckProvisionedExitCode: 255,
1104@@ -103,7 +104,7 @@
1105 SkipDetection: true,
1106 SkipProvisionAgent: true,
1107 }.install(c).Restore()
1108- _, err = ProvisionMachine(args)
1109+ _, err = manual.ProvisionMachine(args)
1110 c.Assert(err, gc.ErrorMatches, "error checking if provisioned: rc: 255")
1111 }
1112
1113@@ -115,7 +116,7 @@
1114 Arch: arch,
1115 InitUbuntuUser: true,
1116 }.install(c).Restore()
1117- machineId, err := ProvisionMachine(s.getArgs(c))
1118+ machineId, err := manual.ProvisionMachine(s.getArgs(c))
1119 c.Assert(err, gc.IsNil)
1120
1121 // Now check what we would've configured it with.
1122
1123=== added directory 'environs/manual/testing'
1124=== modified file 'environs/open.go'
1125--- environs/open.go 2014-02-18 01:29:47 +0000
1126+++ environs/open.go 2014-03-12 12:33:26 +0000
1127@@ -46,16 +46,14 @@
1128 ConfigFromEnvirons
1129 )
1130
1131-// ConfigForName returns the configuration for the environment with the
1132-// given name from the default environments file. If the name is blank,
1133-// the default environment will be used. If the configuration is not
1134-// found, an errors.NotFoundError is returned.
1135-// If the given store contains an entry for the environment
1136-// and it has associated bootstrap config, that configuration
1137-// will be returned.
1138-// ConfigForName also returns where the configuration
1139-// was sourced from (this is also valid even when there
1140-// is an error.
1141+// ConfigForName returns the configuration for the environment with
1142+// the given name from the default environments file. If the name is
1143+// blank, the default environment will be used. If the configuration
1144+// is not found, an errors.NotFoundError is returned. If the given
1145+// store contains an entry for the environment and it has associated
1146+// bootstrap config, that configuration will be returned.
1147+// ConfigForName also returns where the configuration was sourced from
1148+// (this is also valid even when there is an error.
1149 func ConfigForName(name string, store configstore.Storage) (*config.Config, ConfigSource, error) {
1150 envs, err := ReadEnvirons("")
1151 if err != nil {
1152@@ -86,6 +84,16 @@
1153 return cfg, ConfigFromEnvirons, err
1154 }
1155
1156+// maybeNotBootstrapped takes an error and source, returned by
1157+// ConfigForName and returns ErrNotBootstrapped if it looks like the
1158+// environment is not bootstrapped, or err as-is otherwise.
1159+func maybeNotBootstrapped(err error, source ConfigSource) error {
1160+ if err != nil && source == ConfigFromEnvirons {
1161+ return ErrNotBootstrapped
1162+ }
1163+ return err
1164+}
1165+
1166 // NewFromName opens the environment with the given
1167 // name from the default environments file. If the
1168 // name is blank, the default environment will be used.
1169@@ -99,16 +107,16 @@
1170 // configuration attributes don't exist which
1171 // will be filled in by Prepare.
1172 cfg, source, err := ConfigForName(name, store)
1173- if err != nil && source == ConfigFromEnvirons {
1174- err = ErrNotBootstrapped
1175+ if err := maybeNotBootstrapped(err, source); err != nil {
1176+ return nil, err
1177 }
1178 if err != nil {
1179 return nil, err
1180 }
1181
1182 env, err := New(cfg)
1183- if err != nil && source == ConfigFromEnvirons {
1184- err = ErrNotBootstrapped
1185+ if err := maybeNotBootstrapped(err, source); err != nil {
1186+ return nil, err
1187 }
1188 return env, err
1189 }
1190
1191=== modified file 'environs/sshstorage/storage.go'
1192--- environs/sshstorage/storage.go 2014-01-13 06:25:28 +0000
1193+++ environs/sshstorage/storage.go 2014-03-12 12:33:26 +0000
1194@@ -16,11 +16,15 @@
1195 "strconv"
1196 "strings"
1197
1198+ "github.com/juju/loggo"
1199+
1200 coreerrors "launchpad.net/juju-core/errors"
1201 "launchpad.net/juju-core/utils"
1202 "launchpad.net/juju-core/utils/ssh"
1203 )
1204
1205+var logger = loggo.GetLogger("juju.environs.sshstorage")
1206+
1207 // base64LineLength is the default line length for wrapping
1208 // output generated by the base64 command line utility.
1209 const base64LineLength = 76
1210@@ -240,6 +244,7 @@
1211
1212 // Get implements storage.StorageReader.Get.
1213 func (s *SSHStorage) Get(name string) (io.ReadCloser, error) {
1214+ logger.Debugf("getting %q from storage", name)
1215 path, err := s.path(name)
1216 if err != nil {
1217 return nil, err
1218@@ -305,6 +310,7 @@
1219
1220 // Put implements storage.StorageWriter.Put
1221 func (s *SSHStorage) Put(name string, r io.Reader, length int64) error {
1222+ logger.Debugf("putting %q (len %d) to storage", name, length)
1223 path, err := s.path(name)
1224 if err != nil {
1225 return err
1226
1227=== modified file 'environs/testing/storage.go'
1228--- environs/testing/storage.go 2014-03-04 16:55:38 +0000
1229+++ environs/testing/storage.go 2014-03-12 12:33:26 +0000
1230@@ -4,16 +4,13 @@
1231 package testing
1232
1233 import (
1234- "fmt"
1235 "io"
1236
1237 gc "launchpad.net/gocheck"
1238
1239- "launchpad.net/juju-core/environs"
1240 "launchpad.net/juju-core/environs/filestorage"
1241 "launchpad.net/juju-core/environs/httpstorage"
1242 "launchpad.net/juju-core/environs/storage"
1243- "launchpad.net/juju-core/state"
1244 )
1245
1246 // CreateLocalTestStorage returns the listener, which needs to be closed, and
1247@@ -29,17 +26,3 @@
1248 closer = listener
1249 return
1250 }
1251-
1252-// GetEnvironStorage creates an Environ from the config in state and
1253-// returns its storage interface.
1254-func GetEnvironStorage(st *state.State) (storage.Storage, error) {
1255- envConfig, err := st.EnvironConfig()
1256- if err != nil {
1257- return nil, fmt.Errorf("cannot get environment config: %v", err)
1258- }
1259- env, err := environs.New(envConfig)
1260- if err != nil {
1261- return nil, fmt.Errorf("cannot access environment: %v", err)
1262- }
1263- return env.Storage(), nil
1264-}
1265
1266=== added file 'environs/utils.go'
1267--- environs/utils.go 1970-01-01 00:00:00 +0000
1268+++ environs/utils.go 2014-03-12 12:33:26 +0000
1269@@ -0,0 +1,22 @@
1270+package environs
1271+
1272+import (
1273+ "fmt"
1274+
1275+ "launchpad.net/juju-core/environs/storage"
1276+ "launchpad.net/juju-core/state"
1277+)
1278+
1279+// GetStorage creates an Environ from the config in state and returns
1280+// its storage interface.
1281+func GetStorage(st *state.State) (storage.Storage, error) {
1282+ envConfig, err := st.EnvironConfig()
1283+ if err != nil {
1284+ return nil, fmt.Errorf("cannot get environment config: %v", err)
1285+ }
1286+ env, err := New(envConfig)
1287+ if err != nil {
1288+ return nil, fmt.Errorf("cannot access environment: %v", err)
1289+ }
1290+ return env.Storage(), nil
1291+}
1292
1293=== modified file 'juju/conn_test.go'
1294--- juju/conn_test.go 2014-03-07 14:15:28 +0000
1295+++ juju/conn_test.go 2014-03-12 12:33:26 +0000
1296@@ -124,7 +124,7 @@
1297 c.Assert(conn.Environ.Name(), gc.Equals, envName)
1298 }
1299
1300-func (cs *NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) {
1301+func (*NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) {
1302 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{
1303 "admin-secret": "side-effect secret",
1304 "secret": "pork",
1305@@ -175,7 +175,7 @@
1306 c.Assert(err, gc.IsNil)
1307 }
1308
1309-func (cs *NewConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *gc.C) {
1310+func (*NewConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *gc.C) {
1311 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{
1312 "secret": "pork",
1313 })
1314@@ -212,7 +212,7 @@
1315 c.Assert(err, gc.IsNil)
1316 }
1317
1318-func (cs *NewConnSuite) TestConnWithPassword(c *gc.C) {
1319+func (*NewConnSuite) TestConnWithPassword(c *gc.C) {
1320 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{
1321 "admin-secret": "nutkin",
1322 })
1323
1324=== modified file 'provider/azure/config.go'
1325--- provider/azure/config.go 2014-01-29 00:14:51 +0000
1326+++ provider/azure/config.go 2014-03-12 12:33:26 +0000
1327@@ -95,32 +95,40 @@
1328 return cfg.Apply(envCfg.attrs)
1329 }
1330
1331-const boilerplateYAML = `
1332+var boilerplateYAML = `
1333 # https://juju.ubuntu.com/docs/config-azure.html
1334 azure:
1335 type: azure
1336
1337- # location specifies the place where instances will be started, for
1338- # example: West US, North Europe.
1339+ # location specifies the place where instances will be started,
1340+ # for example: West US, North Europe.
1341+ #
1342 location: West US
1343-
1344- # The following attributes specify Windows Azure Management information.
1345- # See http://msdn.microsoft.com/en-us/library/windowsazure
1346+
1347+ # The following attributes specify Windows Azure Management
1348+ # information. See:
1349+ # http://msdn.microsoft.com/en-us/library/windowsazure
1350 # for details.
1351+ #
1352 management-subscription-id: <00000000-0000-0000-0000-000000000000>
1353 management-certificate-path: /home/me/azure.pem
1354
1355 # storage-account-name holds Windows Azure Storage info.
1356+ #
1357 storage-account-name: abcdefghijkl
1358
1359- # force-image-name overrides the OS image selection to use
1360- # a fixed image for all deployments. Most useful for developers.
1361+ # force-image-name overrides the OS image selection to use a fixed
1362+ # image for all deployments. Most useful for developers.
1363+ #
1364 # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB
1365
1366- # image-stream chooses a simplestreams stream to select OS images from,
1367- # for example daily or released images (or any other stream available on simplestreams).
1368+ # image-stream chooses a simplestreams stream to select OS images
1369+ # from, for example daily or released images (or any other stream
1370+ # available on simplestreams).
1371+ #
1372 # image-stream: "released"
1373-`
1374+
1375+`[1:]
1376
1377 func (prov azureEnvironProvider) BoilerplateConfig() string {
1378 return boilerplateYAML
1379
1380=== modified file 'provider/common/bootstrap.go'
1381--- provider/common/bootstrap.go 2014-03-05 19:41:34 +0000
1382+++ provider/common/bootstrap.go 2014-03-12 12:33:26 +0000
1383@@ -405,18 +405,3 @@
1384 }
1385 return bootstrap.SetBootstrapTools(env, possibleTools)
1386 }
1387-
1388-// EnsureNotBootstrapped returns null if the environment is not bootstrapped,
1389-// and an error if it is or if the function was not able to tell.
1390-func EnsureNotBootstrapped(env environs.Environ) error {
1391- _, err := bootstrap.LoadState(env.Storage())
1392- // If there is no error loading the bootstrap state, then we are
1393- // bootstrapped.
1394- if err == nil {
1395- return fmt.Errorf("environment is already bootstrapped")
1396- }
1397- if err == environs.ErrNotBootstrapped {
1398- return nil
1399- }
1400- return err
1401-}
1402
1403=== modified file 'provider/ec2/ec2.go'
1404--- provider/ec2/ec2.go 2014-03-11 07:21:10 +0000
1405+++ provider/ec2/ec2.go 2014-03-12 12:33:26 +0000
1406@@ -179,18 +179,25 @@
1407 # https://juju.ubuntu.com/docs/config-aws.html
1408 amazon:
1409 type: ec2
1410- # region specifies the ec2 region. It defaults to us-east-1.
1411+
1412+ # region specifies the EC2 region. It defaults to us-east-1.
1413+ #
1414 # region: us-east-1
1415+
1416+ # access-key holds the EC2 access key. It defaults to the
1417+ # environment variable AWS_ACCESS_KEY_ID.
1418 #
1419- # access-key holds the ec2 access key. It defaults to the environment
1420- # variable AWS_ACCESS_KEY_ID.
1421 # access-key: <secret>
1422- #
1423- # secret-key holds the ec2 secret key. It defaults to the environment
1424- # variable AWS_SECRET_ACCESS_KEY.
1425- #
1426- # image-stream chooses a simplestreams stream to select OS images from,
1427- # for example daily or released images (or any other stream available on simplestreams).
1428+
1429+ # secret-key holds the EC2 secret key. It defaults to the
1430+ # environment variable AWS_SECRET_ACCESS_KEY.
1431+ #
1432+ # secret-key: <secret>
1433+
1434+ # image-stream chooses a simplestreams stream to select OS images
1435+ # from, for example daily or released images (or any other stream
1436+ # available on simplestreams).
1437+ #
1438 # image-stream: "released"
1439
1440 `[1:]
1441
1442=== modified file 'provider/joyent/config.go'
1443--- provider/joyent/config.go 2014-03-10 00:10:29 +0000
1444+++ provider/joyent/config.go 2014-03-12 12:33:26 +0000
1445@@ -14,25 +14,46 @@
1446
1447 // boilerplateConfig will be shown in help output, so please keep it up to
1448 // date when you change environment configuration below.
1449-const boilerplateConfig = `joyent:
1450- type: joyent
1451-
1452- # SDC config
1453- # Can be set via env variables, or specified here
1454- # sdc-user: <secret>
1455- # Can be set via env variables, or specified here
1456- # sdc-key-id: <secret>
1457- # region defaults to us-west-1, override if required
1458- # sdc-region: us-west-1
1459-
1460- # Manta config
1461- # Can be set via env variables, or specified here
1462- # manta-user: <secret>
1463- # Can be set via env variables, or specified here
1464- # manta-key-id: <secret>
1465- # region defaults to us-east, override if required
1466- # manta-region: us-east
1467-`
1468+var boilerplateConfig = `
1469+joyent:
1470+ type: joyent
1471+
1472+ ## SDC config
1473+
1474+ # sdc-user holds the SDC user. It can also be specified in the
1475+ # SDC_ACCOUNT environment variable.
1476+ #
1477+ # sdc-user: <secret>
1478+
1479+ # sdc-key-id holds the fingerprint of one of user's SSH keys. It
1480+ # can also be specified in the SDC_KEY_ID environment variable.
1481+ #
1482+ # sdc-key-id: <secret>
1483+
1484+ # sdc-region holds the region to use: us-west-1 (default),
1485+ # us-east-1, us-sw-1, eu-ams-1. Override if required.
1486+ #
1487+ # sdc-region: us-west-1
1488+
1489+ ## Manta config
1490+
1491+ # manta-user holds the user's Manta account name. It can also be
1492+ # specified in the MANTA_USER environment variable.
1493+ #
1494+ # manta-user: <secret>
1495+
1496+ # manta-key-id holds the fingerprint of one of the user's SSH
1497+ # keys. It can also be specified in the MANTA_KEY_ID environment
1498+ # variable.
1499+ #
1500+ # manta-key-id: <secret>
1501+
1502+ # manta-region holds the Manta region to use. It defaults to
1503+ # us-east, override if required
1504+ #
1505+ # manta-region: us-east
1506+
1507+`[1:]
1508
1509 const (
1510 SdcAccount = "SDC_ACCOUNT"
1511
1512=== modified file 'provider/local/environprovider.go'
1513--- provider/local/environprovider.go 2014-03-09 20:48:29 +0000
1514+++ provider/local/environprovider.go 2014-03-12 12:33:26 +0000
1515@@ -230,17 +230,23 @@
1516 # https://juju.ubuntu.com/docs/config-local.html
1517 local:
1518 type: local
1519- # Override the directory that is used for the storage files and database.
1520- # The default location is $JUJU_HOME/<ENV>.
1521-
1522- # $JUJU_HOME defaults to ~/.juju
1523+
1524+ # root-dir holds the directory that is used for the storage files and
1525+ # database. The default location is $JUJU_HOME/<env-name>.
1526+ # $JUJU_HOME defaults to ~/.juju. Override if needed.
1527+ #
1528 # root-dir: ~/.juju/local
1529-
1530- # Override the storage port if you have multiple local providers, or if the
1531- # default port is used by another program.
1532+
1533+ # storage-port holds the port where the local provider starts the
1534+ # HTTP file server. Override the value if you have multiple local
1535+ # providers, or if the default port is used by another program.
1536+ #
1537 # storage-port: 8040
1538-
1539- # Override the network bridge if you have changed the default lxc bridge
1540+
1541+ # network-bridge holds the name of the LXC network bridge to use.
1542+ # Override if the default LXC network bridge is different.
1543+ #
1544+ #
1545 # network-bridge: lxcbr0
1546
1547 `[1:]
1548
1549=== modified file 'provider/maas/environprovider.go'
1550--- provider/maas/environprovider.go 2014-03-05 19:41:34 +0000
1551+++ provider/maas/environprovider.go 2014-03-12 12:33:26 +0000
1552@@ -64,12 +64,14 @@
1553 # https://juju.ubuntu.com/docs/config-maas.html
1554 maas:
1555 type: maas
1556-
1557+
1558 # maas-server specifies the location of the MAAS server. It must
1559 # specify the base path.
1560+ #
1561 maas-server: 'http://192.168.1.1/MAAS/'
1562-
1563+
1564 # maas-oauth holds the OAuth credentials from MAAS.
1565+ #
1566 maas-oauth: '<add your OAuth credentials from MAAS here>'
1567
1568 `[1:]
1569
1570=== modified file 'provider/manual/environ.go'
1571--- provider/manual/environ.go 2014-03-05 19:41:34 +0000
1572+++ provider/manual/environ.go 2014-03-12 12:33:26 +0000
1573@@ -188,6 +188,7 @@
1574 }
1575
1576 var newSSHStorage = func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) {
1577+ logger.Debugf("using ssh storage at host %q dir %q", sshHost, storageDir)
1578 return sshstorage.NewSSHStorage(sshstorage.NewSSHStorageParams{
1579 Host: sshHost,
1580 StorageDir: storageDir,
1581
1582=== modified file 'provider/openstack/provider.go'
1583--- provider/openstack/provider.go 2014-03-11 07:21:10 +0000
1584+++ provider/openstack/provider.go 2014-03-12 12:33:26 +0000
1585@@ -67,59 +67,75 @@
1586 # https://juju.ubuntu.com/docs/config-openstack.html
1587 openstack:
1588 type: openstack
1589- # use-floating-ip specifies whether a floating IP address is required
1590- # to give the nodes a public IP address. Some installations assign public IP
1591- # addresses by default without requiring a floating IP address.
1592+
1593+ # use-floating-ip specifies whether a floating IP address is
1594+ # required to give the nodes a public IP address. Some
1595+ # installations assign public IP addresses by default without
1596+ # requiring a floating IP address.
1597+ #
1598 # use-floating-ip: false
1599
1600- # use-default-secgroup specifies whether new machine instances should have the "default"
1601- # Openstack security group assigned.
1602+ # use-default-secgroup specifies whether new machine instances
1603+ # should have the "default" Openstack security group assigned.
1604+ #
1605 # use-default-secgroup: false
1606
1607- # network specifies the network label or uuid to bring machines up on, in
1608- # the case where multiple networks exist. It may be omitted otherwise.
1609+ # network specifies the network label or uuid to bring machines up
1610+ # on, in the case where multiple networks exist. It may be omitted
1611+ # otherwise.
1612+ #
1613 # network: <your network label or uuid>
1614
1615- # tools-metadata-url specifies the location of the Juju tools and metadata. It defaults to the
1616- # global public tools metadata location https://streams.canonical.com/tools.
1617- # tools-metadata-url: https://you-tools-metadata-url
1618-
1619- # image-metadata-url specifies the location of Ubuntu cloud image metadata. It defaults to the
1620- # global public image metadata location https://cloud-images.ubuntu.com/releases.
1621- # image-metadata-url: https://you-tools-metadata-url
1622-
1623- # image-stream chooses a simplestreams stream to select OS images from,
1624- # for example daily or released images (or any other stream available on simplestreams).
1625+ # tools-metadata-url specifies the location of the Juju tools and
1626+ # metadata. It defaults to the global public tools metadata
1627+ # location https://streams.canonical.com/tools.
1628+ #
1629+ # tools-metadata-url: https://your-tools-metadata-url
1630+
1631+ # image-metadata-url specifies the location of Ubuntu cloud image
1632+ # metadata. It defaults to the global public image metadata
1633+ # location https://cloud-images.ubuntu.com/releases.
1634+ #
1635+ # image-metadata-url: https://your-tools-metadata-url
1636+
1637+ # image-stream chooses a simplestreams stream to select OS images
1638+ # from, for example daily or released images (or any other stream
1639+ # available on simplestreams).
1640+ #
1641 # image-stream: "released"
1642
1643- # auth-url defaults to the value of the environment variable OS_AUTH_URL,
1644- # but can be specified here.
1645+ # auth-url defaults to the value of the environment variable
1646+ # OS_AUTH_URL, but can be specified here.
1647+ #
1648 # auth-url: https://yourkeystoneurl:443/v2.0/
1649
1650- # tenant-name holds the openstack tenant name. It defaults to
1651- # the environment variable OS_TENANT_NAME.
1652+ # tenant-name holds the openstack tenant name. It defaults to the
1653+ # environment variable OS_TENANT_NAME.
1654+ #
1655 # tenant-name: <your tenant name>
1656
1657- # region holds the openstack region. It defaults to
1658- # the environment variable OS_REGION_NAME.
1659+ # region holds the openstack region. It defaults to the
1660+ # environment variable OS_REGION_NAME.
1661+ #
1662 # region: <your region>
1663
1664- # The auth-mode, username and password attributes
1665- # are used for userpass authentication (the default).
1666-
1667+ # The auth-mode, username and password attributes are used for
1668+ # userpass authentication (the default).
1669+ #
1670 # auth-mode holds the authentication mode. For user-password
1671- # authentication, auth-mode should be "userpass" and username
1672- # and password should be set appropriately; they default to
1673- # the environment variables OS_USERNAME and OS_PASSWORD
1674- # respectively.
1675+ # authentication, auth-mode should be "userpass" and username and
1676+ # password should be set appropriately; they default to the
1677+ # environment variables OS_USERNAME and OS_PASSWORD respectively.
1678+ #
1679 # auth-mode: userpass
1680 # username: <your username>
1681 # password: <secret>
1682-
1683- # For key-pair authentication, auth-mode should be "keypair"
1684- # and access-key and secret-key should be set appropriately; they default to
1685- # the environment variables OS_ACCESS_KEY and OS_SECRET_KEY
1686- # respectively.
1687+
1688+ # For key-pair authentication, auth-mode should be "keypair" and
1689+ # access-key and secret-key should be set appropriately; they
1690+ # default to the environment variables OS_ACCESS_KEY and
1691+ # OS_SECRET_KEY respectively.
1692+ #
1693 # auth-mode: keypair
1694 # access-key: <secret>
1695 # secret-key: <secret>
1696@@ -127,45 +143,59 @@
1697 # https://juju.ubuntu.com/docs/config-hpcloud.html
1698 hpcloud:
1699 type: openstack
1700-
1701- # use-floating-ip specifies whether a floating IP address is required
1702- # to give the nodes a public IP address. Some installations assign public IP
1703- # addresses by default without requiring a floating IP address.
1704+
1705+ # use-floating-ip specifies whether a floating IP address is
1706+ # required to give the nodes a public IP address. Some
1707+ # installations assign public IP addresses by default without
1708+ # requiring a floating IP address.
1709+ #
1710 # use-floating-ip: false
1711
1712- # use-default-secgroup specifies whether new machine instances should have the "default"
1713- # Openstack security group assigned.
1714+ # use-default-secgroup specifies whether new machine instances
1715+ # should have the "default" Openstack security group assigned.
1716+ #
1717 # use-default-secgroup: false
1718
1719- # tenant-name holds the openstack tenant name. In HPCloud, this is
1720- # synonymous with the project-name It defaults to
1721- # the environment variable OS_TENANT_NAME.
1722+ # tenant-name holds the openstack tenant name. In HPCloud, this is
1723+ # synonymous with the project-name It defaults to the environment
1724+ # variable OS_TENANT_NAME.
1725+ #
1726 # tenant-name: <your tenant name>
1727-
1728- # auth-url holds the keystone url for authentication.
1729- # It defaults to the value of the environment variable OS_AUTH_URL.
1730+
1731+ # image-stream chooses a simplestreams stream to select OS images
1732+ # from, for example daily or released images (or any other stream
1733+ # available on simplestreams).
1734+ #
1735+ # image-stream: "released"
1736+
1737+ # auth-url holds the keystone url for authentication. It defaults
1738+ # to the value of the environment variable OS_AUTH_URL.
1739+ #
1740 # auth-url: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/
1741
1742- # region holds the HP Cloud region (e.g. az-1.region-a.geo-1).
1743- # It defaults to the environment variable OS_REGION_NAME.
1744+ # region holds the HP Cloud region (e.g. az-1.region-a.geo-1). It
1745+ # defaults to the environment variable OS_REGION_NAME.
1746+ #
1747 # region: <your region>
1748-
1749+
1750 # auth-mode holds the authentication mode. For user-password
1751- # authentication, auth-mode should be "userpass" and username
1752- # and password should be set appropriately; they default to
1753- # the environment variables OS_USERNAME and OS_PASSWORD
1754- # respectively.
1755+ # authentication, auth-mode should be "userpass" and username and
1756+ # password should be set appropriately; they default to the
1757+ # environment variables OS_USERNAME and OS_PASSWORD respectively.
1758+ #
1759 # auth-mode: userpass
1760 # username: <your_username>
1761 # password: <your_password>
1762-
1763- # For key-pair authentication, auth-mode should be "keypair"
1764- # and access-key and secret-key should be set appropriately; they default to
1765- # the environment variables OS_ACCESS_KEY and OS_SECRET_KEY
1766- # respectively.
1767+
1768+ # For key-pair authentication, auth-mode should be "keypair" and
1769+ # access-key and secret-key should be set appropriately; they
1770+ # default to the environment variables OS_ACCESS_KEY and
1771+ # OS_SECRET_KEY respectively.
1772+ #
1773 # auth-mode: keypair
1774 # access-key: <secret>
1775 # secret-key: <secret>
1776+
1777 `[1:]
1778 }
1779
1780
1781=== modified file 'state/apiserver/charms.go'
1782--- state/apiserver/charms.go 2014-03-06 18:11:16 +0000
1783+++ state/apiserver/charms.go 2014-03-12 12:33:26 +0000
1784@@ -25,7 +25,7 @@
1785 "github.com/errgo/errgo"
1786
1787 "launchpad.net/juju-core/charm"
1788- envtesting "launchpad.net/juju-core/environs/testing"
1789+ "launchpad.net/juju-core/environs"
1790 "launchpad.net/juju-core/names"
1791 "launchpad.net/juju-core/state"
1792 "launchpad.net/juju-core/state/api/params"
1793@@ -392,7 +392,7 @@
1794 if _, err := repackagedArchive.Seek(0, 0); err != nil {
1795 return errgo.Annotate(err, "cannot rewind the charm file reader")
1796 }
1797- storage, err := envtesting.GetEnvironStorage(h.state)
1798+ storage, err := environs.GetStorage(h.state)
1799 if err != nil {
1800 return errgo.Annotate(err, "cannot access provider storage")
1801 }
1802@@ -455,7 +455,7 @@
1803 // saves the corresponding zip archive to the given charmArchivePath.
1804 func (h *charmsHandler) downloadCharm(name, charmArchivePath string) error {
1805 // Get the provider storage.
1806- storage, err := envtesting.GetEnvironStorage(h.state)
1807+ storage, err := environs.GetStorage(h.state)
1808 if err != nil {
1809 return errgo.Annotate(err, "cannot access provider storage")
1810 }
1811
1812=== modified file 'state/apiserver/charms_test.go'
1813--- state/apiserver/charms_test.go 2014-02-27 14:11:56 +0000
1814+++ state/apiserver/charms_test.go 2014-03-12 12:33:26 +0000
1815@@ -18,7 +18,7 @@
1816 gc "launchpad.net/gocheck"
1817
1818 "launchpad.net/juju-core/charm"
1819- envtesting "launchpad.net/juju-core/environs/testing"
1820+ "launchpad.net/juju-core/environs"
1821 jujutesting "launchpad.net/juju-core/juju/testing"
1822 "launchpad.net/juju-core/state"
1823 "launchpad.net/juju-core/state/api/params"
1824@@ -168,7 +168,7 @@
1825 expectedSHA256, _, err := utils.ReadSHA256(tempFile)
1826 c.Assert(err, gc.IsNil)
1827 name := charm.Quote(expectedURL.String())
1828- storage, err := envtesting.GetEnvironStorage(s.State)
1829+ storage, err := environs.GetStorage(s.State)
1830 c.Assert(err, gc.IsNil)
1831 expectedUploadURL, err := storage.URL(name)
1832 c.Assert(err, gc.IsNil)
1833@@ -219,7 +219,7 @@
1834 // should succeed, because it was repackaged during upload to
1835 // strip nested dirs.
1836 archiveName := strings.TrimPrefix(sch.BundleURL().RequestURI(), "/dummyenv/private/")
1837- storage, err := envtesting.GetEnvironStorage(s.State)
1838+ storage, err := environs.GetStorage(s.State)
1839 c.Assert(err, gc.IsNil)
1840 reader, err := storage.Get(archiveName)
1841 c.Assert(err, gc.IsNil)
1842
1843=== modified file 'state/apiserver/client/client_test.go'
1844--- state/apiserver/client/client_test.go 2014-03-11 02:53:40 +0000
1845+++ state/apiserver/client/client_test.go 2014-03-12 12:33:26 +0000
1846@@ -17,10 +17,10 @@
1847 coreCloudinit "launchpad.net/juju-core/cloudinit"
1848 "launchpad.net/juju-core/cloudinit/sshinit"
1849 "launchpad.net/juju-core/constraints"
1850+ "launchpad.net/juju-core/environs"
1851 "launchpad.net/juju-core/environs/cloudinit"
1852 "launchpad.net/juju-core/environs/config"
1853- "launchpad.net/juju-core/environs/storage"
1854- envtesting "launchpad.net/juju-core/environs/testing"
1855+ envstorage "launchpad.net/juju-core/environs/storage"
1856 ttesting "launchpad.net/juju-core/environs/tools/testing"
1857 "launchpad.net/juju-core/errors"
1858 "launchpad.net/juju-core/instance"
1859@@ -1872,7 +1872,7 @@
1860 // contains the correct data.
1861 sch, err := s.State.Charm(curl)
1862 c.Assert(err, gc.IsNil)
1863- storage, err := envtesting.GetEnvironStorage(s.State)
1864+ storage, err := environs.GetStorage(s.State)
1865 c.Assert(err, gc.IsNil)
1866 uploads, err := storage.List(fmt.Sprintf("%s-%d-", curl.Name, curl.Revision))
1867 c.Assert(err, gc.IsNil)
1868@@ -1938,7 +1938,7 @@
1869 }
1870 }
1871
1872-func (s *clientSuite) assertUploaded(c *gc.C, storage storage.Storage, bundleURL *url.URL, expectedSHA256 string) {
1873+func (s *clientSuite) assertUploaded(c *gc.C, storage envstorage.Storage, bundleURL *url.URL, expectedSHA256 string) {
1874 archiveName := getArchiveName(bundleURL)
1875 reader, err := storage.Get(archiveName)
1876 c.Assert(err, gc.IsNil)
1877
1878=== removed file 'utils/ssh/testing/fakessh.go'
1879--- utils/ssh/testing/fakessh.go 2014-02-18 17:08:55 +0000
1880+++ utils/ssh/testing/fakessh.go 1970-01-01 00:00:00 +0000
1881@@ -1,80 +0,0 @@
1882-// Copyright 2013 Canonical Ltd.
1883-// Licensed under the AGPLv3, see LICENCE file for details.
1884-
1885-package testing
1886-
1887-import (
1888- "fmt"
1889- "io/ioutil"
1890- "path/filepath"
1891-
1892- gc "launchpad.net/gocheck"
1893-
1894- "launchpad.net/juju-core/testing/testbase"
1895-)
1896-
1897-// sshscript should only print the result on the first execution,
1898-// to handle the case where it's called multiple times. On
1899-// subsequent executions, it should find the next 'ssh' in $PATH
1900-// and exec that.
1901-var sshscript = `#!/bin/bash --norc
1902-if [ ! -e "$0.run" ]; then
1903- touch "$0.run"
1904- if [ -e "$0.expected-input" ]; then
1905- diff "$0.expected-input" -
1906- exitcode=$?
1907- if [ $exitcode -ne 0 ]; then
1908- echo "ERROR: did not match expected input" >&2
1909- exit $exitcode
1910- fi
1911- else
1912- head >/dev/null
1913- fi
1914- # stdout
1915- %s
1916- # stderr
1917- %s
1918- exit %d
1919-else
1920- export PATH=${PATH#*:}
1921- exec ssh $*
1922-fi`
1923-
1924-// InstallFakeSSH creates a fake "ssh" command in a new $PATH,
1925-// updates $PATH, and returns a function to reset $PATH to its
1926-// original value when called.
1927-//
1928-// input may be:
1929-// - nil (ignore input)
1930-// - a string (match input exactly)
1931-// output may be:
1932-// - nil (no output)
1933-// - a string (stdout)
1934-// - a slice of strings, of length two (stdout, stderr)
1935-func InstallFakeSSH(c *gc.C, input, output interface{}, rc int) testbase.Restorer {
1936- fakebin := c.MkDir()
1937- ssh := filepath.Join(fakebin, "ssh")
1938- switch input := input.(type) {
1939- case nil:
1940- case string:
1941- sshexpectedinput := ssh + ".expected-input"
1942- err := ioutil.WriteFile(sshexpectedinput, []byte(input), 0644)
1943- c.Assert(err, gc.IsNil)
1944- default:
1945- c.Errorf("input has invalid type: %T", input)
1946- }
1947- var stdout, stderr string
1948- switch output := output.(type) {
1949- case nil:
1950- case string:
1951- stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output)
1952- case []string:
1953- c.Assert(output, gc.HasLen, 2)
1954- stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output[0])
1955- stderr = fmt.Sprintf("cat>&2<<EOF\n%s\nEOF", output[1])
1956- }
1957- script := fmt.Sprintf(sshscript, stdout, stderr, rc)
1958- err := ioutil.WriteFile(ssh, []byte(script), 0777)
1959- c.Assert(err, gc.IsNil)
1960- return testbase.PatchEnvPathPrepend(fakebin)
1961-}

Subscribers

People subscribed via source and target branches

to status/vote changes: