github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/maas/environprovider.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package maas 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/url" 10 11 "github.com/juju/errors" 12 "github.com/juju/gomaasapi" 13 "github.com/juju/loggo" 14 15 "github.com/juju/juju/cloud" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/config" 18 ) 19 20 // Logger for the MAAS provider. 21 var logger = loggo.GetLogger("juju.provider.maas") 22 23 type maasEnvironProvider struct { 24 environProviderCredentials 25 } 26 27 var _ environs.EnvironProvider = (*maasEnvironProvider)(nil) 28 29 var providerInstance maasEnvironProvider 30 31 func (maasEnvironProvider) Open(args environs.OpenParams) (environs.Environ, error) { 32 logger.Debugf("opening model %q.", args.Config.Name()) 33 if err := validateCloudSpec(args.Cloud); err != nil { 34 return nil, errors.Annotate(err, "validating cloud spec") 35 } 36 env, err := NewEnviron(args.Cloud, args.Config) 37 if err != nil { 38 return nil, err 39 } 40 return env, nil 41 } 42 43 var errAgentNameAlreadySet = errors.New( 44 "maas-agent-name is already set; this should not be set by hand") 45 46 // PrepareConfig is specified in the EnvironProvider interface. 47 func (p maasEnvironProvider) PrepareConfig(args environs.PrepareConfigParams) (*config.Config, error) { 48 if err := validateCloudSpec(args.Cloud); err != nil { 49 return nil, errors.Annotate(err, "validating cloud spec") 50 } 51 var attrs map[string]interface{} 52 if _, ok := args.Config.StorageDefaultBlockSource(); !ok { 53 attrs = map[string]interface{}{ 54 config.StorageDefaultBlockSourceKey: maasStorageProviderType, 55 } 56 } 57 if len(attrs) == 0 { 58 return args.Config, nil 59 } 60 return args.Config.Apply(attrs) 61 } 62 63 func verifyCredentials(env *maasEnviron) error { 64 // Verify we can connect to the server and authenticate. 65 if env.usingMAAS2() { 66 // The maas2 controller verifies credentials at creation time. 67 return nil 68 } 69 _, err := env.getMAASClient().GetSubObject("maas").CallGet("get_config", nil) 70 if err, ok := errors.Cause(err).(gomaasapi.ServerError); ok && err.StatusCode == http.StatusUnauthorized { 71 logger.Debugf("authentication failed: %v", err) 72 return errors.New(`authentication failed. 73 74 Please ensure the credentials are correct.`) 75 } 76 return nil 77 } 78 79 // DetectRegions is specified in the environs.CloudRegionDetector interface. 80 func (p maasEnvironProvider) DetectRegions() ([]cloud.Region, error) { 81 return nil, errors.NotFoundf("regions") 82 } 83 84 func validateCloudSpec(spec environs.CloudSpec) error { 85 if err := spec.Validate(); err != nil { 86 return errors.Trace(err) 87 } 88 if _, err := parseCloudEndpoint(spec.Endpoint); err != nil { 89 return errors.Annotate(err, "validating endpoint") 90 } 91 if spec.Credential == nil { 92 return errors.NotValidf("missing credential") 93 } 94 if authType := spec.Credential.AuthType(); authType != cloud.OAuth1AuthType { 95 return errors.NotSupportedf("%q auth-type", authType) 96 } 97 if _, err := parseOAuthToken(*spec.Credential); err != nil { 98 return errors.Annotate(err, "validating MAAS OAuth token") 99 } 100 return nil 101 } 102 103 func parseCloudEndpoint(endpoint string) (server string, _ error) { 104 // For MAAS, the cloud endpoint may be either a full URL 105 // for the MAAS server, or just the IP/host. 106 if endpoint == "" { 107 return "", errors.New("MAAS server not specified") 108 } 109 server = endpoint 110 if url, err := url.Parse(server); err != nil || url.Scheme == "" { 111 server = fmt.Sprintf("http://%s/MAAS", endpoint) 112 if _, err := url.Parse(server); err != nil { 113 return "", errors.NotValidf("endpoint %q", endpoint) 114 } 115 } 116 return server, nil 117 }