func authFromScratch(credsResult *CredentialsResult, serviceType string, urlType gophercloud.Availability, logger *logrus.Logger) (*gophercloud.ServiceClient, error) { logger.Info("Not using cache; Authenticating from scratch.\n") ao := credsResult.AuthOpts region := credsResult.Region pc, err := rackspace.AuthenticatedClient(*ao) if err != nil { switch err.(type) { case *tokens2.ErrNoPassword: return nil, errors.New("Please supply an API key.") } return nil, err } pc.HTTPClient = newHTTPClient() var sc *gophercloud.ServiceClient switch serviceType { case "compute": sc, err = rackspace.NewComputeV2(pc, gophercloud.EndpointOpts{ Region: region, Availability: urlType, }) break case "object-store": sc, err = rackspace.NewObjectStorageV1(pc, gophercloud.EndpointOpts{ Region: region, Availability: urlType, }) break case "blockstorage": sc, err = rackspace.NewBlockStorageV1(pc, gophercloud.EndpointOpts{ Region: region, Availability: urlType, }) break case "network": sc, err = rackspace.NewNetworkV2(pc, gophercloud.EndpointOpts{ Region: region, Availability: urlType, }) break } if err != nil { return nil, err } if sc == nil { return nil, fmt.Errorf("Unable to create service client: Unknown service type: %s\n", serviceType) } if sc.Endpoint == "/" { return nil, fmt.Errorf(strings.Join([]string{"You wanted to use service net for the %s request", "but the %s service doesn't have an internal URL.\n"}, " "), serviceType, serviceType) } logger.Debugf("Created %s service client: %+v", serviceType, sc) sc.UserAgent.Prepend(util.UserAgent) return sc, nil }
func authFromScratch(credsResult *CredentialsResult, serviceType string, logger *logrus.Logger) (*gophercloud.ServiceClient, error) { logger.Info("Not using cache; Authenticating from scratch.\n") ao := credsResult.AuthOpts region := credsResult.Region pc, err := rackspace.AuthenticatedClient(*ao) if err != nil { return nil, err } pc.HTTPClient = newHTTPClient() var sc *gophercloud.ServiceClient switch serviceType { case "compute": sc, err = rackspace.NewComputeV2(pc, gophercloud.EndpointOpts{ Region: region, }) break case "object-store": sc, err = rackspace.NewObjectStorageV1(pc, gophercloud.EndpointOpts{ Region: region, }) break case "blockstorage": sc, err = rackspace.NewBlockStorageV1(pc, gophercloud.EndpointOpts{ Region: region, }) break case "network": sc, err = rackspace.NewNetworkV2(pc, gophercloud.EndpointOpts{ Region: region, }) break } if err != nil { return nil, err } if sc == nil { return nil, fmt.Errorf("Unable to create service client: Unknown service type: %s\n", serviceType) } sc.UserAgent.Prepend(util.UserAgent) return sc, nil }
// NewClient creates and returns a Rackspace client for the given service. func NewClient(c *cli.Context, serviceType string, logger *logrus.Logger, noCache bool) (*gophercloud.ServiceClient, error) { // get the user's authentication credentials credsResult, err := Credentials(c, logger) if err != nil { return nil, err } if noCache { return authFromScratch(credsResult, serviceType, logger) } ao := credsResult.AuthOpts region := credsResult.Region // form the cache key cacheKey := CacheKey(*ao, region, serviceType) // initialize cache cache := &Cache{} logger.Infof("Looking in the cache for cache key: %s\n", cacheKey) // get the value from the cache creds, err := cache.Value(cacheKey) // if there was an error accessing the cache or there was nothing in the cache, // authenticate from scratch if err == nil && creds != nil { // we successfully retrieved a value from the cache logger.Infof("Using token from cache: %s\n", creds.TokenID) pc, err := rackspace.NewClient(ao.IdentityEndpoint) if err == nil { pc.TokenID = creds.TokenID pc.ReauthFunc = reauthFunc(pc, *ao) pc.UserAgent.Prepend(util.UserAgent) pc.HTTPClient = newHTTPClient() return &gophercloud.ServiceClient{ ProviderClient: pc, Endpoint: creds.ServiceEndpoint, }, nil } } else { return authFromScratch(credsResult, serviceType, logger) } return nil, nil }
// Credentials determines the appropriate authentication method for the user. // It returns a gophercloud.AuthOptions object and a region. // // It will use command-line authentication parameters if available, then it will // look for any unset parameters in the config file, and then finally in // environment variables. func Credentials(c *cli.Context, logger *logrus.Logger) (*CredentialsResult, error) { ao := &gophercloud.AuthOptions{ AllowReauth: true, } have := make(map[string]commandoptions.Cred) // let's looks for a region and identity endpoint want := map[string]string{ "auth-url": "", "region": "", } err := findAuthOpts(c, have, want) if err != nil { return nil, err } // if the user didn't provide an auth URL, default to the Rackspace US endpoint if _, ok := have["auth-url"]; !ok || have["auth-url"].Value == "" { have["auth-url"] = commandoptions.Cred{Value: rackspace.RackspaceUSIdentity, From: "default value"} delete(want, "auth-url") } ao.IdentityEndpoint = have["auth-url"].Value // upper-case the region region := strings.ToUpper(have["region"].Value) delete(want, "region") // now we check for token authentication (only allowed via the command-line) want["auth-tenant-id"] = "" want["auth-token"] = "" commandoptions.CLIopts(c, have, want) // if a tenant ID was provided on the command-line, we don't bother checking for a // username or api key if have["auth-tenant-id"].Value != "" || have["auth-token"].Value != "" { if tenantID, ok := have["auth-tenant-id"]; ok { ao.TenantID = tenantID.Value ao.TokenID = have["auth-token"].Value delete(want, "auth-token") } else { return nil, Err(have, want, tenantIDAuthErrSlice) } } else { // otherwise, let's look for a username and API key want = map[string]string{ "username": "", "api-key": "", } err = findAuthOpts(c, have, want) if err != nil { return nil, err } if have["username"].Value != "" || have["api-key"].Value != "" { if username, ok := have["username"]; ok { ao.Username = username.Value ao.APIKey = have["api-key"].Value delete(want, "api-key") } else { return nil, Err(have, want, usernameAuthErrSlice) } } else { return nil, Err(have, want, usernameAuthErrSlice) } } if logger != nil { haveString := "" for k, v := range have { haveString += fmt.Sprintf("%s: %s (from %s)\n", k, v.Value, v.From) } logger.Infof("Authentication Credentials:\n%s\n", haveString) } credsResult := &CredentialsResult{ AuthOpts: ao, Region: region, Have: have, Want: want, } return credsResult, nil }
// Credentials determines the appropriate authentication method for the user. // It returns a gophercloud.AuthOptions object and a region. // // It will use command-line authentication parameters if available, then it will // look for any unset parameters in the config file, and then finally in // environment variables. func Credentials(c *cli.Context, logger *logrus.Logger) (*gophercloud.AuthOptions, string, error) { have := make(map[string]authCred) need := map[string]string{ "username": "", "apikey": "", "authurl": "", "region": "", } // use command-line options if available cliopts(c, have, need) // are there any unset auth variables? if len(need) != 0 { // if so, look in config file err := configfile(c, have, need) if err != nil { return nil, "", err } // still unset auth variables? if len(need) != 0 { // if so, look in environment variables envvars(have, need) } } // if the user didn't provide an auth URL, default to the Rackspace US endpoint if _, ok := have["authurl"]; !ok || have["authurl"].value == "" { have["authurl"] = authCred{value: rackspace.RackspaceUSIdentity, from: "default value"} delete(need, "authurl") } haveString := "" for k, v := range have { haveString += fmt.Sprintf("%s: %s (from %s)\n", k, v.value, v.from) } if len(need) > 0 { needString := "" for k := range need { needString += fmt.Sprintf("%s\n", k) } authErrSlice := []string{"There are some required Rackspace Cloud credentials that we couldn't find.", "Here's what we have:", fmt.Sprintf("%s", haveString), "and here's what we we're missing:", fmt.Sprintf("%s", needString), "", "You can set any of these credentials in the following ways:", "- Run `rack configure` to interactively create a configuration file,", "- Specify it in the command as a flag (--username, --apikey, --region), or", "- Export it as an environment variable (RS_USERNAME, RS_API_KEY, RS_REGION_NAME).", "", } return nil, "", fmt.Errorf(strings.Join(authErrSlice, "\n")) } if logger != nil { logger.Infof("Authentication Credentials:\n%s\n", haveString) } ao := &gophercloud.AuthOptions{ Username: have["username"].value, APIKey: have["apikey"].value, IdentityEndpoint: have["authurl"].value, } // upper-case the region region := strings.ToUpper(have["region"].value) // allow Gophercloud to re-authenticate ao.AllowReauth = true return ao, region, nil }