terraform/vendor/github.com/rackspace/gophercloud/openstack/client.go

369 lines
11 KiB
Go

package openstack
import (
"fmt"
"net/url"
"strconv"
"strings"
"github.com/rackspace/gophercloud"
tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
"github.com/rackspace/gophercloud/openstack/utils"
)
const (
v20 = "v2.0"
v30 = "v3.0"
)
// NewClient prepares an unauthenticated ProviderClient instance.
// Most users will probably prefer using the AuthenticatedClient function instead.
// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
// for example.
func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
u, err := url.Parse(endpoint)
if err != nil {
return nil, err
}
u.RawQuery, u.Fragment = "", ""
// Base is url with path
endpoint = gophercloud.NormalizeURL(endpoint)
base := gophercloud.NormalizeURL(u.String())
path := u.Path
if !strings.HasSuffix(path, "/") {
path = path + "/"
}
parts := strings.Split(path[0:len(path)-1], "/")
for index, version := range parts {
if 2 <= len(version) && len(version) <= 4 && strings.HasPrefix(version, "v") {
_, err := strconv.ParseFloat(version[1:], 64)
if err == nil {
// post version suffixes in path are not supported
// version must be on the last index
if index < len(parts)-1 {
return nil, fmt.Errorf("Path suffixes (after version) are not supported.")
}
switch version {
case "v2.0", "v3":
// valid version found, strip from base
return &gophercloud.ProviderClient{
IdentityBase: base[0 : len(base)-len(version)-1],
IdentityEndpoint: endpoint,
}, nil
default:
return nil, fmt.Errorf("Invalid identity endpoint version %v. Supported versions: v2.0, v3", version)
}
}
}
}
return &gophercloud.ProviderClient{
IdentityBase: base,
IdentityEndpoint: "",
}, nil
}
// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
// returns a Client instance that's ready to operate.
// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
// the most recent identity service available to proceed.
func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
client, err := NewClient(options.IdentityEndpoint)
if err != nil {
return nil, err
}
err = Authenticate(client, options)
if err != nil {
return nil, err
}
return client, nil
}
// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
versions := []*utils.Version{
{ID: v20, Priority: 20, Suffix: "/v2.0/"},
{ID: v30, Priority: 30, Suffix: "/v3/"},
}
chosen, endpoint, err := utils.ChooseVersion(client, versions)
if err != nil {
return err
}
switch chosen.ID {
case v20:
return v2auth(client, endpoint, options)
case v30:
return v3auth(client, endpoint, options)
default:
// The switch statement must be out of date from the versions list.
return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
}
}
// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
return v2auth(client, "", options)
}
func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
v2Client := NewIdentityV2(client)
if endpoint != "" {
v2Client.Endpoint = endpoint
}
result := tokens2.Create(v2Client, tokens2.AuthOptions{AuthOptions: options})
token, err := result.ExtractToken()
if err != nil {
return err
}
catalog, err := result.ExtractServiceCatalog()
if err != nil {
return err
}
if options.AllowReauth {
client.ReauthFunc = func() error {
client.TokenID = ""
return v2auth(client, endpoint, options)
}
}
client.TokenID = token.ID
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
return V2EndpointURL(catalog, opts)
}
return nil
}
// AuthenticateV3 explicitly authenticates against the identity v3 service.
func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
return v3auth(client, "", options)
}
func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
// Override the generated service endpoint with the one returned by the version endpoint.
v3Client := NewIdentityV3(client)
if endpoint != "" {
v3Client.Endpoint = endpoint
}
// copy the auth options to a local variable that we can change. `options`
// needs to stay as-is for reauth purposes
v3Options := options
var scope *tokens3.Scope
if options.TenantID != "" {
scope = &tokens3.Scope{
ProjectID: options.TenantID,
}
v3Options.TenantID = ""
v3Options.TenantName = ""
} else {
if options.TenantName != "" {
scope = &tokens3.Scope{
ProjectName: options.TenantName,
DomainID: options.DomainID,
DomainName: options.DomainName,
}
v3Options.TenantName = ""
}
}
result := tokens3.Create(v3Client, tokens3.AuthOptions{AuthOptions: v3Options}, scope)
token, err := result.ExtractToken()
if err != nil {
return err
}
catalog, err := result.ExtractServiceCatalog()
if err != nil {
return err
}
client.TokenID = token.ID
if options.AllowReauth {
client.ReauthFunc = func() error {
client.TokenID = ""
return v3auth(client, endpoint, options)
}
}
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
return V3EndpointURL(catalog, opts)
}
return nil
}
// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
v2Endpoint := client.IdentityBase + "v2.0/"
return &gophercloud.ServiceClient{
ProviderClient: client,
Endpoint: v2Endpoint,
}
}
// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
v3Endpoint := client.IdentityBase + "v3/"
return &gophercloud.ServiceClient{
ProviderClient: client,
Endpoint: v3Endpoint,
}
}
func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("identity")
eo.Availability = gophercloud.AvailabilityAdmin
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
// Force using v2 API
if strings.Contains(url, "/v3") {
url = strings.Replace(url, "/v3", "/v2.0", -1)
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("identity")
eo.Availability = gophercloud.AvailabilityAdmin
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
// Force using v3 API
if strings.Contains(url, "/v2.0") {
url = strings.Replace(url, "/v2.0", "/v3", -1)
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("object-store")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("compute")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("network")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{
ProviderClient: client,
Endpoint: url,
ResourceBase: url + "v2.0/",
}, nil
}
// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("volume")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("volumev2")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
// CDN service.
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("cdn")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("orchestration")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("database")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 image service.
func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("image")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client,
Endpoint: url,
ResourceBase: url + "v2/"}, nil
}
// NewTelemetryV2 creates a ServiceClient that may be used to access the v2 telemetry service.
func NewTelemetryV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("metering")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}