Permalink
Browse files

Merge pull request #4237 from axw/cloud-credentials-bootstrap2

Bootstrap with (public-)clouds/credentials.yaml

Update bootstrap to use clouds.yaml and credentials.yaml.
The primary difference from the spec is that we will do
an auto-detection of credentials if none are specified
and there is no default.

Full support for cloud credentials has been added to the
following providers so far:
 - dummy (for unit tests)
 - lxd
 - ec2
 - gce
 - openstack

(Review request: http://reviews.vapour.ws/r/3677/)
  • Loading branch information...
2 parents 3c4c5d6 + 99f72f4 commit b033024f0dc55e2bc371978828e7301a532fc926 @jujubot jujubot committed Jan 29, 2016
Showing with 977 additions and 578 deletions.
  1. +8 −2 apiserver/addresser/addresser_test.go
  2. +4 −1 apiserver/common/environwatcher_test.go
  3. +2 −2 apiserver/environmentmanager/environmentmanager_test.go
  4. +4 −1 apiserver/imagemetadata/updatefrompublished_test.go
  5. +24 −0 cloud/clouds.go
  6. +49 −0 cloud/credentials.go
  7. +182 −65 cmd/juju/commands/bootstrap.go
  8. +142 −163 cmd/juju/commands/bootstrap_test.go
  9. +0 −44 cmd/juju/commands/common.go
  10. +31 −0 cmd/juju/commands/flags.go
  11. +8 −2 cmd/plugins/juju-metadata/imagemetadata.go
  12. +5 −1 cmd/plugins/juju-metadata/toolsmetadata_test.go
  13. +1 −0 cmd/plugins/juju-metadata/validateimagemetadata_test.go
  14. +0 −23 environs/bootstrap/bootstrap.go
  15. +3 −4 environs/errors.go
  16. +4 −1 environs/imagemetadata_test.go
  17. +35 −12 environs/jujutest/livetests.go
  18. +17 −16 environs/jujutest/tests.go
  19. +29 −21 environs/open.go
  20. +11 −10 environs/open_test.go
  21. +1 −1 environs/tools/tools_test.go
  22. +1 −1 environs/tools/urls_test.go
  23. +2 −2 juju/api_test.go
  24. +10 −1 juju/testing/conn.go
  25. +8 −2 provider/dummy/config_test.go
  26. +6 −5 provider/dummy/environs.go
  27. +4 −3 provider/dummy/environs_test.go
  28. +33 −0 provider/ec2/config_test.go
  29. +16 −1 provider/ec2/ebs_test.go
  30. +33 −4 provider/ec2/local_test.go
  31. +27 −3 provider/ec2/provider.go
  32. +23 −0 provider/gce/credentials.go
  33. +9 −5 provider/gce/google/config.go
  34. +5 −0 provider/gce/google/config_credentials_test.go
  35. +27 −0 provider/gce/provider.go
  36. +10 −0 provider/gce/provider_test.go
  37. +4 −1 provider/joyent/export_test.go
  38. +14 −36 provider/openstack/config.go
  39. +106 −134 provider/openstack/config_test.go
  40. +46 −8 provider/openstack/local_test.go
  41. +33 −3 provider/openstack/provider.go
@@ -275,7 +275,10 @@ func (s *AddresserSuite) TestWatchIPAddresses(c *gc.C) {
func testingEnvConfig(c *gc.C) *config.Config {
cfg, err := config.New(config.NoDefaults, dummy.SampleConfig())
c.Assert(err, jc.ErrorIsNil)
- env, err := environs.Prepare(cfg, envcmd.BootstrapContext(coretesting.Context(c)), configstore.NewMem())
+ env, err := environs.Prepare(
+ envcmd.BootstrapContext(coretesting.Context(c)), configstore.NewMem(),
+ "dummycontroller", environs.PrepareForBootstrapParams{Config: cfg},
+ )
c.Assert(err, jc.ErrorIsNil)
return env.Config()
}
@@ -296,7 +299,10 @@ func nonexTestingEnvConfig(c *gc.C) *config.Config {
func mockTestingEnvConfig(c *gc.C) *config.Config {
cfg, err := config.New(config.NoDefaults, mockConfig())
c.Assert(err, jc.ErrorIsNil)
- env, err := environs.Prepare(cfg, envcmd.BootstrapContext(coretesting.Context(c)), configstore.NewMem())
+ env, err := environs.Prepare(
+ envcmd.BootstrapContext(coretesting.Context(c)), configstore.NewMem(),
+ "dummycontroller", environs.PrepareForBootstrapParams{Config: cfg},
+ )
c.Assert(err, jc.ErrorIsNil)
return env.Config()
}
@@ -125,7 +125,10 @@ func (*environWatcherSuite) TestEnvironConfigMaskedSecrets(c *gc.C) {
func testingEnvConfig(c *gc.C) *config.Config {
cfg, err := config.New(config.NoDefaults, dummy.SampleConfig())
c.Assert(err, jc.ErrorIsNil)
- env, err := environs.Prepare(cfg, envcmd.BootstrapContext(testing.Context(c)), configstore.NewMem())
+ env, err := environs.Prepare(
+ envcmd.BootstrapContext(testing.Context(c)), configstore.NewMem(),
+ "dummycontroller", environs.PrepareForBootstrapParams{Config: cfg},
+ )
c.Assert(err, jc.ErrorIsNil)
return env.Config()
}
@@ -211,9 +211,9 @@ func (s *envManagerSuite) TestCreateEnvironmentValidatesConfig(c *gc.C) {
admin := s.AdminUserTag(c)
s.setAPIUser(c, admin)
args := s.createArgs(c, admin)
- delete(args.Config, "state-server")
+ args.Config["state-server"] = "maybe"
_, err := s.envmanager.CreateEnvironment(args)
- c.Assert(err, gc.ErrorMatches, "provider validation failed: state-server: expected bool, got nothing")
+ c.Assert(err, gc.ErrorMatches, "provider validation failed: state-server: expected bool, got string\\(\"maybe\"\\)")
}
func (s *envManagerSuite) TestCreateEnvironmentBadConfig(c *gc.C) {
@@ -178,7 +178,10 @@ func (s *imageMetadataUpdateSuite) TestUpdateFromPublishedImages(c *gc.C) {
s.calls = append(s.calls, environConfig)
cfg, err := config.New(config.NoDefaults, dummy.SampleConfig())
c.Assert(err, jc.ErrorIsNil)
- env, err := environs.Prepare(cfg, envcmd.BootstrapContext(testing.Context(c)), configstore.NewMem())
+ env, err := environs.Prepare(
+ envcmd.BootstrapContext(testing.Context(c)), configstore.NewMem(),
+ "dummycontroller", environs.PrepareForBootstrapParams{Config: cfg},
+ )
c.Assert(err, jc.ErrorIsNil)
return env.Config(), err
}
View
@@ -73,6 +73,30 @@ type Region struct {
Endpoint string `yaml:"endpoint,omitempty"`
}
+// CloudByName returns the cloud with the specified name.
+// If there exists no cloud with the specified name, an
+// error satisfying errors.IsNotFound will be returned.
+//
+// TODO(axw) write unit tests for this.
+func CloudByName(name string) (*Cloud, error) {
+ // Personal clouds take precedence.
+ personalClouds, err := PersonalCloudMetadata()
+ if err != nil {
+ return nil, errors.Trace(err)
+ }
+ if cloud, ok := personalClouds[name]; ok {
+ return &cloud, nil
+ }
+ clouds, _, err := PublicCloudMetadata(JujuPublicCloudsPath())
+ if err != nil {
+ return nil, errors.Trace(err)
+ }
+ if cloud, ok := clouds[name]; ok {
+ return &cloud, nil
+ }
+ return nil, errors.NotFoundf("cloud %s", name)
+}
+
// JujuPublicCloudsPath is the location where public cloud information is
// expected to be found. Requires JUJU_HOME to be set.
func JujuPublicCloudsPath() string {
View
@@ -4,6 +4,8 @@
package cloud
import (
+ "io/ioutil"
+ "os"
"strings"
"github.com/juju/errors"
@@ -181,6 +183,53 @@ func (c cloudCredentialValueChecker) Coerce(v interface{}, path []string) (inter
return Credential{AuthType(authType), attrs}, nil
}
+// CredentialByName returns the credential and default region to use for the
+// specified cloud, optionally specifying a credential name. If no credential
+// name is specified, then use the default credential for the cloud if one has
+// been specified. The credential name is returned also, in case the default
+// credential is used.
+//
+// If there exists no matching credentials, an error satisfying
+// errors.IsNotFound will be returned.
+//
+// NOTE: the credential returned is not validated. The caller must validate
+// the credential with the cloud provider.
+//
+// TODO(axw) write unit tests for this.
+func CredentialByName(
+ cloudName, credentialName string,
+) (_ *Credential, credentialNameUsed string, defaultRegion string, _ error) {
+
+ // Parse the credentials, and extract the credentials for the specified
+ // cloud.
+ credentialsData, err := ioutil.ReadFile(JujuCredentials())
+ if os.IsNotExist(err) {
+ return nil, "", "", errors.NotFoundf("credentials file")
+ } else if err != nil {
+ return nil, "", "", errors.Trace(err)
+ }
+ credentials, err := ParseCredentials(credentialsData)
+ if err != nil {
+ return nil, "", "", errors.Annotate(err, "parsing credentials")
+ }
+ cloudCredentials, ok := credentials.Credentials[cloudName]
+ if !ok {
+ return nil, "", "", errors.NotFoundf("credentials for cloud %q", cloudName)
+ }
+
+ if credentialName == "" {
+ // No credential specified, so use the default for the cloud.
+ credentialName = cloudCredentials.DefaultCredential
+ }
+ credential, ok := cloudCredentials.AuthCredentials[credentialName]
+ if !ok {
+ return nil, "", "", errors.NotFoundf(
+ "%q credential for cloud %q", credentialName, cloudName,
+ )
+ }
+ return &credential, credentialName, cloudCredentials.DefaultRegion, nil
+}
+
// JujuCredentials is the location where credentials are
// expected to be found. Requires JUJU_HOME to be set.
func JujuCredentials() string {
Oops, something went wrong.

0 comments on commit b033024

Please sign in to comment.