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 }
// NewClient creates and returns a Rackspace client for the given service. func NewClient(c *cli.Context, serviceType string, logger *logrus.Logger, noCache bool, useServiceNet bool) (*gophercloud.ServiceClient, error) { // get the user's authentication credentials credsResult, err := Credentials(c, logger) if err != nil { return nil, err } logMsg := "Using public endpoint" urlType := gophercloud.AvailabilityPublic if useServiceNet { logMsg = "Using service net endpoint" urlType = gophercloud.AvailabilityInternal } logger.Infoln(logMsg) if noCache { return authFromScratch(credsResult, serviceType, urlType, logger) } ao := credsResult.AuthOpts region := credsResult.Region // form the cache key cacheKey := CacheKey(*ao, region, serviceType, urlType) // 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, urlType, 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 }