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
=== modified file 'cmd/juju/destroyenvironment.go'
--- cmd/juju/destroyenvironment.go 2014-02-11 01:35:20 +0000
+++ cmd/juju/destroyenvironment.go 2014-03-12 12:33:26 +0000
@@ -67,7 +67,7 @@
67 }67 }
68 answer := strings.ToLower(scanner.Text())68 answer := strings.ToLower(scanner.Text())
69 if answer != "y" && answer != "yes" {69 if answer != "y" && answer != "yes" {
70 return errors.New("Environment destruction aborted")70 return errors.New("environment destruction aborted")
71 }71 }
72 }72 }
73 // If --force is supplied, then don't attempt to use the API.73 // If --force is supplied, then don't attempt to use the API.
7474
=== modified file 'cmd/juju/destroyenvironment_test.go'
--- cmd/juju/destroyenvironment_test.go 2014-02-13 02:46:58 +0000
+++ cmd/juju/destroyenvironment_test.go 2014-03-12 12:33:26 +0000
@@ -130,7 +130,7 @@
130 // Ensure confirmation is requested if "-y" is not specified.130 // Ensure confirmation is requested if "-y" is not specified.
131 stdin.WriteString("n")131 stdin.WriteString("n")
132 opc, errc := runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv")132 opc, errc := runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv")
133 c.Check(<-errc, gc.ErrorMatches, "Environment destruction aborted")133 c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted")
134 c.Check(<-opc, gc.IsNil)134 c.Check(<-opc, gc.IsNil)
135 c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*")135 c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*")
136 assertEnvironNotDestroyed(c, env, s.ConfigStore)136 assertEnvironNotDestroyed(c, env, s.ConfigStore)
@@ -140,7 +140,7 @@
140 stdout.Reset()140 stdout.Reset()
141 opc, errc = runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv")141 opc, errc = runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv")
142 c.Check(<-opc, gc.IsNil)142 c.Check(<-opc, gc.IsNil)
143 c.Check(<-errc, gc.ErrorMatches, "Environment destruction aborted")143 c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted")
144 assertEnvironNotDestroyed(c, env, s.ConfigStore)144 assertEnvironNotDestroyed(c, env, s.ConfigStore)
145145
146 // "--yes" passed: no confirmation request.146 // "--yes" passed: no confirmation request.
147147
=== modified file 'cmd/juju/help_topics.go'
--- cmd/juju/help_topics.go 2014-03-04 14:33:43 +0000
+++ cmd/juju/help_topics.go 2014-03-12 12:33:26 +0000
@@ -47,8 +47,8 @@
4747
48`48`
4949
50const helpLocalProvider = ` 50const helpLocalProvider = `
51The local provider is a linux-only Juju environment that uses LXC containers as51The local provider is a Linux-only Juju environment that uses LXC containers as
52a virtual cloud on the local machine. Because of this, lxc and mongodb are52a virtual cloud on the local machine. Because of this, lxc and mongodb are
53required for the local provider to work. If you don't already have lxc and53required for the local provider to work. If you don't already have lxc and
54mongodb installed, run the following commands:54mongodb installed, run the following commands:
@@ -64,14 +64,14 @@
64Now you need to tell Juju to use the local provider and then bootstrap:64Now you need to tell Juju to use the local provider and then bootstrap:
6565
66 juju switch local66 juju switch local
67 sudo juju bootstrap67 juju bootstrap
6868
69The first time this runs it might take a bit, as it's doing a netinstall for69The first time this runs it might take a bit, as it's doing a netinstall for
70the container, it's around a 300 megabyte download. Subsequent bootstraps70the container, it's around a 300 megabyte download. Subsequent bootstraps
71should be much quicker. 'sudo' is needed because only root can create LXC71should be much quicker. You'll be asked for your 'sudo' password, which is
72containers. After the initial bootstrap, you do not need 'sudo' anymore,72needed because only root can create LXC containers. When you need to destroy
73except to 'sudo juju destroy-environment' when you want to tear everything73the environment, do 'juju destroy-environment local' and you could be asked
74down.74for your 'sudo' password again.
7575
76You deploy charms from the charm store using the following commands:76You deploy charms from the charm store using the following commands:
7777
@@ -90,31 +90,35 @@
9090
91 sample_openstack:91 sample_openstack:
92 type: openstack92 type: openstack
93
93 # Specifies whether the use of a floating IP address is required to94 # Specifies whether the use of a floating IP address is required to
94 # give the nodes a public IP address. Some installations assign public95 # give the nodes a public IP address. Some installations assign public
95 # IP addresses by default without requiring a floating IP address.96 # IP addresses by default without requiring a floating IP address.
96 # use-floating-ip: false97 # use-floating-ip: false
98
97 # Specifies whether new machine instances should have the "default"99 # Specifies whether new machine instances should have the "default"
98 # Openstack security group assigned.100 # Openstack security group assigned.
99 # use-default-secgroup: false101 # use-default-secgroup: false
100 admin-secret: 13850d1b9786065cadd0f477e8c97cd3102
101 # Globally unique swift bucket name
102 control-bucket: juju-fd6ab8d02393af742bfbe8b9629707ee
103 # Usually set via the env variable OS_AUTH_URL, but can be specified here103 # Usually set via the env variable OS_AUTH_URL, but can be specified here
104 # auth-url: https://yourkeystoneurl:443/v2.0/104 # auth-url: https://yourkeystoneurl:443/v2.0/
105 # override if your workstation is running a different series to which105
106 # you are deploying
107 # The following are used for userpass authentication (the default)106 # The following are used for userpass authentication (the default)
108 auth-mode: userpass107 # auth-mode: userpass
108
109 # Usually set via the env variable OS_USERNAME, but can be specified here109 # Usually set via the env variable OS_USERNAME, but can be specified here
110 # username: <your username>110 # username: <your username>
111
111 # Usually set via the env variable OS_PASSWORD, but can be specified here112 # Usually set via the env variable OS_PASSWORD, but can be specified here
112 # password: <secret>113 # password: <secret>
114
113 # Usually set via the env variable OS_TENANT_NAME, but can be specified here115 # Usually set via the env variable OS_TENANT_NAME, but can be specified here
114 # tenant-name: <your tenant name>116 # tenant-name: <your tenant name>
117
115 # Usually set via the env variable OS_REGION_NAME, but can be specified here118 # Usually set via the env variable OS_REGION_NAME, but can be specified here
116 # region: <your region>119 # region: <your region>
117120
121If you have set the described OS_* environment variables, you only need "type:".
118References:122References:
119123
120 http://juju.ubuntu.com/docs/provider-configuration-openstack.html124 http://juju.ubuntu.com/docs/provider-configuration-openstack.html
@@ -125,7 +129,7 @@
125This answer is for generic OpenStack support, if you're using an OpenStack-based129This answer is for generic OpenStack support, if you're using an OpenStack-based
126provider check these questions out for provider-specific information:130provider check these questions out for provider-specific information:
127131
128 https://juju.ubuntu.com/docs/config-hpcloud.html 132 https://juju.ubuntu.com/docs/config-hpcloud.html
129133
130`134`
131135
@@ -141,11 +145,8 @@
141 type: ec2145 type: ec2
142 # access-key: YOUR-ACCESS-KEY-GOES-HERE146 # access-key: YOUR-ACCESS-KEY-GOES-HERE
143 # secret-key: YOUR-SECRET-KEY-GOES-HERE147 # secret-key: YOUR-SECRET-KEY-GOES-HERE
144 control-bucket: juju-faefb490d69a41f0a3616a4808e0766b
145 admin-secret: 81a1e7429e6847c4941fda7591246594
146148
147See the EC2 provider documentation[2] for more options. The S3 bucket does not149See the EC2 provider documentation[2] for more options.
148need to exist already.
149150
150Note If you already have an AWS account, you can determine your access key by151Note If you already have an AWS account, you can determine your access key by
151visiting your account page[3], clicking "Security Credentials" and then clicking152visiting your account page[3], clicking "Security Credentials" and then clicking
@@ -183,7 +184,7 @@
183184
184See the online help for more information:185See the online help for more information:
185186
186 https://juju.ubuntu.com/docs/config-hpcloud.html 187 https://juju.ubuntu.com/docs/config-hpcloud.html
187`188`
188189
189const helpAzureProvider = `190const helpAzureProvider = `
@@ -191,17 +192,22 @@
191192
192 sample_azure:193 sample_azure:
193 type: azure194 type: azure
195
194 # Location for instances, e.g. West US, North Europe.196 # Location for instances, e.g. West US, North Europe.
195 location: West US197 location: West US
198
196 # http://msdn.microsoft.com/en-us/library/windowsazure199 # http://msdn.microsoft.com/en-us/library/windowsazure
197 # Windows Azure Management info.200 # Windows Azure Management info.
198 management-subscription-id: 886413e1-3b8a-5382-9b90-0c9aee199e5d201 management-subscription-id: 886413e1-3b8a-5382-9b90-0c9aee199e5d
199 management-certificate-path: /home/me/azure.pem202 management-certificate-path: /home/me/azure.pem
203
200 # Windows Azure Storage info.204 # Windows Azure Storage info.
201 storage-account-name: juju0useast0205 storage-account-name: juju0useast0
206
202 # Override OS image selection with a fixed image for all deployments.207 # Override OS image selection with a fixed image for all deployments.
203 # Most useful for developers.208 # Most useful for developers.
204 # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB209 # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB
210
205 # Pick a simplestreams stream to select OS images from: daily or released211 # Pick a simplestreams stream to select OS images from: daily or released
206 # images, or any other stream available on simplestreams. Leave blank for212 # images, or any other stream available on simplestreams. Leave blank for
207 # released images.213 # released images.
@@ -219,7 +225,7 @@
219 https://juju.ubuntu.com/docs/config-azure.html225 https://juju.ubuntu.com/docs/config-azure.html
220`226`
221227
222const helpConstraints = ` 228const helpConstraints = `
223Constraints constrain the possible instances that may be started by juju229Constraints constrain the possible instances that may be started by juju
224commands. They are usually passed as a flag to commands that provision a new230commands. They are usually passed as a flag to commands that provision a new
225machine (such as bootstrap, deploy, and add-machine).231machine (such as bootstrap, deploy, and add-machine).
@@ -272,7 +278,7 @@
272 T terabytes (1024 gigabytes)278 T terabytes (1024 gigabytes)
273 P petabytes (1024 terabytes)279 P petabytes (1024 terabytes)
274280
275root-disk 281root-disk
276 Root-Disk is a float that defines the amount of space in megabytes that must282 Root-Disk is a float that defines the amount of space in megabytes that must
277 be available in the machine's root partition. For providers that have283 be available in the machine's root partition. For providers that have
278 configurable root disk sizes (such as EC2) an instance with the specified284 configurable root disk sizes (such as EC2) an instance with the specified
@@ -280,8 +286,8 @@
280 defaults to megabytes and may be specified in the same manner as the mem286 defaults to megabytes and may be specified in the same manner as the mem
281 constraint.287 constraint.
282288
283container 289container
284 Container defines that the machine must be a container of the specified type. 290 Container defines that the machine must be a container of the specified type.
285 A container of that type may be created by juju to fulfill the request.291 A container of that type may be created by juju to fulfill the request.
286 Currently supported containers:292 Currently supported containers:
287 none - (default) no container293 none - (default) no container
@@ -294,7 +300,7 @@
294 roughly, a single 2007-era Xeon). Cpu-power is currently only supported by300 roughly, a single 2007-era Xeon). Cpu-power is currently only supported by
295 the Amazon EC2 environment.301 the Amazon EC2 environment.
296302
297tags 303tags
298 Tags defines the list of tags that the machine must have applied to it.304 Tags defines the list of tags that the machine must have applied to it.
299 Multiple tags must be delimited by a comma. Tags are currently only supported305 Multiple tags must be delimited by a comma. Tags are currently only supported
300 by the MaaS environment.306 by the MaaS environment.
@@ -374,7 +380,7 @@
374 other, and the way in which the topology of Services is assembled. The Charm380 other, and the way in which the topology of Services is assembled. The Charm
375 defines which Relations a given Service may establish, and what kind of381 defines which Relations a given Service may establish, and what kind of
376 interface these Relations require.382 interface these Relations require.
377 383
378 In many cases, the establishment of a Relation will result into an actual TCP384 In many cases, the establishment of a Relation will result into an actual TCP
379 connection being created between the Service Units, but that's not necessarily385 connection being created between the Service Units, but that's not necessarily
380 the case. Relations may also be established to inform Services of386 the case. Relations may also be established to inform Services of
381387
=== modified file 'environs/boilerplate_config.go'
--- environs/boilerplate_config.go 2013-10-11 14:05:04 +0000
+++ environs/boilerplate_config.go 2014-03-12 12:33:26 +0000
@@ -12,24 +12,36 @@
12)12)
1313
14var configHeader = `14var configHeader = `
15# This is the Juju config file, which you can use to specify multiple environments in which to deploy.15# This is the Juju config file, which you can use to specify multiple
16# By default Juju ships AWS (default), HP Cloud, OpenStack.16# environments in which to deploy. By default Juju ships with AWS
17# See https://juju.ubuntu.com/docs for more information17# (default), HP Cloud, OpenStack, Azure, MaaS, Local and Manual
18# providers. See https://juju.ubuntu.com/docs for more information
1819
19# An environment configuration must always specify at least the following information:20# An environment configuration must always specify at least the
20#21# following information:
21# - name (to identify the environment)22# - name (to identify the environment)
22# - type (to specify the provider)23# - type (to specify the provider)
24# In the following example the name is "myenv" and type is "ec2".
25# myenv:
26# type: ec2
2327
24# Values in <brackets> below need to be filled in by the user.28# Values in <brackets> below need to be filled in by the user.
25# Optional attributes are shown commented out, with29# Optional attributes are shown commented out, with
26# a sample value or a value in <brackets>.30# a sample value or a value in <brackets>.
2731
32# There are several settings supported by all environments, all of which
33# are optional and have specified default values. For more info, see the
34# Juju documentation.
35
28# The default environment is chosen when an environment is not36# The default environment is chosen when an environment is not
29# specified using any of the following, in descending order of precedence:37# specified using any of the following, in descending order of precedence:
30# -e or --environment command line parameter.38# 1. -e or --environment command line parameter, passed after the command, e.g.
31# JUJU_ENV environment variable.39# $ juju add-unit -e myenv myservice
32# the juju switch command.40# 2. By setting JUJU_ENV environment variable.
41# 3. Using the juju switch command like this:
42# $ juju switch myenv
43#
44
33default: amazon45default: amazon
3446
35environments:47environments:
3648
=== modified file 'environs/bootstrap/bootstrap.go'
--- environs/bootstrap/bootstrap.go 2014-03-05 19:41:34 +0000
+++ environs/bootstrap/bootstrap.go 2014-03-12 12:33:26 +0000
@@ -71,8 +71,9 @@
71 return toolsList, nil71 return toolsList, nil
72}72}
7373
74// EnsureNotBootstrapped returns null if the environment is not bootstrapped,74// EnsureNotBootstrapped returns nil if the environment is not
75// and an error if it is or if the function was not able to tell.75// bootstrapped, and an error if it is or if the function was not able
76// to tell.
76func EnsureNotBootstrapped(env environs.Environ) error {77func EnsureNotBootstrapped(env environs.Environ) error {
77 _, err := LoadState(env.Storage())78 _, err := LoadState(env.Storage())
78 // If there is no error loading the bootstrap state, then we are79 // If there is no error loading the bootstrap state, then we are
7980
=== modified file 'environs/bootstrap/state.go'
--- environs/bootstrap/state.go 2014-02-25 22:19:30 +0000
+++ environs/bootstrap/state.go 2014-03-12 12:33:26 +0000
@@ -40,6 +40,7 @@
40// putState writes the given data to the state file on the given storage.40// putState writes the given data to the state file on the given storage.
41// The file's name is as defined in StateFile.41// The file's name is as defined in StateFile.
42func putState(storage storage.StorageWriter, data []byte) error {42func putState(storage storage.StorageWriter, data []byte) error {
43 logger.Debugf("putting %q to bootstrap storage %T", StateFile, storage)
43 return storage.Put(StateFile, bytes.NewBuffer(data), int64(len(data)))44 return storage.Put(StateFile, bytes.NewBuffer(data), int64(len(data)))
44}45}
4546
@@ -69,6 +70,7 @@
6970
70// LoadStateFromURL reads state from the given URL.71// LoadStateFromURL reads state from the given URL.
71func LoadStateFromURL(url string, disableSSLHostnameVerification bool) (*BootstrapState, error) {72func LoadStateFromURL(url string, disableSSLHostnameVerification bool) (*BootstrapState, error) {
73 logger.Debugf("loading %q from %q", StateFile, url)
72 client := http.DefaultClient74 client := http.DefaultClient
73 if disableSSLHostnameVerification {75 if disableSSLHostnameVerification {
74 logger.Infof("hostname SSL verification disabled")76 logger.Infof("hostname SSL verification disabled")
7577
=== modified file 'environs/cloudinit/cloudinit.go'
--- environs/cloudinit/cloudinit.go 2014-03-11 18:33:39 +0000
+++ environs/cloudinit/cloudinit.go 2014-03-12 12:33:26 +0000
@@ -299,9 +299,9 @@
299 if strings.HasPrefix(cfg.Tools.URL, fileSchemePrefix) {299 if strings.HasPrefix(cfg.Tools.URL, fileSchemePrefix) {
300 copyCmd = fmt.Sprintf("cp %s $bin/tools.tar.gz", shquote(cfg.Tools.URL[len(fileSchemePrefix):]))300 copyCmd = fmt.Sprintf("cp %s $bin/tools.tar.gz", shquote(cfg.Tools.URL[len(fileSchemePrefix):]))
301 } else {301 } else {
302 curlCommand := "curl"302 curlCommand := "curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s '"
303 if cfg.DisableSSLHostnameVerification {303 if cfg.DisableSSLHostnameVerification {
304 curlCommand = "curl --insecure"304 curlCommand += " --insecure"
305 }305 }
306 copyCmd = fmt.Sprintf("%s -o $bin/tools.tar.gz %s", curlCommand, shquote(cfg.Tools.URL))306 copyCmd = fmt.Sprintf("%s -o $bin/tools.tar.gz %s", curlCommand, shquote(cfg.Tools.URL))
307 c.AddRunCmd(cloudinit.LogProgressCmd("Fetching tools: %s", copyCmd))307 c.AddRunCmd(cloudinit.LogProgressCmd("Fetching tools: %s", copyCmd))
308308
=== modified file 'environs/cloudinit/cloudinit_test.go'
--- environs/cloudinit/cloudinit_test.go 2014-03-11 18:33:39 +0000
+++ environs/cloudinit/cloudinit_test.go 2014-03-12 12:33:26 +0000
@@ -124,7 +124,7 @@
124echo 'Fetching tools.*124echo 'Fetching tools.*
125bin='/var/lib/juju/tools/1\.2\.3-precise-amd64'125bin='/var/lib/juju/tools/1\.2\.3-precise-amd64'
126mkdir -p \$bin126mkdir -p \$bin
127curl -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-precise-amd64\.tgz'127curl -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'
128sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-precise-amd64\.sha256128sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-precise-amd64\.sha256
129grep '1234' \$bin/juju1\.2\.3-precise-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)129grep '1234' \$bin/juju1\.2\.3-precise-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)
130tar zxf \$bin/tools.tar.gz -C \$bin130tar zxf \$bin/tools.tar.gz -C \$bin
@@ -195,7 +195,7 @@
195 inexactMatch: true,195 inexactMatch: true,
196 expectScripts: `196 expectScripts: `
197bin='/var/lib/juju/tools/1\.2\.3-raring-amd64'197bin='/var/lib/juju/tools/1\.2\.3-raring-amd64'
198curl -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz'198curl -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'
199sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-raring-amd64\.sha256199sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-raring-amd64\.sha256
200grep '1234' \$bin/juju1\.2\.3-raring-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)200grep '1234' \$bin/juju1\.2\.3-raring-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)
201rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-raring-amd64\.sha256201rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-raring-amd64\.sha256
@@ -243,7 +243,7 @@
243echo 'Fetching tools.*243echo 'Fetching tools.*
244bin='/var/lib/juju/tools/1\.2\.3-linux-amd64'244bin='/var/lib/juju/tools/1\.2\.3-linux-amd64'
245mkdir -p \$bin245mkdir -p \$bin
246curl -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz'246curl -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'
247sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-linux-amd64\.sha256247sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-linux-amd64\.sha256
248grep '1234' \$bin/juju1\.2\.3-linux-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)248grep '1234' \$bin/juju1\.2\.3-linux-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\)
249tar zxf \$bin/tools.tar.gz -C \$bin249tar zxf \$bin/tools.tar.gz -C \$bin
@@ -324,7 +324,7 @@
324 },324 },
325 inexactMatch: true,325 inexactMatch: true,
326 expectScripts: `326 expectScripts: `
327curl --insecure -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz'327curl -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'
328`,328`,
329 }, {329 }, {
330 // empty contraints.330 // empty contraints.
331331
=== modified file 'environs/httpstorage/storage.go'
--- environs/httpstorage/storage.go 2014-02-25 22:19:30 +0000
+++ environs/httpstorage/storage.go 2014-03-12 12:33:26 +0000
@@ -16,11 +16,15 @@
16 "strings"16 "strings"
17 "sync"17 "sync"
1818
19 "github.com/juju/loggo"
20
19 "launchpad.net/juju-core/environs/storage"21 "launchpad.net/juju-core/environs/storage"
20 coreerrors "launchpad.net/juju-core/errors"22 coreerrors "launchpad.net/juju-core/errors"
21 "launchpad.net/juju-core/utils"23 "launchpad.net/juju-core/utils"
22)24)
2325
26var logger = loggo.GetLogger("juju.environs.httpstorage")
27
24// storage implements the storage.Storage interface.28// storage implements the storage.Storage interface.
25type localStorage struct {29type localStorage struct {
26 addr string30 addr string
@@ -46,6 +50,7 @@
46// using TLS. The client is given an authentication key,50// using TLS. The client is given an authentication key,
47// which the server will verify for Put and Remove* operations.51// which the server will verify for Put and Remove* operations.
48func ClientTLS(addr string, caCertPEM []byte, authkey string) (storage.Storage, error) {52func ClientTLS(addr string, caCertPEM []byte, authkey string) (storage.Storage, error) {
53 logger.Debugf("using https storage at %q", addr)
49 caCerts := x509.NewCertPool()54 caCerts := x509.NewCertPool()
50 if caCertPEM != nil {55 if caCertPEM != nil {
51 if !caCerts.AppendCertsFromPEM(caCertPEM) {56 if !caCerts.AppendCertsFromPEM(caCertPEM) {
@@ -83,6 +88,7 @@
83// responsibility to close it after use. If the name does not88// responsibility to close it after use. If the name does not
84// exist, it should return a *NotFoundError.89// exist, it should return a *NotFoundError.
85func (s *localStorage) Get(name string) (io.ReadCloser, error) {90func (s *localStorage) Get(name string) (io.ReadCloser, error) {
91 logger.Debugf("getting %q from storage", name)
86 url, err := s.URL(name)92 url, err := s.URL(name)
87 if err != nil {93 if err != nil {
88 return nil, err94 return nil, err
@@ -167,6 +173,7 @@
167// Put reads from r and writes to the given storage file.173// Put reads from r and writes to the given storage file.
168// The length must be set to the total length of the file.174// The length must be set to the total length of the file.
169func (s *localStorage) Put(name string, r io.Reader, length int64) error {175func (s *localStorage) Put(name string, r io.Reader, length int64) error {
176 logger.Debugf("putting %q (len %d) to storage", name, length)
170 url, err := s.modURL(name)177 url, err := s.modURL(name)
171 if err != nil {178 if err != nil {
172 return err179 return err
173180
=== modified file 'environs/jujutest/livetests.go'
--- environs/jujutest/livetests.go 2014-02-20 08:23:40 +0000
+++ environs/jujutest/livetests.go 2014-03-12 12:33:26 +0000
@@ -27,7 +27,6 @@
27 "launchpad.net/juju-core/instance"27 "launchpad.net/juju-core/instance"
28 "launchpad.net/juju-core/juju"28 "launchpad.net/juju-core/juju"
29 "launchpad.net/juju-core/juju/testing"29 "launchpad.net/juju-core/juju/testing"
30 "launchpad.net/juju-core/provider/common"
31 "launchpad.net/juju-core/provider/dummy"30 "launchpad.net/juju-core/provider/dummy"
32 "launchpad.net/juju-core/state"31 "launchpad.net/juju-core/state"
33 "launchpad.net/juju-core/state/api"32 "launchpad.net/juju-core/state/api"
@@ -138,7 +137,7 @@
138 c.Assert(err, gc.IsNil)137 c.Assert(err, gc.IsNil)
139 }138 }
140 envtesting.UploadFakeTools(c, t.Env.Storage())139 envtesting.UploadFakeTools(c, t.Env.Storage())
141 err := common.EnsureNotBootstrapped(t.Env)140 err := bootstrap.EnsureNotBootstrapped(t.Env)
142 c.Assert(err, gc.IsNil)141 c.Assert(err, gc.IsNil)
143 err = bootstrap.Bootstrap(coretesting.Context(c), t.Env, cons)142 err = bootstrap.Bootstrap(coretesting.Context(c), t.Env, cons)
144 c.Assert(err, gc.IsNil)143 c.Assert(err, gc.IsNil)
@@ -382,7 +381,7 @@
382 // already up, this has been moved into the bootstrap command.381 // already up, this has been moved into the bootstrap command.
383 t.BootstrapOnce(c)382 t.BootstrapOnce(c)
384383
385 err := common.EnsureNotBootstrapped(t.Env)384 err := bootstrap.EnsureNotBootstrapped(t.Env)
386 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")385 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
387386
388 c.Logf("destroy env")387 c.Logf("destroy env")
389388
=== modified file 'environs/jujutest/tests.go'
--- environs/jujutest/tests.go 2014-02-13 02:46:58 +0000
+++ environs/jujutest/tests.go 2014-03-12 12:33:26 +0000
@@ -21,7 +21,6 @@
21 "launchpad.net/juju-core/errors"21 "launchpad.net/juju-core/errors"
22 "launchpad.net/juju-core/instance"22 "launchpad.net/juju-core/instance"
23 "launchpad.net/juju-core/juju/testing"23 "launchpad.net/juju-core/juju/testing"
24 "launchpad.net/juju-core/provider/common"
25 coretesting "launchpad.net/juju-core/testing"24 coretesting "launchpad.net/juju-core/testing"
26 jc "launchpad.net/juju-core/testing/checkers"25 jc "launchpad.net/juju-core/testing/checkers"
27 "launchpad.net/juju-core/testing/testbase"26 "launchpad.net/juju-core/testing/testbase"
@@ -132,7 +131,7 @@
132func (t *Tests) TestBootstrap(c *gc.C) {131func (t *Tests) TestBootstrap(c *gc.C) {
133 e := t.Prepare(c)132 e := t.Prepare(c)
134 envtesting.UploadFakeTools(c, e.Storage())133 envtesting.UploadFakeTools(c, e.Storage())
135 err := common.EnsureNotBootstrapped(e)134 err := bootstrap.EnsureNotBootstrapped(e)
136 c.Assert(err, gc.IsNil)135 c.Assert(err, gc.IsNil)
137 err = bootstrap.Bootstrap(coretesting.Context(c), e, constraints.Value{})136 err = bootstrap.Bootstrap(coretesting.Context(c), e, constraints.Value{})
138 c.Assert(err, gc.IsNil)137 c.Assert(err, gc.IsNil)
@@ -141,12 +140,12 @@
141 c.Check(info.Addrs, gc.Not(gc.HasLen), 0)140 c.Check(info.Addrs, gc.Not(gc.HasLen), 0)
142 c.Check(apiInfo.Addrs, gc.Not(gc.HasLen), 0)141 c.Check(apiInfo.Addrs, gc.Not(gc.HasLen), 0)
143142
144 err = common.EnsureNotBootstrapped(e)143 err = bootstrap.EnsureNotBootstrapped(e)
145 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")144 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
146145
147 e2 := t.Open(c)146 e2 := t.Open(c)
148 envtesting.UploadFakeTools(c, e2.Storage())147 envtesting.UploadFakeTools(c, e2.Storage())
149 err = common.EnsureNotBootstrapped(e2)148 err = bootstrap.EnsureNotBootstrapped(e2)
150 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")149 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
151150
152 info2, apiInfo2, err := e2.StateInfo()151 info2, apiInfo2, err := e2.StateInfo()
@@ -160,12 +159,12 @@
160 e3 := t.Prepare(c)159 e3 := t.Prepare(c)
161 envtesting.UploadFakeTools(c, e3.Storage())160 envtesting.UploadFakeTools(c, e3.Storage())
162161
163 err = common.EnsureNotBootstrapped(e3)162 err = bootstrap.EnsureNotBootstrapped(e3)
164 c.Assert(err, gc.IsNil)163 c.Assert(err, gc.IsNil)
165 err = bootstrap.Bootstrap(coretesting.Context(c), e3, constraints.Value{})164 err = bootstrap.Bootstrap(coretesting.Context(c), e3, constraints.Value{})
166 c.Assert(err, gc.IsNil)165 c.Assert(err, gc.IsNil)
167166
168 err = common.EnsureNotBootstrapped(e3)167 err = bootstrap.EnsureNotBootstrapped(e3)
169 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")168 c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped")
170}169}
171170
172171
=== modified file 'environs/manual/bootstrap_test.go'
--- environs/manual/bootstrap_test.go 2014-03-04 16:55:38 +0000
+++ environs/manual/bootstrap_test.go 2014-03-12 12:33:26 +0000
@@ -1,7 +1,7 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package manual4package manual_test
55
6import (6import (
7 "fmt"7 "fmt"
@@ -14,6 +14,7 @@
14 "launchpad.net/juju-core/environs/bootstrap"14 "launchpad.net/juju-core/environs/bootstrap"
15 "launchpad.net/juju-core/environs/cloudinit"15 "launchpad.net/juju-core/environs/cloudinit"
16 "launchpad.net/juju-core/environs/filestorage"16 "launchpad.net/juju-core/environs/filestorage"
17 "launchpad.net/juju-core/environs/manual"
17 "launchpad.net/juju-core/environs/storage"18 "launchpad.net/juju-core/environs/storage"
18 "launchpad.net/juju-core/environs/tools"19 "launchpad.net/juju-core/environs/tools"
19 "launchpad.net/juju-core/instance"20 "launchpad.net/juju-core/instance"
@@ -59,13 +60,13 @@
59 s.env.storage = storage60 s.env.storage = storage
60}61}
6162
62func (s *bootstrapSuite) getArgs(c *gc.C) BootstrapArgs {63func (s *bootstrapSuite) getArgs(c *gc.C) manual.BootstrapArgs {
63 hostname, err := os.Hostname()64 hostname, err := os.Hostname()
64 c.Assert(err, gc.IsNil)65 c.Assert(err, gc.IsNil)
65 toolsList, err := tools.FindBootstrapTools(s.Conn.Environ, tools.BootstrapToolsParams{})66 toolsList, err := tools.FindBootstrapTools(s.Conn.Environ, tools.BootstrapToolsParams{})
66 c.Assert(err, gc.IsNil)67 c.Assert(err, gc.IsNil)
67 arch := "amd64"68 arch := "amd64"
68 return BootstrapArgs{69 return manual.BootstrapArgs{
69 Host: hostname,70 Host: hostname,
70 DataDir: "/var/lib/juju",71 DataDir: "/var/lib/juju",
71 Environ: s.env,72 Environ: s.env,
@@ -83,7 +84,7 @@
83 args.Host = "ubuntu@" + args.Host84 args.Host = "ubuntu@" + args.Host
8485
85 defer fakeSSH{SkipDetection: true}.install(c).Restore()86 defer fakeSSH{SkipDetection: true}.install(c).Restore()
86 err := Bootstrap(args)87 err := manual.Bootstrap(args)
87 c.Assert(err, gc.IsNil)88 c.Assert(err, gc.IsNil)
8889
89 bootstrapState, err := bootstrap.LoadState(s.env.Storage())90 bootstrapState, err := bootstrap.LoadState(s.env.Storage())
@@ -91,14 +92,14 @@
91 c.Assert(92 c.Assert(
92 bootstrapState.StateInstances,93 bootstrapState.StateInstances,
93 gc.DeepEquals,94 gc.DeepEquals,
94 []instance.Id{BootstrapInstanceId},95 []instance.Id{manual.BootstrapInstanceId},
95 )96 )
9697
97 // Do it all again; this should work, despite the fact that98 // Do it all again; this should work, despite the fact that
98 // there's a bootstrap state file. Existence for that is99 // there's a bootstrap state file. Existence for that is
99 // checked in general bootstrap code (environs/bootstrap).100 // checked in general bootstrap code (environs/bootstrap).
100 defer fakeSSH{SkipDetection: true}.install(c).Restore()101 defer fakeSSH{SkipDetection: true}.install(c).Restore()
101 err = Bootstrap(args)102 err = manual.Bootstrap(args)
102 c.Assert(err, gc.IsNil)103 c.Assert(err, gc.IsNil)
103104
104 // We *do* check that the machine has no juju* upstart jobs, though.105 // We *do* check that the machine has no juju* upstart jobs, though.
@@ -107,15 +108,15 @@
107 SkipDetection: true,108 SkipDetection: true,
108 SkipProvisionAgent: true,109 SkipProvisionAgent: true,
109 }.install(c).Restore()110 }.install(c).Restore()
110 err = Bootstrap(args)111 err = manual.Bootstrap(args)
111 c.Assert(err, gc.Equals, ErrProvisioned)112 c.Assert(err, gc.Equals, manual.ErrProvisioned)
112}113}
113114
114func (s *bootstrapSuite) TestBootstrapScriptFailure(c *gc.C) {115func (s *bootstrapSuite) TestBootstrapScriptFailure(c *gc.C) {
115 args := s.getArgs(c)116 args := s.getArgs(c)
116 args.Host = "ubuntu@" + args.Host117 args.Host = "ubuntu@" + args.Host
117 defer fakeSSH{SkipDetection: true, ProvisionAgentExitCode: 1}.install(c).Restore()118 defer fakeSSH{SkipDetection: true, ProvisionAgentExitCode: 1}.install(c).Restore()
118 err := Bootstrap(args)119 err := manual.Bootstrap(args)
119 c.Assert(err, gc.NotNil)120 c.Assert(err, gc.NotNil)
120121
121 // Since the script failed, the state file should have been122 // Since the script failed, the state file should have been
@@ -127,19 +128,19 @@
127func (s *bootstrapSuite) TestBootstrapEmptyDataDir(c *gc.C) {128func (s *bootstrapSuite) TestBootstrapEmptyDataDir(c *gc.C) {
128 args := s.getArgs(c)129 args := s.getArgs(c)
129 args.DataDir = ""130 args.DataDir = ""
130 c.Assert(Bootstrap(args), gc.ErrorMatches, "data-dir argument is empty")131 c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "data-dir argument is empty")
131}132}
132133
133func (s *bootstrapSuite) TestBootstrapEmptyHost(c *gc.C) {134func (s *bootstrapSuite) TestBootstrapEmptyHost(c *gc.C) {
134 args := s.getArgs(c)135 args := s.getArgs(c)
135 args.Host = ""136 args.Host = ""
136 c.Assert(Bootstrap(args), gc.ErrorMatches, "host argument is empty")137 c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "host argument is empty")
137}138}
138139
139func (s *bootstrapSuite) TestBootstrapNilEnviron(c *gc.C) {140func (s *bootstrapSuite) TestBootstrapNilEnviron(c *gc.C) {
140 args := s.getArgs(c)141 args := s.getArgs(c)
141 args.Environ = nil142 args.Environ = nil
142 c.Assert(Bootstrap(args), gc.ErrorMatches, "environ argument is nil")143 c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "environ argument is nil")
143}144}
144145
145func (s *bootstrapSuite) TestBootstrapNoMatchingTools(c *gc.C) {146func (s *bootstrapSuite) TestBootstrapNoMatchingTools(c *gc.C) {
@@ -147,13 +148,13 @@
147 args := s.getArgs(c)148 args := s.getArgs(c)
148 args.PossibleTools = nil149 args.PossibleTools = nil
149 defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore()150 defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore()
150 c.Assert(Bootstrap(args), gc.ErrorMatches, "possible tools is empty")151 c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "possible tools is empty")
151152
152 // Non-empty list, but none that match the series/arch.153 // Non-empty list, but none that match the series/arch.
153 args = s.getArgs(c)154 args = s.getArgs(c)
154 args.Series = "edgy"155 args.Series = "edgy"
155 defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore()156 defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore()
156 c.Assert(Bootstrap(args), gc.ErrorMatches, "no matching tools available")157 c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "no matching tools available")
157}158}
158159
159func (s *bootstrapSuite) TestBootstrapToolsFileURL(c *gc.C) {160func (s *bootstrapSuite) TestBootstrapToolsFileURL(c *gc.C) {
@@ -170,13 +171,13 @@
170}171}
171172
172func (s *bootstrapSuite) testBootstrapToolsURL(c *gc.C, toolsURL, expectedURL string) {173func (s *bootstrapSuite) testBootstrapToolsURL(c *gc.C, toolsURL, expectedURL string) {
173 s.PatchValue(&provisionMachineAgent, func(host string, mcfg *cloudinit.MachineConfig, w io.Writer) error {174 s.PatchValue(manual.ProvisionMachineAgent, func(host string, mcfg *cloudinit.MachineConfig, w io.Writer) error {
174 c.Assert(mcfg.Tools.URL, gc.Equals, expectedURL)175 c.Assert(mcfg.Tools.URL, gc.Equals, expectedURL)
175 return nil176 return nil
176 })177 })
177 args := s.getArgs(c)178 args := s.getArgs(c)
178 args.PossibleTools[0].URL = toolsURL179 args.PossibleTools[0].URL = toolsURL
179 defer fakeSSH{SkipDetection: true}.install(c).Restore()180 defer fakeSSH{SkipDetection: true}.install(c).Restore()
180 err := Bootstrap(args)181 err := manual.Bootstrap(args)
181 c.Assert(err, gc.IsNil)182 c.Assert(err, gc.IsNil)
182}183}
183184
=== modified file 'environs/manual/export_test.go'
--- environs/manual/export_test.go 2014-01-30 03:18:20 +0000
+++ environs/manual/export_test.go 2014-03-12 12:33:26 +0000
@@ -5,4 +5,11 @@
55
6var (6var (
7 InstanceHostAddresses = &instanceHostAddresses7 InstanceHostAddresses = &instanceHostAddresses
8 ProvisionMachineAgent = &provisionMachineAgent
9 CheckProvisioned = checkProvisioned
10)
11
12const (
13 DetectionScript = detectionScript
14 CheckProvisionedScript = checkProvisionedScript
8)15)
916
=== removed file 'environs/manual/fakessh.go'
--- environs/manual/fakessh.go 2014-01-13 06:25:28 +0000
+++ environs/manual/fakessh.go 1970-01-01 00:00:00 +0000
@@ -1,87 +0,0 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package manual
5
6import (
7 "strings"
8
9 gc "launchpad.net/gocheck"
10
11 "launchpad.net/juju-core/testing/testbase"
12 sshtesting "launchpad.net/juju-core/utils/ssh/testing"
13)
14
15// installDetectionFakeSSH installs a fake SSH command, which will respond
16// to the series/hardware detection script with the specified
17// series/arch.
18func installDetectionFakeSSH(c *gc.C, series, arch string) testbase.Restorer {
19 if series == "" {
20 series = "precise"
21 }
22 if arch == "" {
23 arch = "amd64"
24 }
25 detectionoutput := strings.Join([]string{
26 series,
27 arch,
28 "MemTotal: 4096 kB",
29 "processor: 0",
30 }, "\n")
31 return sshtesting.InstallFakeSSH(c, detectionScript, detectionoutput, 0)
32}
33
34// FakeSSH wraps the invocation of InstallFakeSSH based on the parameters.
35type fakeSSH struct {
36 Series string
37 Arch string
38
39 // Provisioned should be set to true if the fakeSSH script
40 // should respond to checkProvisioned with a non-empty result.
41 Provisioned bool
42
43 // exit code for the checkProvisioned script.
44 CheckProvisionedExitCode int
45
46 // exit code for the machine agent provisioning script.
47 ProvisionAgentExitCode int
48
49 // InitUbuntuUser should be set to true if the fakeSSH script
50 // should respond to an attempt to initialise the ubuntu user.
51 InitUbuntuUser bool
52
53 // there are conditions other than error in the above
54 // that might cause provisioning to not go ahead, such
55 // as tools being missing.
56 SkipProvisionAgent bool
57
58 // detection will be skipped if the series/hardware were
59 // detected ahead of time. This should always be set to
60 // true when testing Bootstrap.
61 SkipDetection bool
62}
63
64// install installs fake SSH commands, which will respond to
65// manual provisioning/bootstrapping commands with the specified
66// output and exit codes.
67func (r fakeSSH) install(c *gc.C) testbase.Restorer {
68 var restore testbase.Restorer
69 add := func(input, output interface{}, rc int) {
70 restore = restore.Add(sshtesting.InstallFakeSSH(c, input, output, rc))
71 }
72 if !r.SkipProvisionAgent {
73 add(nil, nil, r.ProvisionAgentExitCode)
74 }
75 if !r.SkipDetection {
76 restore.Add(installDetectionFakeSSH(c, r.Series, r.Arch))
77 }
78 var checkProvisionedOutput interface{}
79 if r.Provisioned {
80 checkProvisionedOutput = "/etc/init/jujud-machine-0.conf"
81 }
82 add(checkProvisionedScript, checkProvisionedOutput, r.CheckProvisionedExitCode)
83 if r.InitUbuntuUser {
84 add("", nil, 0)
85 }
86 return restore
87}
880
=== added file 'environs/manual/fakessh_test.go'
--- environs/manual/fakessh_test.go 1970-01-01 00:00:00 +0000
+++ environs/manual/fakessh_test.go 2014-03-12 12:33:26 +0000
@@ -0,0 +1,156 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package manual_test
5
6import (
7 "fmt"
8 "io/ioutil"
9 "path/filepath"
10 "strings"
11
12 gc "launchpad.net/gocheck"
13
14 "launchpad.net/juju-core/environs/manual"
15 "launchpad.net/juju-core/testing/testbase"
16)
17
18// sshscript should only print the result on the first execution,
19// to handle the case where it's called multiple times. On
20// subsequent executions, it should find the next 'ssh' in $PATH
21// and exec that.
22var sshscript = `#!/bin/bash --norc
23if [ ! -e "$0.run" ]; then
24 touch "$0.run"
25 if [ -e "$0.expected-input" ]; then
26 diff "$0.expected-input" -
27 exitcode=$?
28 if [ $exitcode -ne 0 ]; then
29 echo "ERROR: did not match expected input" >&2
30 exit $exitcode
31 fi
32 else
33 head >/dev/null
34 fi
35 # stdout
36 %s
37 # stderr
38 %s
39 exit %d
40else
41 export PATH=${PATH#*:}
42 exec ssh $*
43fi`
44
45// installFakeSSH creates a fake "ssh" command in a new $PATH,
46// updates $PATH, and returns a function to reset $PATH to its
47// original value when called.
48//
49// input may be:
50// - nil (ignore input)
51// - a string (match input exactly)
52// output may be:
53// - nil (no output)
54// - a string (stdout)
55// - a slice of strings, of length two (stdout, stderr)
56func installFakeSSH(c *gc.C, input, output interface{}, rc int) testbase.Restorer {
57 fakebin := c.MkDir()
58 ssh := filepath.Join(fakebin, "ssh")
59 switch input := input.(type) {
60 case nil:
61 case string:
62 sshexpectedinput := ssh + ".expected-input"
63 err := ioutil.WriteFile(sshexpectedinput, []byte(input), 0644)
64 c.Assert(err, gc.IsNil)
65 default:
66 c.Errorf("input has invalid type: %T", input)
67 }
68 var stdout, stderr string
69 switch output := output.(type) {
70 case nil:
71 case string:
72 stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output)
73 case []string:
74 c.Assert(output, gc.HasLen, 2)
75 stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output[0])
76 stderr = fmt.Sprintf("cat>&2<<EOF\n%s\nEOF", output[1])
77 }
78 script := fmt.Sprintf(sshscript, stdout, stderr, rc)
79 err := ioutil.WriteFile(ssh, []byte(script), 0777)
80 c.Assert(err, gc.IsNil)
81 return testbase.PatchEnvPathPrepend(fakebin)
82}
83
84// installDetectionFakeSSH installs a fake SSH command, which will respond
85// to the series/hardware detection script with the specified
86// series/arch.
87func installDetectionFakeSSH(c *gc.C, series, arch string) testbase.Restorer {
88 if series == "" {
89 series = "precise"
90 }
91 if arch == "" {
92 arch = "amd64"
93 }
94 detectionoutput := strings.Join([]string{
95 series,
96 arch,
97 "MemTotal: 4096 kB",
98 "processor: 0",
99 }, "\n")
100 return installFakeSSH(c, manual.DetectionScript, detectionoutput, 0)
101}
102
103// fakeSSH wraps the invocation of InstallFakeSSH based on the parameters.
104type fakeSSH struct {
105 Series string
106 Arch string
107
108 // Provisioned should be set to true if the fakeSSH script
109 // should respond to checkProvisioned with a non-empty result.
110 Provisioned bool
111
112 // exit code for the checkProvisioned script.
113 CheckProvisionedExitCode int
114
115 // exit code for the machine agent provisioning script.
116 ProvisionAgentExitCode int
117
118 // InitUbuntuUser should be set to true if the fakeSSH script
119 // should respond to an attempt to initialise the ubuntu user.
120 InitUbuntuUser bool
121
122 // there are conditions other than error in the above
123 // that might cause provisioning to not go ahead, such
124 // as tools being missing.
125 SkipProvisionAgent bool
126
127 // detection will be skipped if the series/hardware were
128 // detected ahead of time. This should always be set to
129 // true when testing Bootstrap.
130 SkipDetection bool
131}
132
133// install installs fake SSH commands, which will respond to
134// manual provisioning/bootstrapping commands with the specified
135// output and exit codes.
136func (r fakeSSH) install(c *gc.C) testbase.Restorer {
137 var restore testbase.Restorer
138 add := func(input, output interface{}, rc int) {
139 restore = restore.Add(installFakeSSH(c, input, output, rc))
140 }
141 if !r.SkipProvisionAgent {
142 add(nil, nil, r.ProvisionAgentExitCode)
143 }
144 if !r.SkipDetection {
145 restore.Add(installDetectionFakeSSH(c, r.Series, r.Arch))
146 }
147 var checkProvisionedOutput interface{}
148 if r.Provisioned {
149 checkProvisionedOutput = "/etc/init/jujud-machine-0.conf"
150 }
151 add(manual.CheckProvisionedScript, checkProvisionedOutput, r.CheckProvisionedExitCode)
152 if r.InitUbuntuUser {
153 add("", nil, 0)
154 }
155 return restore
156}
0157
=== modified file 'environs/manual/init.go'
--- environs/manual/init.go 2014-03-11 05:55:35 +0000
+++ environs/manual/init.go 2014-03-12 12:33:26 +0000
@@ -16,6 +16,15 @@
16 "launchpad.net/juju-core/utils/ssh"16 "launchpad.net/juju-core/utils/ssh"
17)17)
1818
19// detectionScript is the script to run on the remote machine to
20// detect the OS series and hardware characteristics.
21const detectionScript = `#!/bin/bash
22set -e
23lsb_release -cs
24uname -m
25grep MemTotal /proc/meminfo
26cat /proc/cpuinfo`
27
19// checkProvisionedScript is the script to run on the remote machine28// checkProvisionedScript is the script to run on the remote machine
20// to check if a machine has already been provisioned.29// to check if a machine has already been provisioned.
21//30//
@@ -132,13 +141,6 @@
132 {regexp.MustCompile("ppc64el|ppc64le"), "ppc64"},141 {regexp.MustCompile("ppc64el|ppc64le"), "ppc64"},
133}142}
134143
135const detectionScript = `#!/bin/bash
136set -e
137lsb_release -cs
138uname -m
139grep MemTotal /proc/meminfo
140cat /proc/cpuinfo`
141
142// InitUbuntuUser adds the ubuntu user if it doesn't144// InitUbuntuUser adds the ubuntu user if it doesn't
143// already exist, updates its ~/.ssh/authorized_keys,145// already exist, updates its ~/.ssh/authorized_keys,
144// and enables passwordless sudo for it.146// and enables passwordless sudo for it.
145147
=== modified file 'environs/manual/init_test.go'
--- environs/manual/init_test.go 2014-02-07 13:58:06 +0000
+++ environs/manual/init_test.go 2014-03-12 12:33:26 +0000
@@ -1,16 +1,16 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package manual4package manual_test
55
6import (6import (
7 "strings"7 "strings"
88
9 gc "launchpad.net/gocheck"9 gc "launchpad.net/gocheck"
1010
11 "launchpad.net/juju-core/environs/manual"
11 jc "launchpad.net/juju-core/testing/checkers"12 jc "launchpad.net/juju-core/testing/checkers"
12 "launchpad.net/juju-core/testing/testbase"13 "launchpad.net/juju-core/testing/testbase"
13 sshtesting "launchpad.net/juju-core/utils/ssh/testing"
14)14)
1515
16type initialisationSuite struct {16type initialisationSuite struct {
@@ -26,8 +26,8 @@
26 "MemTotal: 4096 kB",26 "MemTotal: 4096 kB",
27 "processor: 0",27 "processor: 0",
28 }, "\n")28 }, "\n")
29 defer sshtesting.InstallFakeSSH(c, detectionScript, response, 0)()29 defer installFakeSSH(c, manual.DetectionScript, response, 0)()
30 _, series, err := DetectSeriesAndHardwareCharacteristics("whatever")30 _, series, err := manual.DetectSeriesAndHardwareCharacteristics("whatever")
31 c.Assert(err, gc.IsNil)31 c.Assert(err, gc.IsNil)
32 c.Assert(series, gc.Equals, "edgy")32 c.Assert(series, gc.Equals, "edgy")
33}33}
@@ -41,12 +41,12 @@
41 }, "\n")41 }, "\n")
42 // if the script fails for whatever reason, then checkProvisioned42 // if the script fails for whatever reason, then checkProvisioned
43 // will return an error. stderr will be included in the error message.43 // will return an error. stderr will be included in the error message.
44 defer sshtesting.InstallFakeSSH(c, detectionScript, []string{scriptResponse, "oh noes"}, 33)()44 defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "oh noes"}, 33)()
45 hc, _, err := DetectSeriesAndHardwareCharacteristics("hostname")45 hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname")
46 c.Assert(err, gc.ErrorMatches, "rc: 33 \\(oh noes\\)")46 c.Assert(err, gc.ErrorMatches, "rc: 33 \\(oh noes\\)")
47 // if the script doesn't fail, stderr is simply ignored.47 // if the script doesn't fail, stderr is simply ignored.
48 defer sshtesting.InstallFakeSSH(c, detectionScript, []string{scriptResponse, "non-empty-stderr"}, 0)()48 defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "non-empty-stderr"}, 0)()
49 hc, _, err = DetectSeriesAndHardwareCharacteristics("hostname")49 hc, _, err = manual.DetectSeriesAndHardwareCharacteristics("hostname")
50 c.Assert(err, gc.IsNil)50 c.Assert(err, gc.IsNil)
51 c.Assert(hc.String(), gc.Equals, "arch=arm cpu-cores=1 mem=4M")51 c.Assert(hc.String(), gc.Equals, "arch=arm cpu-cores=1 mem=4M")
52}52}
@@ -106,52 +106,52 @@
106 for i, test := range tests {106 for i, test := range tests {
107 c.Logf("test %d: %s", i, test.summary)107 c.Logf("test %d: %s", i, test.summary)
108 scriptResponse := strings.Join(test.scriptResponse, "\n")108 scriptResponse := strings.Join(test.scriptResponse, "\n")
109 defer sshtesting.InstallFakeSSH(c, detectionScript, scriptResponse, 0)()109 defer installFakeSSH(c, manual.DetectionScript, scriptResponse, 0)()
110 hc, _, err := DetectSeriesAndHardwareCharacteristics("hostname")110 hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname")
111 c.Assert(err, gc.IsNil)111 c.Assert(err, gc.IsNil)
112 c.Assert(hc.String(), gc.Equals, test.expectedHc)112 c.Assert(hc.String(), gc.Equals, test.expectedHc)
113 }113 }
114}114}
115115
116func (s *initialisationSuite) TestCheckProvisioned(c *gc.C) {116func (s *initialisationSuite) TestCheckProvisioned(c *gc.C) {
117 defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, "", 0)()117 defer installFakeSSH(c, manual.CheckProvisionedScript, "", 0)()
118 provisioned, err := checkProvisioned("example.com")118 provisioned, err := manual.CheckProvisioned("example.com")
119 c.Assert(err, gc.IsNil)119 c.Assert(err, gc.IsNil)
120 c.Assert(provisioned, jc.IsFalse)120 c.Assert(provisioned, jc.IsFalse)
121121
122 defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, "non-empty", 0)()122 defer installFakeSSH(c, manual.CheckProvisionedScript, "non-empty", 0)()
123 provisioned, err = checkProvisioned("example.com")123 provisioned, err = manual.CheckProvisioned("example.com")
124 c.Assert(err, gc.IsNil)124 c.Assert(err, gc.IsNil)
125 c.Assert(provisioned, jc.IsTrue)125 c.Assert(provisioned, jc.IsTrue)
126126
127 // stderr should not affect result.127 // stderr should not affect result.
128 defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, []string{"", "non-empty-stderr"}, 0)()128 defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"", "non-empty-stderr"}, 0)()
129 provisioned, err = checkProvisioned("example.com")129 provisioned, err = manual.CheckProvisioned("example.com")
130 c.Assert(err, gc.IsNil)130 c.Assert(err, gc.IsNil)
131 c.Assert(provisioned, jc.IsFalse)131 c.Assert(provisioned, jc.IsFalse)
132132
133 // if the script fails for whatever reason, then checkProvisioned133 // if the script fails for whatever reason, then checkProvisioned
134 // will return an error. stderr will be included in the error message.134 // will return an error. stderr will be included in the error message.
135 defer sshtesting.InstallFakeSSH(c, checkProvisionedScript, []string{"non-empty-stdout", "non-empty-stderr"}, 255)()135 defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"non-empty-stdout", "non-empty-stderr"}, 255)()
136 _, err = checkProvisioned("example.com")136 _, err = manual.CheckProvisioned("example.com")
137 c.Assert(err, gc.ErrorMatches, "rc: 255 \\(non-empty-stderr\\)")137 c.Assert(err, gc.ErrorMatches, "rc: 255 \\(non-empty-stderr\\)")
138}138}
139139
140func (s *initialisationSuite) TestInitUbuntuUserNonExisting(c *gc.C) {140func (s *initialisationSuite) TestInitUbuntuUserNonExisting(c *gc.C) {
141 defer sshtesting.InstallFakeSSH(c, "", "", 0)() // successful creation of ubuntu user141 defer installFakeSSH(c, "", "", 0)() // successful creation of ubuntu user
142 defer sshtesting.InstallFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login142 defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
143 err := InitUbuntuUser("testhost", "testuser", "", nil, nil)143 err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
144 c.Assert(err, gc.IsNil)144 c.Assert(err, gc.IsNil)
145}145}
146146
147func (s *initialisationSuite) TestInitUbuntuUserExisting(c *gc.C) {147func (s *initialisationSuite) TestInitUbuntuUserExisting(c *gc.C) {
148 defer sshtesting.InstallFakeSSH(c, "", nil, 0)()148 defer installFakeSSH(c, "", nil, 0)()
149 InitUbuntuUser("testhost", "testuser", "", nil, nil)149 manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
150}150}
151151
152func (s *initialisationSuite) TestInitUbuntuUserError(c *gc.C) {152func (s *initialisationSuite) TestInitUbuntuUserError(c *gc.C) {
153 defer sshtesting.InstallFakeSSH(c, "", []string{"", "failed to create ubuntu user"}, 123)()153 defer installFakeSSH(c, "", []string{"", "failed to create ubuntu user"}, 123)()
154 defer sshtesting.InstallFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login154 defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login
155 err := InitUbuntuUser("testhost", "testuser", "", nil, nil)155 err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil)
156 c.Assert(err, gc.ErrorMatches, "rc: 123 \\(failed to create ubuntu user\\)")156 c.Assert(err, gc.ErrorMatches, "rc: 123 \\(failed to create ubuntu user\\)")
157}157}
158158
=== modified file 'environs/manual/provisioner_test.go'
--- environs/manual/provisioner_test.go 2014-02-07 13:58:06 +0000
+++ environs/manual/provisioner_test.go 2014-03-12 12:33:26 +0000
@@ -1,7 +1,7 @@
1// Copyright 2013 Canonical Ltd.1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package manual4package manual_test
55
6import (6import (
7 "fmt"7 "fmt"
@@ -9,6 +9,7 @@
99
10 gc "launchpad.net/gocheck"10 gc "launchpad.net/gocheck"
1111
12 "launchpad.net/juju-core/environs/manual"
12 envtesting "launchpad.net/juju-core/environs/testing"13 envtesting "launchpad.net/juju-core/environs/testing"
13 "launchpad.net/juju-core/instance"14 "launchpad.net/juju-core/instance"
14 "launchpad.net/juju-core/juju/testing"15 "launchpad.net/juju-core/juju/testing"
@@ -25,10 +26,10 @@
2526
26var _ = gc.Suite(&provisionerSuite{})27var _ = gc.Suite(&provisionerSuite{})
2728
28func (s *provisionerSuite) getArgs(c *gc.C) ProvisionMachineArgs {29func (s *provisionerSuite) getArgs(c *gc.C) manual.ProvisionMachineArgs {
29 hostname, err := os.Hostname()30 hostname, err := os.Hostname()
30 c.Assert(err, gc.IsNil)31 c.Assert(err, gc.IsNil)
31 return ProvisionMachineArgs{32 return manual.ProvisionMachineArgs{
32 Host: hostname,33 Host: hostname,
33 EnvName: "dummyenv",34 EnvName: "dummyenv",
34 }35 }
@@ -50,7 +51,7 @@
50 SkipProvisionAgent: true,51 SkipProvisionAgent: true,
51 }.install(c).Restore()52 }.install(c).Restore()
52 // Attempt to provision a machine with no tools available, expect it to fail.53 // Attempt to provision a machine with no tools available, expect it to fail.
53 machineId, err := ProvisionMachine(args)54 machineId, err := manual.ProvisionMachine(args)
54 c.Assert(err, jc.Satisfies, params.IsCodeNotFound)55 c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
55 c.Assert(machineId, gc.Equals, "")56 c.Assert(machineId, gc.Equals, "")
5657
@@ -68,7 +69,7 @@
68 InitUbuntuUser: true,69 InitUbuntuUser: true,
69 ProvisionAgentExitCode: errorCode,70 ProvisionAgentExitCode: errorCode,
70 }.install(c).Restore()71 }.install(c).Restore()
71 machineId, err = ProvisionMachine(args)72 machineId, err = manual.ProvisionMachine(args)
72 if errorCode != 0 {73 if errorCode != 0 {
73 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("rc: %d", errorCode))74 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("rc: %d", errorCode))
74 c.Assert(machineId, gc.Equals, "")75 c.Assert(machineId, gc.Equals, "")
@@ -94,8 +95,8 @@
94 SkipDetection: true,95 SkipDetection: true,
95 SkipProvisionAgent: true,96 SkipProvisionAgent: true,
96 }.install(c).Restore()97 }.install(c).Restore()
97 _, err = ProvisionMachine(args)98 _, err = manual.ProvisionMachine(args)
98 c.Assert(err, gc.Equals, ErrProvisioned)99 c.Assert(err, gc.Equals, manual.ErrProvisioned)
99 defer fakeSSH{100 defer fakeSSH{
100 Provisioned: true,101 Provisioned: true,
101 CheckProvisionedExitCode: 255,102 CheckProvisionedExitCode: 255,
@@ -103,7 +104,7 @@
103 SkipDetection: true,104 SkipDetection: true,
104 SkipProvisionAgent: true,105 SkipProvisionAgent: true,
105 }.install(c).Restore()106 }.install(c).Restore()
106 _, err = ProvisionMachine(args)107 _, err = manual.ProvisionMachine(args)
107 c.Assert(err, gc.ErrorMatches, "error checking if provisioned: rc: 255")108 c.Assert(err, gc.ErrorMatches, "error checking if provisioned: rc: 255")
108}109}
109110
@@ -115,7 +116,7 @@
115 Arch: arch,116 Arch: arch,
116 InitUbuntuUser: true,117 InitUbuntuUser: true,
117 }.install(c).Restore()118 }.install(c).Restore()
118 machineId, err := ProvisionMachine(s.getArgs(c))119 machineId, err := manual.ProvisionMachine(s.getArgs(c))
119 c.Assert(err, gc.IsNil)120 c.Assert(err, gc.IsNil)
120121
121 // Now check what we would've configured it with.122 // Now check what we would've configured it with.
122123
=== added directory 'environs/manual/testing'
=== modified file 'environs/open.go'
--- environs/open.go 2014-02-18 01:29:47 +0000
+++ environs/open.go 2014-03-12 12:33:26 +0000
@@ -46,16 +46,14 @@
46 ConfigFromEnvirons46 ConfigFromEnvirons
47)47)
4848
49// ConfigForName returns the configuration for the environment with the49// ConfigForName returns the configuration for the environment with
50// given name from the default environments file. If the name is blank,50// the given name from the default environments file. If the name is
51// the default environment will be used. If the configuration is not51// blank, the default environment will be used. If the configuration
52// found, an errors.NotFoundError is returned.52// is not found, an errors.NotFoundError is returned. If the given
53// If the given store contains an entry for the environment53// store contains an entry for the environment and it has associated
54// and it has associated bootstrap config, that configuration54// bootstrap config, that configuration will be returned.
55// will be returned.55// ConfigForName also returns where the configuration was sourced from
56// ConfigForName also returns where the configuration56// (this is also valid even when there is an error.
57// was sourced from (this is also valid even when there
58// is an error.
59func ConfigForName(name string, store configstore.Storage) (*config.Config, ConfigSource, error) {57func ConfigForName(name string, store configstore.Storage) (*config.Config, ConfigSource, error) {
60 envs, err := ReadEnvirons("")58 envs, err := ReadEnvirons("")
61 if err != nil {59 if err != nil {
@@ -86,6 +84,16 @@
86 return cfg, ConfigFromEnvirons, err84 return cfg, ConfigFromEnvirons, err
87}85}
8886
87// maybeNotBootstrapped takes an error and source, returned by
88// ConfigForName and returns ErrNotBootstrapped if it looks like the
89// environment is not bootstrapped, or err as-is otherwise.
90func maybeNotBootstrapped(err error, source ConfigSource) error {
91 if err != nil && source == ConfigFromEnvirons {
92 return ErrNotBootstrapped
93 }
94 return err
95}
96
89// NewFromName opens the environment with the given97// NewFromName opens the environment with the given
90// name from the default environments file. If the98// name from the default environments file. If the
91// name is blank, the default environment will be used.99// name is blank, the default environment will be used.
@@ -99,16 +107,16 @@
99 // configuration attributes don't exist which107 // configuration attributes don't exist which
100 // will be filled in by Prepare.108 // will be filled in by Prepare.
101 cfg, source, err := ConfigForName(name, store)109 cfg, source, err := ConfigForName(name, store)
102 if err != nil && source == ConfigFromEnvirons {110 if err := maybeNotBootstrapped(err, source); err != nil {
103 err = ErrNotBootstrapped111 return nil, err
104 }112 }
105 if err != nil {113 if err != nil {
106 return nil, err114 return nil, err
107 }115 }
108116
109 env, err := New(cfg)117 env, err := New(cfg)
110 if err != nil && source == ConfigFromEnvirons {118 if err := maybeNotBootstrapped(err, source); err != nil {
111 err = ErrNotBootstrapped119 return nil, err
112 }120 }
113 return env, err121 return env, err
114}122}
115123
=== modified file 'environs/sshstorage/storage.go'
--- environs/sshstorage/storage.go 2014-01-13 06:25:28 +0000
+++ environs/sshstorage/storage.go 2014-03-12 12:33:26 +0000
@@ -16,11 +16,15 @@
16 "strconv"16 "strconv"
17 "strings"17 "strings"
1818
19 "github.com/juju/loggo"
20
19 coreerrors "launchpad.net/juju-core/errors"21 coreerrors "launchpad.net/juju-core/errors"
20 "launchpad.net/juju-core/utils"22 "launchpad.net/juju-core/utils"
21 "launchpad.net/juju-core/utils/ssh"23 "launchpad.net/juju-core/utils/ssh"
22)24)
2325
26var logger = loggo.GetLogger("juju.environs.sshstorage")
27
24// base64LineLength is the default line length for wrapping28// base64LineLength is the default line length for wrapping
25// output generated by the base64 command line utility.29// output generated by the base64 command line utility.
26const base64LineLength = 7630const base64LineLength = 76
@@ -240,6 +244,7 @@
240244
241// Get implements storage.StorageReader.Get.245// Get implements storage.StorageReader.Get.
242func (s *SSHStorage) Get(name string) (io.ReadCloser, error) {246func (s *SSHStorage) Get(name string) (io.ReadCloser, error) {
247 logger.Debugf("getting %q from storage", name)
243 path, err := s.path(name)248 path, err := s.path(name)
244 if err != nil {249 if err != nil {
245 return nil, err250 return nil, err
@@ -305,6 +310,7 @@
305310
306// Put implements storage.StorageWriter.Put311// Put implements storage.StorageWriter.Put
307func (s *SSHStorage) Put(name string, r io.Reader, length int64) error {312func (s *SSHStorage) Put(name string, r io.Reader, length int64) error {
313 logger.Debugf("putting %q (len %d) to storage", name, length)
308 path, err := s.path(name)314 path, err := s.path(name)
309 if err != nil {315 if err != nil {
310 return err316 return err
311317
=== modified file 'environs/testing/storage.go'
--- environs/testing/storage.go 2014-03-04 16:55:38 +0000
+++ environs/testing/storage.go 2014-03-12 12:33:26 +0000
@@ -4,16 +4,13 @@
4package testing4package testing
55
6import (6import (
7 "fmt"
8 "io"7 "io"
98
10 gc "launchpad.net/gocheck"9 gc "launchpad.net/gocheck"
1110
12 "launchpad.net/juju-core/environs"
13 "launchpad.net/juju-core/environs/filestorage"11 "launchpad.net/juju-core/environs/filestorage"
14 "launchpad.net/juju-core/environs/httpstorage"12 "launchpad.net/juju-core/environs/httpstorage"
15 "launchpad.net/juju-core/environs/storage"13 "launchpad.net/juju-core/environs/storage"
16 "launchpad.net/juju-core/state"
17)14)
1815
19// CreateLocalTestStorage returns the listener, which needs to be closed, and16// CreateLocalTestStorage returns the listener, which needs to be closed, and
@@ -29,17 +26,3 @@
29 closer = listener26 closer = listener
30 return27 return
31}28}
32
33// GetEnvironStorage creates an Environ from the config in state and
34// returns its storage interface.
35func GetEnvironStorage(st *state.State) (storage.Storage, error) {
36 envConfig, err := st.EnvironConfig()
37 if err != nil {
38 return nil, fmt.Errorf("cannot get environment config: %v", err)
39 }
40 env, err := environs.New(envConfig)
41 if err != nil {
42 return nil, fmt.Errorf("cannot access environment: %v", err)
43 }
44 return env.Storage(), nil
45}
4629
=== added file 'environs/utils.go'
--- environs/utils.go 1970-01-01 00:00:00 +0000
+++ environs/utils.go 2014-03-12 12:33:26 +0000
@@ -0,0 +1,22 @@
1package environs
2
3import (
4 "fmt"
5
6 "launchpad.net/juju-core/environs/storage"
7 "launchpad.net/juju-core/state"
8)
9
10// GetStorage creates an Environ from the config in state and returns
11// its storage interface.
12func GetStorage(st *state.State) (storage.Storage, error) {
13 envConfig, err := st.EnvironConfig()
14 if err != nil {
15 return nil, fmt.Errorf("cannot get environment config: %v", err)
16 }
17 env, err := New(envConfig)
18 if err != nil {
19 return nil, fmt.Errorf("cannot access environment: %v", err)
20 }
21 return env.Storage(), nil
22}
023
=== modified file 'juju/conn_test.go'
--- juju/conn_test.go 2014-03-07 14:15:28 +0000
+++ juju/conn_test.go 2014-03-12 12:33:26 +0000
@@ -124,7 +124,7 @@
124 c.Assert(conn.Environ.Name(), gc.Equals, envName)124 c.Assert(conn.Environ.Name(), gc.Equals, envName)
125}125}
126126
127func (cs *NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) {127func (*NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) {
128 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{128 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{
129 "admin-secret": "side-effect secret",129 "admin-secret": "side-effect secret",
130 "secret": "pork",130 "secret": "pork",
@@ -175,7 +175,7 @@
175 c.Assert(err, gc.IsNil)175 c.Assert(err, gc.IsNil)
176}176}
177177
178func (cs *NewConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *gc.C) {178func (*NewConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *gc.C) {
179 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{179 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{
180 "secret": "pork",180 "secret": "pork",
181 })181 })
@@ -212,7 +212,7 @@
212 c.Assert(err, gc.IsNil)212 c.Assert(err, gc.IsNil)
213}213}
214214
215func (cs *NewConnSuite) TestConnWithPassword(c *gc.C) {215func (*NewConnSuite) TestConnWithPassword(c *gc.C) {
216 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{216 attrs := dummy.SampleConfig().Merge(coretesting.Attrs{
217 "admin-secret": "nutkin",217 "admin-secret": "nutkin",
218 })218 })
219219
=== modified file 'provider/azure/config.go'
--- provider/azure/config.go 2014-01-29 00:14:51 +0000
+++ provider/azure/config.go 2014-03-12 12:33:26 +0000
@@ -95,32 +95,40 @@
95 return cfg.Apply(envCfg.attrs)95 return cfg.Apply(envCfg.attrs)
96}96}
9797
98const boilerplateYAML = `98var boilerplateYAML = `
99# https://juju.ubuntu.com/docs/config-azure.html99# https://juju.ubuntu.com/docs/config-azure.html
100azure:100azure:
101 type: azure101 type: azure
102102
103 # location specifies the place where instances will be started, for103 # location specifies the place where instances will be started,
104 # example: West US, North Europe.104 # for example: West US, North Europe.
105 #
105 location: West US106 location: West US
106 107
107 # The following attributes specify Windows Azure Management information.108 # The following attributes specify Windows Azure Management
108 # See http://msdn.microsoft.com/en-us/library/windowsazure109 # information. See:
110 # http://msdn.microsoft.com/en-us/library/windowsazure
109 # for details.111 # for details.
112 #
110 management-subscription-id: <00000000-0000-0000-0000-000000000000>113 management-subscription-id: <00000000-0000-0000-0000-000000000000>
111 management-certificate-path: /home/me/azure.pem114 management-certificate-path: /home/me/azure.pem
112115
113 # storage-account-name holds Windows Azure Storage info.116 # storage-account-name holds Windows Azure Storage info.
117 #
114 storage-account-name: abcdefghijkl118 storage-account-name: abcdefghijkl
115119
116 # force-image-name overrides the OS image selection to use120 # force-image-name overrides the OS image selection to use a fixed
117 # a fixed image for all deployments. Most useful for developers.121 # image for all deployments. Most useful for developers.
122 #
118 # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB123 # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB
119124
120 # image-stream chooses a simplestreams stream to select OS images from,125 # image-stream chooses a simplestreams stream to select OS images
121 # for example daily or released images (or any other stream available on simplestreams).126 # from, for example daily or released images (or any other stream
127 # available on simplestreams).
128 #
122 # image-stream: "released"129 # image-stream: "released"
123`130
131`[1:]
124132
125func (prov azureEnvironProvider) BoilerplateConfig() string {133func (prov azureEnvironProvider) BoilerplateConfig() string {
126 return boilerplateYAML134 return boilerplateYAML
127135
=== modified file 'provider/common/bootstrap.go'
--- provider/common/bootstrap.go 2014-03-05 19:41:34 +0000
+++ provider/common/bootstrap.go 2014-03-12 12:33:26 +0000
@@ -405,18 +405,3 @@
405 }405 }
406 return bootstrap.SetBootstrapTools(env, possibleTools)406 return bootstrap.SetBootstrapTools(env, possibleTools)
407}407}
408
409// EnsureNotBootstrapped returns null if the environment is not bootstrapped,
410// and an error if it is or if the function was not able to tell.
411func EnsureNotBootstrapped(env environs.Environ) error {
412 _, err := bootstrap.LoadState(env.Storage())
413 // If there is no error loading the bootstrap state, then we are
414 // bootstrapped.
415 if err == nil {
416 return fmt.Errorf("environment is already bootstrapped")
417 }
418 if err == environs.ErrNotBootstrapped {
419 return nil
420 }
421 return err
422}
423408
=== modified file 'provider/ec2/ec2.go'
--- provider/ec2/ec2.go 2014-03-11 07:21:10 +0000
+++ provider/ec2/ec2.go 2014-03-12 12:33:26 +0000
@@ -179,18 +179,25 @@
179# https://juju.ubuntu.com/docs/config-aws.html179# https://juju.ubuntu.com/docs/config-aws.html
180amazon:180amazon:
181 type: ec2181 type: ec2
182 # region specifies the ec2 region. It defaults to us-east-1.182
183 # region specifies the EC2 region. It defaults to us-east-1.
184 #
183 # region: us-east-1185 # region: us-east-1
186
187 # access-key holds the EC2 access key. It defaults to the
188 # environment variable AWS_ACCESS_KEY_ID.
184 #189 #
185 # access-key holds the ec2 access key. It defaults to the environment
186 # variable AWS_ACCESS_KEY_ID.
187 # access-key: <secret>190 # access-key: <secret>
188 #191
189 # secret-key holds the ec2 secret key. It defaults to the environment192 # secret-key holds the EC2 secret key. It defaults to the
190 # variable AWS_SECRET_ACCESS_KEY.193 # environment variable AWS_SECRET_ACCESS_KEY.
191 #194 #
192 # image-stream chooses a simplestreams stream to select OS images from,195 # secret-key: <secret>
193 # for example daily or released images (or any other stream available on simplestreams).196
197 # image-stream chooses a simplestreams stream to select OS images
198 # from, for example daily or released images (or any other stream
199 # available on simplestreams).
200 #
194 # image-stream: "released"201 # image-stream: "released"
195202
196`[1:]203`[1:]
197204
=== modified file 'provider/joyent/config.go'
--- provider/joyent/config.go 2014-03-10 00:10:29 +0000
+++ provider/joyent/config.go 2014-03-12 12:33:26 +0000
@@ -14,25 +14,46 @@
1414
15// boilerplateConfig will be shown in help output, so please keep it up to15// boilerplateConfig will be shown in help output, so please keep it up to
16// date when you change environment configuration below.16// date when you change environment configuration below.
17const boilerplateConfig = `joyent:17var boilerplateConfig = `
18 type: joyent18joyent:
1919 type: joyent
20 # SDC config20
21 # Can be set via env variables, or specified here21 ## SDC config
22 # sdc-user: <secret>22
23 # Can be set via env variables, or specified here23 # sdc-user holds the SDC user. It can also be specified in the
24 # sdc-key-id: <secret>24 # SDC_ACCOUNT environment variable.
25 # region defaults to us-west-1, override if required25 #
26 # sdc-region: us-west-126 # sdc-user: <secret>
2727
28 # Manta config28 # sdc-key-id holds the fingerprint of one of user's SSH keys. It
29 # Can be set via env variables, or specified here29 # can also be specified in the SDC_KEY_ID environment variable.
30 # manta-user: <secret>30 #
31 # Can be set via env variables, or specified here31 # sdc-key-id: <secret>
32 # manta-key-id: <secret>32
33 # region defaults to us-east, override if required33 # sdc-region holds the region to use: us-west-1 (default),
34 # manta-region: us-east34 # us-east-1, us-sw-1, eu-ams-1. Override if required.
35`35 #
36 # sdc-region: us-west-1
37
38 ## Manta config
39
40 # manta-user holds the user's Manta account name. It can also be
41 # specified in the MANTA_USER environment variable.
42 #
43 # manta-user: <secret>
44
45 # manta-key-id holds the fingerprint of one of the user's SSH
46 # keys. It can also be specified in the MANTA_KEY_ID environment
47 # variable.
48 #
49 # manta-key-id: <secret>
50
51 # manta-region holds the Manta region to use. It defaults to
52 # us-east, override if required
53 #
54 # manta-region: us-east
55
56`[1:]
3657
37const (58const (
38 SdcAccount = "SDC_ACCOUNT"59 SdcAccount = "SDC_ACCOUNT"
3960
=== modified file 'provider/local/environprovider.go'
--- provider/local/environprovider.go 2014-03-09 20:48:29 +0000
+++ provider/local/environprovider.go 2014-03-12 12:33:26 +0000
@@ -230,17 +230,23 @@
230# https://juju.ubuntu.com/docs/config-local.html230# https://juju.ubuntu.com/docs/config-local.html
231local:231local:
232 type: local232 type: local
233 # Override the directory that is used for the storage files and database.233
234 # The default location is $JUJU_HOME/<ENV>.234 # root-dir holds the directory that is used for the storage files and
235 235 # database. The default location is $JUJU_HOME/<env-name>.
236 # $JUJU_HOME defaults to ~/.juju236 # $JUJU_HOME defaults to ~/.juju. Override if needed.
237 #
237 # root-dir: ~/.juju/local238 # root-dir: ~/.juju/local
238 239
239 # Override the storage port if you have multiple local providers, or if the240 # storage-port holds the port where the local provider starts the
240 # default port is used by another program.241 # HTTP file server. Override the value if you have multiple local
242 # providers, or if the default port is used by another program.
243 #
241 # storage-port: 8040244 # storage-port: 8040
242 245
243 # Override the network bridge if you have changed the default lxc bridge246 # network-bridge holds the name of the LXC network bridge to use.
247 # Override if the default LXC network bridge is different.
248 #
249 #
244 # network-bridge: lxcbr0250 # network-bridge: lxcbr0
245251
246`[1:]252`[1:]
247253
=== modified file 'provider/maas/environprovider.go'
--- provider/maas/environprovider.go 2014-03-05 19:41:34 +0000
+++ provider/maas/environprovider.go 2014-03-12 12:33:26 +0000
@@ -64,12 +64,14 @@
64# https://juju.ubuntu.com/docs/config-maas.html64# https://juju.ubuntu.com/docs/config-maas.html
65maas:65maas:
66 type: maas66 type: maas
67 67
68 # maas-server specifies the location of the MAAS server. It must68 # maas-server specifies the location of the MAAS server. It must
69 # specify the base path.69 # specify the base path.
70 #
70 maas-server: 'http://192.168.1.1/MAAS/'71 maas-server: 'http://192.168.1.1/MAAS/'
71 72
72 # maas-oauth holds the OAuth credentials from MAAS.73 # maas-oauth holds the OAuth credentials from MAAS.
74 #
73 maas-oauth: '<add your OAuth credentials from MAAS here>'75 maas-oauth: '<add your OAuth credentials from MAAS here>'
7476
75`[1:]77`[1:]
7678
=== modified file 'provider/manual/environ.go'
--- provider/manual/environ.go 2014-03-05 19:41:34 +0000
+++ provider/manual/environ.go 2014-03-12 12:33:26 +0000
@@ -188,6 +188,7 @@
188}188}
189189
190var newSSHStorage = func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) {190var newSSHStorage = func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) {
191 logger.Debugf("using ssh storage at host %q dir %q", sshHost, storageDir)
191 return sshstorage.NewSSHStorage(sshstorage.NewSSHStorageParams{192 return sshstorage.NewSSHStorage(sshstorage.NewSSHStorageParams{
192 Host: sshHost,193 Host: sshHost,
193 StorageDir: storageDir,194 StorageDir: storageDir,
194195
=== modified file 'provider/openstack/provider.go'
--- provider/openstack/provider.go 2014-03-11 07:21:10 +0000
+++ provider/openstack/provider.go 2014-03-12 12:33:26 +0000
@@ -67,59 +67,75 @@
67# https://juju.ubuntu.com/docs/config-openstack.html67# https://juju.ubuntu.com/docs/config-openstack.html
68openstack:68openstack:
69 type: openstack69 type: openstack
70 # use-floating-ip specifies whether a floating IP address is required70
71 # to give the nodes a public IP address. Some installations assign public IP71 # use-floating-ip specifies whether a floating IP address is
72 # addresses by default without requiring a floating IP address.72 # required to give the nodes a public IP address. Some
73 # installations assign public IP addresses by default without
74 # requiring a floating IP address.
75 #
73 # use-floating-ip: false76 # use-floating-ip: false
7477
75 # use-default-secgroup specifies whether new machine instances should have the "default"78 # use-default-secgroup specifies whether new machine instances
76 # Openstack security group assigned.79 # should have the "default" Openstack security group assigned.
80 #
77 # use-default-secgroup: false81 # use-default-secgroup: false
7882
79 # network specifies the network label or uuid to bring machines up on, in83 # network specifies the network label or uuid to bring machines up
80 # the case where multiple networks exist. It may be omitted otherwise.84 # on, in the case where multiple networks exist. It may be omitted
85 # otherwise.
86 #
81 # network: <your network label or uuid>87 # network: <your network label or uuid>
8288
83 # tools-metadata-url specifies the location of the Juju tools and metadata. It defaults to the89 # tools-metadata-url specifies the location of the Juju tools and
84 # global public tools metadata location https://streams.canonical.com/tools.90 # metadata. It defaults to the global public tools metadata
85 # tools-metadata-url: https://you-tools-metadata-url91 # location https://streams.canonical.com/tools.
8692 #
87 # image-metadata-url specifies the location of Ubuntu cloud image metadata. It defaults to the93 # tools-metadata-url: https://your-tools-metadata-url
88 # global public image metadata location https://cloud-images.ubuntu.com/releases.94
89 # image-metadata-url: https://you-tools-metadata-url95 # image-metadata-url specifies the location of Ubuntu cloud image
9096 # metadata. It defaults to the global public image metadata
91 # image-stream chooses a simplestreams stream to select OS images from,97 # location https://cloud-images.ubuntu.com/releases.
92 # for example daily or released images (or any other stream available on simplestreams).98 #
99 # image-metadata-url: https://your-tools-metadata-url
100
101 # image-stream chooses a simplestreams stream to select OS images
102 # from, for example daily or released images (or any other stream
103 # available on simplestreams).
104 #
93 # image-stream: "released"105 # image-stream: "released"
94106
95 # auth-url defaults to the value of the environment variable OS_AUTH_URL,107 # auth-url defaults to the value of the environment variable
96 # but can be specified here.108 # OS_AUTH_URL, but can be specified here.
109 #
97 # auth-url: https://yourkeystoneurl:443/v2.0/110 # auth-url: https://yourkeystoneurl:443/v2.0/
98111
99 # tenant-name holds the openstack tenant name. It defaults to112 # tenant-name holds the openstack tenant name. It defaults to the
100 # the environment variable OS_TENANT_NAME.113 # environment variable OS_TENANT_NAME.
114 #
101 # tenant-name: <your tenant name>115 # tenant-name: <your tenant name>
102116
103 # region holds the openstack region. It defaults to117 # region holds the openstack region. It defaults to the
104 # the environment variable OS_REGION_NAME.118 # environment variable OS_REGION_NAME.
119 #
105 # region: <your region>120 # region: <your region>
106121
107 # The auth-mode, username and password attributes122 # The auth-mode, username and password attributes are used for
108 # are used for userpass authentication (the default).123 # userpass authentication (the default).
109124 #
110 # auth-mode holds the authentication mode. For user-password125 # auth-mode holds the authentication mode. For user-password
111 # authentication, auth-mode should be "userpass" and username126 # authentication, auth-mode should be "userpass" and username and
112 # and password should be set appropriately; they default to127 # password should be set appropriately; they default to the
113 # the environment variables OS_USERNAME and OS_PASSWORD128 # environment variables OS_USERNAME and OS_PASSWORD respectively.
114 # respectively.129 #
115 # auth-mode: userpass130 # auth-mode: userpass
116 # username: <your username>131 # username: <your username>
117 # password: <secret>132 # password: <secret>
118 133
119 # For key-pair authentication, auth-mode should be "keypair"134 # For key-pair authentication, auth-mode should be "keypair" and
120 # and access-key and secret-key should be set appropriately; they default to135 # access-key and secret-key should be set appropriately; they
121 # the environment variables OS_ACCESS_KEY and OS_SECRET_KEY136 # default to the environment variables OS_ACCESS_KEY and
122 # respectively.137 # OS_SECRET_KEY respectively.
138 #
123 # auth-mode: keypair139 # auth-mode: keypair
124 # access-key: <secret>140 # access-key: <secret>
125 # secret-key: <secret>141 # secret-key: <secret>
@@ -127,45 +143,59 @@
127# https://juju.ubuntu.com/docs/config-hpcloud.html143# https://juju.ubuntu.com/docs/config-hpcloud.html
128hpcloud:144hpcloud:
129 type: openstack145 type: openstack
130 146
131 # use-floating-ip specifies whether a floating IP address is required147 # use-floating-ip specifies whether a floating IP address is
132 # to give the nodes a public IP address. Some installations assign public IP148 # required to give the nodes a public IP address. Some
133 # addresses by default without requiring a floating IP address.149 # installations assign public IP addresses by default without
150 # requiring a floating IP address.
151 #
134 # use-floating-ip: false152 # use-floating-ip: false
135153
136 # use-default-secgroup specifies whether new machine instances should have the "default"154 # use-default-secgroup specifies whether new machine instances
137 # Openstack security group assigned.155 # should have the "default" Openstack security group assigned.
156 #
138 # use-default-secgroup: false157 # use-default-secgroup: false
139158
140 # tenant-name holds the openstack tenant name. In HPCloud, this is 159 # tenant-name holds the openstack tenant name. In HPCloud, this is
141 # synonymous with the project-name It defaults to160 # synonymous with the project-name It defaults to the environment
142 # the environment variable OS_TENANT_NAME.161 # variable OS_TENANT_NAME.
162 #
143 # tenant-name: <your tenant name>163 # tenant-name: <your tenant name>
144 164
145 # auth-url holds the keystone url for authentication. 165 # image-stream chooses a simplestreams stream to select OS images
146 # It defaults to the value of the environment variable OS_AUTH_URL.166 # from, for example daily or released images (or any other stream
167 # available on simplestreams).
168 #
169 # image-stream: "released"
170
171 # auth-url holds the keystone url for authentication. It defaults
172 # to the value of the environment variable OS_AUTH_URL.
173 #
147 # auth-url: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/174 # auth-url: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/
148175
149 # region holds the HP Cloud region (e.g. az-1.region-a.geo-1). 176 # region holds the HP Cloud region (e.g. az-1.region-a.geo-1). It
150 # It defaults to the environment variable OS_REGION_NAME.177 # defaults to the environment variable OS_REGION_NAME.
178 #
151 # region: <your region>179 # region: <your region>
152 180
153 # auth-mode holds the authentication mode. For user-password181 # auth-mode holds the authentication mode. For user-password
154 # authentication, auth-mode should be "userpass" and username182 # authentication, auth-mode should be "userpass" and username and
155 # and password should be set appropriately; they default to183 # password should be set appropriately; they default to the
156 # the environment variables OS_USERNAME and OS_PASSWORD184 # environment variables OS_USERNAME and OS_PASSWORD respectively.
157 # respectively.185 #
158 # auth-mode: userpass186 # auth-mode: userpass
159 # username: <your_username>187 # username: <your_username>
160 # password: <your_password>188 # password: <your_password>
161 189
162 # For key-pair authentication, auth-mode should be "keypair"190 # For key-pair authentication, auth-mode should be "keypair" and
163 # and access-key and secret-key should be set appropriately; they default to191 # access-key and secret-key should be set appropriately; they
164 # the environment variables OS_ACCESS_KEY and OS_SECRET_KEY192 # default to the environment variables OS_ACCESS_KEY and
165 # respectively.193 # OS_SECRET_KEY respectively.
194 #
166 # auth-mode: keypair195 # auth-mode: keypair
167 # access-key: <secret>196 # access-key: <secret>
168 # secret-key: <secret>197 # secret-key: <secret>
198
169`[1:]199`[1:]
170}200}
171201
172202
=== modified file 'state/apiserver/charms.go'
--- state/apiserver/charms.go 2014-03-06 18:11:16 +0000
+++ state/apiserver/charms.go 2014-03-12 12:33:26 +0000
@@ -25,7 +25,7 @@
25 "github.com/errgo/errgo"25 "github.com/errgo/errgo"
2626
27 "launchpad.net/juju-core/charm"27 "launchpad.net/juju-core/charm"
28 envtesting "launchpad.net/juju-core/environs/testing"28 "launchpad.net/juju-core/environs"
29 "launchpad.net/juju-core/names"29 "launchpad.net/juju-core/names"
30 "launchpad.net/juju-core/state"30 "launchpad.net/juju-core/state"
31 "launchpad.net/juju-core/state/api/params"31 "launchpad.net/juju-core/state/api/params"
@@ -392,7 +392,7 @@
392 if _, err := repackagedArchive.Seek(0, 0); err != nil {392 if _, err := repackagedArchive.Seek(0, 0); err != nil {
393 return errgo.Annotate(err, "cannot rewind the charm file reader")393 return errgo.Annotate(err, "cannot rewind the charm file reader")
394 }394 }
395 storage, err := envtesting.GetEnvironStorage(h.state)395 storage, err := environs.GetStorage(h.state)
396 if err != nil {396 if err != nil {
397 return errgo.Annotate(err, "cannot access provider storage")397 return errgo.Annotate(err, "cannot access provider storage")
398 }398 }
@@ -455,7 +455,7 @@
455// saves the corresponding zip archive to the given charmArchivePath.455// saves the corresponding zip archive to the given charmArchivePath.
456func (h *charmsHandler) downloadCharm(name, charmArchivePath string) error {456func (h *charmsHandler) downloadCharm(name, charmArchivePath string) error {
457 // Get the provider storage.457 // Get the provider storage.
458 storage, err := envtesting.GetEnvironStorage(h.state)458 storage, err := environs.GetStorage(h.state)
459 if err != nil {459 if err != nil {
460 return errgo.Annotate(err, "cannot access provider storage")460 return errgo.Annotate(err, "cannot access provider storage")
461 }461 }
462462
=== modified file 'state/apiserver/charms_test.go'
--- state/apiserver/charms_test.go 2014-02-27 14:11:56 +0000
+++ state/apiserver/charms_test.go 2014-03-12 12:33:26 +0000
@@ -18,7 +18,7 @@
18 gc "launchpad.net/gocheck"18 gc "launchpad.net/gocheck"
1919
20 "launchpad.net/juju-core/charm"20 "launchpad.net/juju-core/charm"
21 envtesting "launchpad.net/juju-core/environs/testing"21 "launchpad.net/juju-core/environs"
22 jujutesting "launchpad.net/juju-core/juju/testing"22 jujutesting "launchpad.net/juju-core/juju/testing"
23 "launchpad.net/juju-core/state"23 "launchpad.net/juju-core/state"
24 "launchpad.net/juju-core/state/api/params"24 "launchpad.net/juju-core/state/api/params"
@@ -168,7 +168,7 @@
168 expectedSHA256, _, err := utils.ReadSHA256(tempFile)168 expectedSHA256, _, err := utils.ReadSHA256(tempFile)
169 c.Assert(err, gc.IsNil)169 c.Assert(err, gc.IsNil)
170 name := charm.Quote(expectedURL.String())170 name := charm.Quote(expectedURL.String())
171 storage, err := envtesting.GetEnvironStorage(s.State)171 storage, err := environs.GetStorage(s.State)
172 c.Assert(err, gc.IsNil)172 c.Assert(err, gc.IsNil)
173 expectedUploadURL, err := storage.URL(name)173 expectedUploadURL, err := storage.URL(name)
174 c.Assert(err, gc.IsNil)174 c.Assert(err, gc.IsNil)
@@ -219,7 +219,7 @@
219 // should succeed, because it was repackaged during upload to219 // should succeed, because it was repackaged during upload to
220 // strip nested dirs.220 // strip nested dirs.
221 archiveName := strings.TrimPrefix(sch.BundleURL().RequestURI(), "/dummyenv/private/")221 archiveName := strings.TrimPrefix(sch.BundleURL().RequestURI(), "/dummyenv/private/")
222 storage, err := envtesting.GetEnvironStorage(s.State)222 storage, err := environs.GetStorage(s.State)
223 c.Assert(err, gc.IsNil)223 c.Assert(err, gc.IsNil)
224 reader, err := storage.Get(archiveName)224 reader, err := storage.Get(archiveName)
225 c.Assert(err, gc.IsNil)225 c.Assert(err, gc.IsNil)
226226
=== modified file 'state/apiserver/client/client_test.go'
--- state/apiserver/client/client_test.go 2014-03-11 02:53:40 +0000
+++ state/apiserver/client/client_test.go 2014-03-12 12:33:26 +0000
@@ -17,10 +17,10 @@
17 coreCloudinit "launchpad.net/juju-core/cloudinit"17 coreCloudinit "launchpad.net/juju-core/cloudinit"
18 "launchpad.net/juju-core/cloudinit/sshinit"18 "launchpad.net/juju-core/cloudinit/sshinit"
19 "launchpad.net/juju-core/constraints"19 "launchpad.net/juju-core/constraints"
20 "launchpad.net/juju-core/environs"
20 "launchpad.net/juju-core/environs/cloudinit"21 "launchpad.net/juju-core/environs/cloudinit"
21 "launchpad.net/juju-core/environs/config"22 "launchpad.net/juju-core/environs/config"
22 "launchpad.net/juju-core/environs/storage"23 envstorage "launchpad.net/juju-core/environs/storage"
23 envtesting "launchpad.net/juju-core/environs/testing"
24 ttesting "launchpad.net/juju-core/environs/tools/testing"24 ttesting "launchpad.net/juju-core/environs/tools/testing"
25 "launchpad.net/juju-core/errors"25 "launchpad.net/juju-core/errors"
26 "launchpad.net/juju-core/instance"26 "launchpad.net/juju-core/instance"
@@ -1872,7 +1872,7 @@
1872 // contains the correct data.1872 // contains the correct data.
1873 sch, err := s.State.Charm(curl)1873 sch, err := s.State.Charm(curl)
1874 c.Assert(err, gc.IsNil)1874 c.Assert(err, gc.IsNil)
1875 storage, err := envtesting.GetEnvironStorage(s.State)1875 storage, err := environs.GetStorage(s.State)
1876 c.Assert(err, gc.IsNil)1876 c.Assert(err, gc.IsNil)
1877 uploads, err := storage.List(fmt.Sprintf("%s-%d-", curl.Name, curl.Revision))1877 uploads, err := storage.List(fmt.Sprintf("%s-%d-", curl.Name, curl.Revision))
1878 c.Assert(err, gc.IsNil)1878 c.Assert(err, gc.IsNil)
@@ -1938,7 +1938,7 @@
1938 }1938 }
1939}1939}
19401940
1941func (s *clientSuite) assertUploaded(c *gc.C, storage storage.Storage, bundleURL *url.URL, expectedSHA256 string) {1941func (s *clientSuite) assertUploaded(c *gc.C, storage envstorage.Storage, bundleURL *url.URL, expectedSHA256 string) {
1942 archiveName := getArchiveName(bundleURL)1942 archiveName := getArchiveName(bundleURL)
1943 reader, err := storage.Get(archiveName)1943 reader, err := storage.Get(archiveName)
1944 c.Assert(err, gc.IsNil)1944 c.Assert(err, gc.IsNil)
19451945
=== removed file 'utils/ssh/testing/fakessh.go'
--- utils/ssh/testing/fakessh.go 2014-02-18 17:08:55 +0000
+++ utils/ssh/testing/fakessh.go 1970-01-01 00:00:00 +0000
@@ -1,80 +0,0 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package testing
5
6import (
7 "fmt"
8 "io/ioutil"
9 "path/filepath"
10
11 gc "launchpad.net/gocheck"
12
13 "launchpad.net/juju-core/testing/testbase"
14)
15
16// sshscript should only print the result on the first execution,
17// to handle the case where it's called multiple times. On
18// subsequent executions, it should find the next 'ssh' in $PATH
19// and exec that.
20var sshscript = `#!/bin/bash --norc
21if [ ! -e "$0.run" ]; then
22 touch "$0.run"
23 if [ -e "$0.expected-input" ]; then
24 diff "$0.expected-input" -
25 exitcode=$?
26 if [ $exitcode -ne 0 ]; then
27 echo "ERROR: did not match expected input" >&2
28 exit $exitcode
29 fi
30 else
31 head >/dev/null
32 fi
33 # stdout
34 %s
35 # stderr
36 %s
37 exit %d
38else
39 export PATH=${PATH#*:}
40 exec ssh $*
41fi`
42
43// InstallFakeSSH creates a fake "ssh" command in a new $PATH,
44// updates $PATH, and returns a function to reset $PATH to its
45// original value when called.
46//
47// input may be:
48// - nil (ignore input)
49// - a string (match input exactly)
50// output may be:
51// - nil (no output)
52// - a string (stdout)
53// - a slice of strings, of length two (stdout, stderr)
54func InstallFakeSSH(c *gc.C, input, output interface{}, rc int) testbase.Restorer {
55 fakebin := c.MkDir()
56 ssh := filepath.Join(fakebin, "ssh")
57 switch input := input.(type) {
58 case nil:
59 case string:
60 sshexpectedinput := ssh + ".expected-input"
61 err := ioutil.WriteFile(sshexpectedinput, []byte(input), 0644)
62 c.Assert(err, gc.IsNil)
63 default:
64 c.Errorf("input has invalid type: %T", input)
65 }
66 var stdout, stderr string
67 switch output := output.(type) {
68 case nil:
69 case string:
70 stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output)
71 case []string:
72 c.Assert(output, gc.HasLen, 2)
73 stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output[0])
74 stderr = fmt.Sprintf("cat>&2<<EOF\n%s\nEOF", output[1])
75 }
76 script := fmt.Sprintf(sshscript, stdout, stderr, rc)
77 err := ioutil.WriteFile(ssh, []byte(script), 0777)
78 c.Assert(err, gc.IsNil)
79 return testbase.PatchEnvPathPrepend(fakebin)
80}

Subscribers

People subscribed via source and target branches

to status/vote changes: