tests: upload & boot image in OpenStack. Closes #339
This commit is contained in:
parent
18b17c87fa
commit
f7c4dca5d5
96 changed files with 22287 additions and 1965 deletions
128
vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
generated
vendored
Normal file
128
vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
var nilOptions = gophercloud.AuthOptions{}
|
||||
|
||||
/*
|
||||
AuthOptionsFromEnv fills out an identity.AuthOptions structure with the
|
||||
settings found on the various OpenStack OS_* environment variables.
|
||||
|
||||
The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME,
|
||||
OS_PASSWORD and OS_PROJECT_ID.
|
||||
|
||||
Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings,
|
||||
or an error will result. OS_PROJECT_ID, is optional.
|
||||
|
||||
OS_TENANT_ID and OS_TENANT_NAME are deprecated forms of OS_PROJECT_ID and
|
||||
OS_PROJECT_NAME and the latter are expected against a v3 auth api.
|
||||
|
||||
If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred
|
||||
as "tenant" in Gophercloud.
|
||||
|
||||
If OS_PROJECT_NAME is set, it requires OS_PROJECT_ID to be set as well to
|
||||
handle projects not on the default domain.
|
||||
|
||||
To use this function, first set the OS_* environment variables (for example,
|
||||
by sourcing an `openrc` file), then:
|
||||
|
||||
opts, err := openstack.AuthOptionsFromEnv()
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
*/
|
||||
func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||
authURL := os.Getenv("OS_AUTH_URL")
|
||||
username := os.Getenv("OS_USERNAME")
|
||||
userID := os.Getenv("OS_USERID")
|
||||
password := os.Getenv("OS_PASSWORD")
|
||||
passcode := os.Getenv("OS_PASSCODE")
|
||||
tenantID := os.Getenv("OS_TENANT_ID")
|
||||
tenantName := os.Getenv("OS_TENANT_NAME")
|
||||
domainID := os.Getenv("OS_DOMAIN_ID")
|
||||
domainName := os.Getenv("OS_DOMAIN_NAME")
|
||||
applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID")
|
||||
applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME")
|
||||
applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET")
|
||||
|
||||
// If OS_PROJECT_ID is set, overwrite tenantID with the value.
|
||||
if v := os.Getenv("OS_PROJECT_ID"); v != "" {
|
||||
tenantID = v
|
||||
}
|
||||
|
||||
// If OS_PROJECT_NAME is set, overwrite tenantName with the value.
|
||||
if v := os.Getenv("OS_PROJECT_NAME"); v != "" {
|
||||
tenantName = v
|
||||
}
|
||||
|
||||
if authURL == "" {
|
||||
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||
EnvironmentVariable: "OS_AUTH_URL",
|
||||
}
|
||||
return nilOptions, err
|
||||
}
|
||||
|
||||
if userID == "" && username == "" {
|
||||
// Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set
|
||||
if applicationCredentialID == "" && applicationCredentialSecret == "" {
|
||||
err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"},
|
||||
}
|
||||
return nilOptions, err
|
||||
}
|
||||
}
|
||||
|
||||
if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
||||
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||
// silently ignore TOTP passcode warning, since it is not a common auth method
|
||||
EnvironmentVariable: "OS_PASSWORD",
|
||||
}
|
||||
return nilOptions, err
|
||||
}
|
||||
|
||||
if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" {
|
||||
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||
EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET",
|
||||
}
|
||||
return nilOptions, err
|
||||
}
|
||||
|
||||
if domainID == "" && domainName == "" && tenantID == "" && tenantName != "" {
|
||||
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||
EnvironmentVariable: "OS_PROJECT_ID",
|
||||
}
|
||||
return nilOptions, err
|
||||
}
|
||||
|
||||
if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" {
|
||||
if userID == "" && username == "" {
|
||||
return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"},
|
||||
}
|
||||
}
|
||||
if username != "" && domainID == "" && domainName == "" {
|
||||
return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ao := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: authURL,
|
||||
UserID: userID,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Passcode: passcode,
|
||||
TenantID: tenantID,
|
||||
TenantName: tenantName,
|
||||
DomainID: domainID,
|
||||
DomainName: domainName,
|
||||
ApplicationCredentialID: applicationCredentialID,
|
||||
ApplicationCredentialName: applicationCredentialName,
|
||||
ApplicationCredentialSecret: applicationCredentialSecret,
|
||||
}
|
||||
|
||||
return ao, nil
|
||||
}
|
||||
503
vendor/github.com/gophercloud/gophercloud/openstack/client.go
generated
vendored
Normal file
503
vendor/github.com/gophercloud/gophercloud/openstack/client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1"
|
||||
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// v2 represents Keystone v2.
|
||||
// It should never increase beyond 2.0.
|
||||
v2 = "v2.0"
|
||||
|
||||
// v3 represents Keystone v3.
|
||||
// The version can be anything from v3 to v3.x.
|
||||
v3 = "v3"
|
||||
)
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
A basic example of using this would be:
|
||||
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
provider, err := openstack.NewClient(ao.IdentityEndpoint)
|
||||
client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{})
|
||||
*/
|
||||
func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
|
||||
base, err := utils.BaseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoint = gophercloud.NormalizeURL(endpoint)
|
||||
base = gophercloud.NormalizeURL(base)
|
||||
|
||||
p := new(gophercloud.ProviderClient)
|
||||
p.IdentityBase = base
|
||||
p.IdentityEndpoint = endpoint
|
||||
p.UseTokenLock()
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
/*
|
||||
AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint
|
||||
specified by the options, acquires a token, and returns a Provider Client
|
||||
instance that's ready to operate.
|
||||
|
||||
If the full path to a versioned identity endpoint was specified (example:
|
||||
http://example.com:5000/v3), that path will be used as the endpoint to query.
|
||||
|
||||
If a versionless endpoint was specified (example: http://example.com:5000/),
|
||||
the endpoint will be queried to determine which versions of the identity service
|
||||
are available, then chooses the most recent or most supported version.
|
||||
|
||||
Example:
|
||||
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
provider, err := openstack.AuthenticatedClient(ao)
|
||||
client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
*/
|
||||
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: v2, Priority: 20, Suffix: "/v2.0/"},
|
||||
{ID: v3, Priority: 30, Suffix: "/v3/"},
|
||||
}
|
||||
|
||||
chosen, endpoint, err := utils.ChooseVersion(client, versions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch chosen.ID {
|
||||
case v2:
|
||||
return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
|
||||
case v3:
|
||||
return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{})
|
||||
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, eo gophercloud.EndpointOpts) error {
|
||||
return v2auth(client, "", options, eo)
|
||||
}
|
||||
|
||||
func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
|
||||
v2Client, err := NewIdentityV2(client, eo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if endpoint != "" {
|
||||
v2Client.Endpoint = endpoint
|
||||
}
|
||||
|
||||
v2Opts := tokens2.AuthOptions{
|
||||
IdentityEndpoint: options.IdentityEndpoint,
|
||||
Username: options.Username,
|
||||
Password: options.Password,
|
||||
TenantID: options.TenantID,
|
||||
TenantName: options.TenantName,
|
||||
AllowReauth: options.AllowReauth,
|
||||
TokenID: options.TokenID,
|
||||
}
|
||||
|
||||
result := tokens2.Create(v2Client, v2Opts)
|
||||
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
catalog, err := result.ExtractServiceCatalog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.AllowReauth {
|
||||
// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
|
||||
// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
|
||||
// this should retry authentication only once
|
||||
tac := *client
|
||||
tac.SetThrowaway(true)
|
||||
tac.ReauthFunc = nil
|
||||
tac.SetTokenAndAuthResult(nil)
|
||||
tao := options
|
||||
tao.AllowReauth = false
|
||||
client.ReauthFunc = func() error {
|
||||
err := v2auth(&tac, endpoint, tao, eo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.CopyTokenFrom(&tac)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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 tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
|
||||
return v3auth(client, "", options, eo)
|
||||
}
|
||||
|
||||
func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
|
||||
// Override the generated service endpoint with the one returned by the version endpoint.
|
||||
v3Client, err := NewIdentityV3(client, eo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if endpoint != "" {
|
||||
v3Client.Endpoint = endpoint
|
||||
}
|
||||
|
||||
var catalog *tokens3.ServiceCatalog
|
||||
|
||||
var tokenID string
|
||||
// passthroughToken allows to passthrough the token without a scope
|
||||
var passthroughToken bool
|
||||
switch v := opts.(type) {
|
||||
case *gophercloud.AuthOptions:
|
||||
tokenID = v.TokenID
|
||||
passthroughToken = (v.Scope == nil || *v.Scope == gophercloud.AuthScope{})
|
||||
case *tokens3.AuthOptions:
|
||||
tokenID = v.TokenID
|
||||
passthroughToken = (v.Scope == tokens3.Scope{})
|
||||
}
|
||||
|
||||
if tokenID != "" && passthroughToken {
|
||||
// passing through the token ID without requesting a new scope
|
||||
if opts.CanReauth() {
|
||||
return fmt.Errorf("cannot use AllowReauth, when the token ID is defined and auth scope is not set")
|
||||
}
|
||||
|
||||
v3Client.SetToken(tokenID)
|
||||
result := tokens3.Get(v3Client, tokenID)
|
||||
if result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
catalog, err = result.ExtractServiceCatalog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var result tokens3.CreateResult
|
||||
switch opts.(type) {
|
||||
case *ec2tokens.AuthOptions:
|
||||
result = ec2tokens.Create(v3Client, opts)
|
||||
case *oauth1.AuthOptions:
|
||||
result = oauth1.Create(v3Client, opts)
|
||||
default:
|
||||
result = tokens3.Create(v3Client, opts)
|
||||
}
|
||||
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
catalog, err = result.ExtractServiceCatalog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.CanReauth() {
|
||||
// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
|
||||
// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
|
||||
// this should retry authentication only once
|
||||
tac := *client
|
||||
tac.SetThrowaway(true)
|
||||
tac.ReauthFunc = nil
|
||||
tac.SetTokenAndAuthResult(nil)
|
||||
var tao tokens3.AuthOptionsBuilder
|
||||
switch ot := opts.(type) {
|
||||
case *gophercloud.AuthOptions:
|
||||
o := *ot
|
||||
o.AllowReauth = false
|
||||
tao = &o
|
||||
case *tokens3.AuthOptions:
|
||||
o := *ot
|
||||
o.AllowReauth = false
|
||||
tao = &o
|
||||
case *ec2tokens.AuthOptions:
|
||||
o := *ot
|
||||
o.AllowReauth = false
|
||||
tao = &o
|
||||
case *oauth1.AuthOptions:
|
||||
o := *ot
|
||||
o.AllowReauth = false
|
||||
tao = &o
|
||||
default:
|
||||
tao = opts
|
||||
}
|
||||
client.ReauthFunc = func() error {
|
||||
err := v3auth(&tac, endpoint, tao, eo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.CopyTokenFrom(&tac)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
endpoint := client.IdentityBase + "v2.0/"
|
||||
clientType := "identity"
|
||||
var err error
|
||||
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||
eo.ApplyDefaults(clientType)
|
||||
endpoint, err = client.EndpointLocator(eo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &gophercloud.ServiceClient{
|
||||
ProviderClient: client,
|
||||
Endpoint: endpoint,
|
||||
Type: clientType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewIdentityV3 creates a ServiceClient that may be used to access the v3
|
||||
// identity service.
|
||||
func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
endpoint := client.IdentityBase + "v3/"
|
||||
clientType := "identity"
|
||||
var err error
|
||||
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||
eo.ApplyDefaults(clientType)
|
||||
endpoint, err = client.EndpointLocator(eo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure endpoint still has a suffix of v3.
|
||||
// This is because EndpointLocator might have found a versionless
|
||||
// endpoint or the published endpoint is still /v2.0. In both
|
||||
// cases, we need to fix the endpoint to point to /v3.
|
||||
base, err := utils.BaseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base = gophercloud.NormalizeURL(base)
|
||||
|
||||
endpoint = base + "v3/"
|
||||
|
||||
return &gophercloud.ServiceClient{
|
||||
ProviderClient: client,
|
||||
Endpoint: endpoint,
|
||||
Type: clientType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) {
|
||||
sc := new(gophercloud.ServiceClient)
|
||||
eo.ApplyDefaults(clientType)
|
||||
url, err := client.EndpointLocator(eo)
|
||||
if err != nil {
|
||||
return sc, err
|
||||
}
|
||||
sc.ProviderClient = client
|
||||
sc.Endpoint = url
|
||||
sc.Type = clientType
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
// NewBareMetalV1 creates a ServiceClient that may be used with the v1
|
||||
// bare metal package.
|
||||
func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "baremetal")
|
||||
}
|
||||
|
||||
// NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1
|
||||
// bare metal introspection package.
|
||||
func NewBareMetalIntrospectionV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "baremetal-inspector")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return initClientOpts(client, eo, "object-store")
|
||||
}
|
||||
|
||||
// NewComputeV2 creates a ServiceClient that may be used with the v2 compute
|
||||
// package.
|
||||
func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "compute")
|
||||
}
|
||||
|
||||
// NewNetworkV2 creates a ServiceClient that may be used with the v2 network
|
||||
// package.
|
||||
func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
sc, err := initClientOpts(client, eo, "network")
|
||||
sc.ResourceBase = sc.Endpoint + "v2.0/"
|
||||
return sc, err
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return initClientOpts(client, eo, "volume")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return initClientOpts(client, eo, "volumev2")
|
||||
}
|
||||
|
||||
// NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service.
|
||||
func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "volumev3")
|
||||
}
|
||||
|
||||
// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
|
||||
func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "sharev2")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return initClientOpts(client, eo, "cdn")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return initClientOpts(client, eo, "orchestration")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return initClientOpts(client, eo, "database")
|
||||
}
|
||||
|
||||
// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS
|
||||
// service.
|
||||
func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
sc, err := initClientOpts(client, eo, "dns")
|
||||
sc.ResourceBase = sc.Endpoint + "v2/"
|
||||
return sc, err
|
||||
}
|
||||
|
||||
// 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) {
|
||||
sc, err := initClientOpts(client, eo, "image")
|
||||
sc.ResourceBase = sc.Endpoint + "v2/"
|
||||
return sc, err
|
||||
}
|
||||
|
||||
// NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2
|
||||
// load balancer service.
|
||||
func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
sc, err := initClientOpts(client, eo, "load-balancer")
|
||||
|
||||
// Fixes edge case having an OpenStack lb endpoint with trailing version number.
|
||||
endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1)
|
||||
|
||||
sc.ResourceBase = endpoint + "v2.0/"
|
||||
return sc, err
|
||||
}
|
||||
|
||||
// NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering
|
||||
// package.
|
||||
func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "clustering")
|
||||
}
|
||||
|
||||
// NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging
|
||||
// service.
|
||||
func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
sc, err := initClientOpts(client, eo, "messaging")
|
||||
sc.MoreHeaders = map[string]string{"Client-ID": clientID}
|
||||
return sc, err
|
||||
}
|
||||
|
||||
// NewContainerV1 creates a ServiceClient that may be used with v1 container package
|
||||
func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "container")
|
||||
}
|
||||
|
||||
// NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key
|
||||
// manager service.
|
||||
func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
sc, err := initClientOpts(client, eo, "key-manager")
|
||||
sc.ResourceBase = sc.Endpoint + "v1/"
|
||||
return sc, err
|
||||
}
|
||||
|
||||
// NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management
|
||||
// package.
|
||||
func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "container-infra")
|
||||
}
|
||||
|
||||
// NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package.
|
||||
func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "workflowv2")
|
||||
}
|
||||
|
||||
// NewPlacementV1 creates a ServiceClient that may be used with the placement package.
|
||||
func NewPlacementV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||
return initClientOpts(client, eo, "placement")
|
||||
}
|
||||
115
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go
generated
vendored
Normal file
115
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Package servers provides information and interaction with the server API
|
||||
resource in the OpenStack Compute service.
|
||||
|
||||
A server is a virtual machine instance in the compute system. In order for
|
||||
one to be provisioned, a valid flavor and image are required.
|
||||
|
||||
Example to List Servers
|
||||
|
||||
listOpts := servers.ListOpts{
|
||||
AllTenants: true,
|
||||
}
|
||||
|
||||
allPages, err := servers.List(computeClient, listOpts).AllPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
allServers, err := servers.ExtractServers(allPages)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, server := range allServers {
|
||||
fmt.Printf("%+v\n", server)
|
||||
}
|
||||
|
||||
Example to Create a Server
|
||||
|
||||
createOpts := servers.CreateOpts{
|
||||
Name: "server_name",
|
||||
ImageRef: "image-uuid",
|
||||
FlavorRef: "flavor-uuid",
|
||||
}
|
||||
|
||||
server, err := servers.Create(computeClient, createOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Delete a Server
|
||||
|
||||
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||
err := servers.Delete(computeClient, serverID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Force Delete a Server
|
||||
|
||||
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||
err := servers.ForceDelete(computeClient, serverID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Reboot a Server
|
||||
|
||||
rebootOpts := servers.RebootOpts{
|
||||
Type: servers.SoftReboot,
|
||||
}
|
||||
|
||||
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||
|
||||
err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Rebuild a Server
|
||||
|
||||
rebuildOpts := servers.RebuildOpts{
|
||||
Name: "new_name",
|
||||
ImageID: "image-uuid",
|
||||
}
|
||||
|
||||
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||
|
||||
server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Resize a Server
|
||||
|
||||
resizeOpts := servers.ResizeOpts{
|
||||
FlavorRef: "flavor-uuid",
|
||||
}
|
||||
|
||||
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||
|
||||
err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = servers.ConfirmResize(computeClient, serverID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Snapshot a Server
|
||||
|
||||
snapshotOpts := servers.CreateImageOpts{
|
||||
Name: "snapshot_name",
|
||||
}
|
||||
|
||||
serverID := "d9072956-1560-487c-97f2-18bdf65ec749"
|
||||
|
||||
image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package servers
|
||||
71
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go
generated
vendored
Normal file
71
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package servers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
// ErrNeitherImageIDNorImageNameProvided is the error when neither the image
|
||||
// ID nor the image name is provided for a server operation
|
||||
type ErrNeitherImageIDNorImageNameProvided struct{ gophercloud.ErrMissingInput }
|
||||
|
||||
func (e ErrNeitherImageIDNorImageNameProvided) Error() string {
|
||||
return "One and only one of the image ID and the image name must be provided."
|
||||
}
|
||||
|
||||
// ErrNeitherFlavorIDNorFlavorNameProvided is the error when neither the flavor
|
||||
// ID nor the flavor name is provided for a server operation
|
||||
type ErrNeitherFlavorIDNorFlavorNameProvided struct{ gophercloud.ErrMissingInput }
|
||||
|
||||
func (e ErrNeitherFlavorIDNorFlavorNameProvided) Error() string {
|
||||
return "One and only one of the flavor ID and the flavor name must be provided."
|
||||
}
|
||||
|
||||
type ErrNoClientProvidedForIDByName struct{ gophercloud.ErrMissingInput }
|
||||
|
||||
func (e ErrNoClientProvidedForIDByName) Error() string {
|
||||
return "A service client must be provided to find a resource ID by name."
|
||||
}
|
||||
|
||||
// ErrInvalidHowParameterProvided is the error when an unknown value is given
|
||||
// for the `how` argument
|
||||
type ErrInvalidHowParameterProvided struct{ gophercloud.ErrInvalidInput }
|
||||
|
||||
// ErrNoAdminPassProvided is the error when an administrative password isn't
|
||||
// provided for a server operation
|
||||
type ErrNoAdminPassProvided struct{ gophercloud.ErrMissingInput }
|
||||
|
||||
// ErrNoImageIDProvided is the error when an image ID isn't provided for a server
|
||||
// operation
|
||||
type ErrNoImageIDProvided struct{ gophercloud.ErrMissingInput }
|
||||
|
||||
// ErrNoIDProvided is the error when a server ID isn't provided for a server
|
||||
// operation
|
||||
type ErrNoIDProvided struct{ gophercloud.ErrMissingInput }
|
||||
|
||||
// ErrServer is a generic error type for servers HTTP operations.
|
||||
type ErrServer struct {
|
||||
gophercloud.ErrUnexpectedResponseCode
|
||||
ID string
|
||||
}
|
||||
|
||||
func (se ErrServer) Error() string {
|
||||
return fmt.Sprintf("Error while executing HTTP request for server [%s]", se.ID)
|
||||
}
|
||||
|
||||
// Error404 overrides the generic 404 error message.
|
||||
func (se ErrServer) Error404(e gophercloud.ErrUnexpectedResponseCode) error {
|
||||
se.ErrUnexpectedResponseCode = e
|
||||
return &ErrServerNotFound{se}
|
||||
}
|
||||
|
||||
// ErrServerNotFound is the error when a 404 is received during server HTTP
|
||||
// operations.
|
||||
type ErrServerNotFound struct {
|
||||
ErrServer
|
||||
}
|
||||
|
||||
func (e ErrServerNotFound) Error() string {
|
||||
return fmt.Sprintf("I couldn't find server [%s]", e.ID)
|
||||
}
|
||||
752
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
Normal file
752
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,752 @@
|
|||
package servers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToServerListQuery() (string, error)
|
||||
}
|
||||
|
||||
// ListOpts allows the filtering and sorting of paginated collections through
|
||||
// the API. Filtering is achieved by passing in struct field values that map to
|
||||
// the server attributes you want to see returned. Marker and Limit are used
|
||||
// for pagination.
|
||||
type ListOpts struct {
|
||||
// ChangesSince is a time/date stamp for when the server last changed status.
|
||||
ChangesSince string `q:"changes-since"`
|
||||
|
||||
// Image is the name of the image in URL format.
|
||||
Image string `q:"image"`
|
||||
|
||||
// Flavor is the name of the flavor in URL format.
|
||||
Flavor string `q:"flavor"`
|
||||
|
||||
// Name of the server as a string; can be queried with regular expressions.
|
||||
// Realize that ?name=bob returns both bob and bobb. If you need to match bob
|
||||
// only, you can use a regular expression matching the syntax of the
|
||||
// underlying database server implemented for Compute.
|
||||
Name string `q:"name"`
|
||||
|
||||
// Status is the value of the status of the server so that you can filter on
|
||||
// "ACTIVE" for example.
|
||||
Status string `q:"status"`
|
||||
|
||||
// Host is the name of the host as a string.
|
||||
Host string `q:"host"`
|
||||
|
||||
// Marker is a UUID of the server at which you want to set a marker.
|
||||
Marker string `q:"marker"`
|
||||
|
||||
// Limit is an integer value for the limit of values to return.
|
||||
Limit int `q:"limit"`
|
||||
|
||||
// AllTenants is a bool to show all tenants.
|
||||
AllTenants bool `q:"all_tenants"`
|
||||
|
||||
// TenantID lists servers for a particular tenant.
|
||||
// Setting "AllTenants = true" is required.
|
||||
TenantID string `q:"tenant_id"`
|
||||
|
||||
// This requires the client to be set to microversion 2.26 or later.
|
||||
// Tags filters on specific server tags. All tags must be present for the server.
|
||||
Tags string `q:"tags"`
|
||||
|
||||
// This requires the client to be set to microversion 2.26 or later.
|
||||
// TagsAny filters on specific server tags. At least one of the tags must be present for the server.
|
||||
TagsAny string `q:"tags-any"`
|
||||
|
||||
// This requires the client to be set to microversion 2.26 or later.
|
||||
// NotTags filters on specific server tags. All tags must be absent for the server.
|
||||
NotTags string `q:"not-tags"`
|
||||
|
||||
// This requires the client to be set to microversion 2.26 or later.
|
||||
// NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server.
|
||||
NotTagsAny string `q:"not-tags-any"`
|
||||
}
|
||||
|
||||
// ToServerListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToServerListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
return q.String(), err
|
||||
}
|
||||
|
||||
// List makes a request against the API to list servers accessible to you.
|
||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||
url := listDetailURL(client)
|
||||
if opts != nil {
|
||||
query, err := opts.ToServerListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||
return ServerPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
||||
// Create request.
|
||||
type CreateOptsBuilder interface {
|
||||
ToServerCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// Network is used within CreateOpts to control a new server's network
|
||||
// attachments.
|
||||
type Network struct {
|
||||
// UUID of a network to attach to the newly provisioned server.
|
||||
// Required unless Port is provided.
|
||||
UUID string
|
||||
|
||||
// Port of a neutron network to attach to the newly provisioned server.
|
||||
// Required unless UUID is provided.
|
||||
Port string
|
||||
|
||||
// FixedIP specifies a fixed IPv4 address to be used on this network.
|
||||
FixedIP string
|
||||
}
|
||||
|
||||
// Personality is an array of files that are injected into the server at launch.
|
||||
type Personality []*File
|
||||
|
||||
// File is used within CreateOpts and RebuildOpts to inject a file into the
|
||||
// server at launch.
|
||||
// File implements the json.Marshaler interface, so when a Create or Rebuild
|
||||
// operation is requested, json.Marshal will call File's MarshalJSON method.
|
||||
type File struct {
|
||||
// Path of the file.
|
||||
Path string
|
||||
|
||||
// Contents of the file. Maximum content size is 255 bytes.
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the escaped file, base64 encoding the contents.
|
||||
func (f *File) MarshalJSON() ([]byte, error) {
|
||||
file := struct {
|
||||
Path string `json:"path"`
|
||||
Contents string `json:"contents"`
|
||||
}{
|
||||
Path: f.Path,
|
||||
Contents: base64.StdEncoding.EncodeToString(f.Contents),
|
||||
}
|
||||
return json.Marshal(file)
|
||||
}
|
||||
|
||||
// CreateOpts specifies server creation parameters.
|
||||
type CreateOpts struct {
|
||||
// Name is the name to assign to the newly launched server.
|
||||
Name string `json:"name" required:"true"`
|
||||
|
||||
// ImageRef is the ID or full URL to the image that contains the
|
||||
// server's OS and initial state.
|
||||
// Also optional if using the boot-from-volume extension.
|
||||
ImageRef string `json:"imageRef"`
|
||||
|
||||
// FlavorRef is the ID or full URL to the flavor that describes the server's specs.
|
||||
FlavorRef string `json:"flavorRef"`
|
||||
|
||||
// SecurityGroups lists the names of the security groups to which this server
|
||||
// should belong.
|
||||
SecurityGroups []string `json:"-"`
|
||||
|
||||
// UserData contains configuration information or scripts to use upon launch.
|
||||
// Create will base64-encode it for you, if it isn't already.
|
||||
UserData []byte `json:"-"`
|
||||
|
||||
// AvailabilityZone in which to launch the server.
|
||||
AvailabilityZone string `json:"availability_zone,omitempty"`
|
||||
|
||||
// Networks dictates how this server will be attached to available networks.
|
||||
// By default, the server will be attached to all isolated networks for the
|
||||
// tenant.
|
||||
// Starting with microversion 2.37 networks can also be an "auto" or "none"
|
||||
// string.
|
||||
Networks interface{} `json:"-"`
|
||||
|
||||
// Metadata contains key-value pairs (up to 255 bytes each) to attach to the
|
||||
// server.
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
|
||||
// Personality includes files to inject into the server at launch.
|
||||
// Create will base64-encode file contents for you.
|
||||
Personality Personality `json:"personality,omitempty"`
|
||||
|
||||
// ConfigDrive enables metadata injection through a configuration drive.
|
||||
ConfigDrive *bool `json:"config_drive,omitempty"`
|
||||
|
||||
// AdminPass sets the root user password. If not set, a randomly-generated
|
||||
// password will be created and returned in the response.
|
||||
AdminPass string `json:"adminPass,omitempty"`
|
||||
|
||||
// AccessIPv4 specifies an IPv4 address for the instance.
|
||||
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||
|
||||
// AccessIPv6 specifies an IPv6 address for the instance.
|
||||
AccessIPv6 string `json:"accessIPv6,omitempty"`
|
||||
|
||||
// Min specifies Minimum number of servers to launch.
|
||||
Min int `json:"min_count,omitempty"`
|
||||
|
||||
// Max specifies Maximum number of servers to launch.
|
||||
Max int `json:"max_count,omitempty"`
|
||||
|
||||
// ServiceClient will allow calls to be made to retrieve an image or
|
||||
// flavor ID by name.
|
||||
ServiceClient *gophercloud.ServiceClient `json:"-"`
|
||||
|
||||
// Tags allows a server to be tagged with single-word metadata.
|
||||
// Requires microversion 2.52 or later.
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerCreateMap assembles a request body based on the contents of a
|
||||
// CreateOpts.
|
||||
func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
||||
opts.ServiceClient = nil
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.UserData != nil {
|
||||
var userData string
|
||||
if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil {
|
||||
userData = base64.StdEncoding.EncodeToString(opts.UserData)
|
||||
} else {
|
||||
userData = string(opts.UserData)
|
||||
}
|
||||
b["user_data"] = &userData
|
||||
}
|
||||
|
||||
if len(opts.SecurityGroups) > 0 {
|
||||
securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups))
|
||||
for i, groupName := range opts.SecurityGroups {
|
||||
securityGroups[i] = map[string]interface{}{"name": groupName}
|
||||
}
|
||||
b["security_groups"] = securityGroups
|
||||
}
|
||||
|
||||
switch v := opts.Networks.(type) {
|
||||
case []Network:
|
||||
if len(v) > 0 {
|
||||
networks := make([]map[string]interface{}, len(v))
|
||||
for i, net := range v {
|
||||
networks[i] = make(map[string]interface{})
|
||||
if net.UUID != "" {
|
||||
networks[i]["uuid"] = net.UUID
|
||||
}
|
||||
if net.Port != "" {
|
||||
networks[i]["port"] = net.Port
|
||||
}
|
||||
if net.FixedIP != "" {
|
||||
networks[i]["fixed_ip"] = net.FixedIP
|
||||
}
|
||||
}
|
||||
b["networks"] = networks
|
||||
}
|
||||
case string:
|
||||
if v == "auto" || v == "none" {
|
||||
b["networks"] = v
|
||||
} else {
|
||||
return nil, fmt.Errorf(`networks must be a slice of Network struct or a string with "auto" or "none" values, current value is %q`, v)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Min != 0 {
|
||||
b["min_count"] = opts.Min
|
||||
}
|
||||
|
||||
if opts.Max != 0 {
|
||||
b["max_count"] = opts.Max
|
||||
}
|
||||
|
||||
return map[string]interface{}{"server": b}, nil
|
||||
}
|
||||
|
||||
// Create requests a server to be provisioned to the user in the current tenant.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||
reqBody, err := opts.ToServerCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(listURL(client), reqBody, &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete requests that a server previously provisioned be removed from your
|
||||
// account.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||
resp, err := client.Delete(deleteURL(client, id), nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ForceDelete forces the deletion of a server.
|
||||
func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) {
|
||||
resp, err := client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get requests details on a single server, by ID.
|
||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 203},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional attributes to the
|
||||
// Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToServerUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts specifies the base attributes that may be updated on an existing
|
||||
// server.
|
||||
type UpdateOpts struct {
|
||||
// Name changes the displayed name of the server.
|
||||
// The server host name will *not* change.
|
||||
// Server names are not constrained to be unique, even within the same tenant.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// AccessIPv4 provides a new IPv4 address for the instance.
|
||||
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||
|
||||
// AccessIPv6 provides a new IPv6 address for the instance.
|
||||
AccessIPv6 string `json:"accessIPv6,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerUpdateMap formats an UpdateOpts structure into a request body.
|
||||
func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "server")
|
||||
}
|
||||
|
||||
// Update requests that various attributes of the indicated server be changed.
|
||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToServerUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ChangeAdminPassword alters the administrator or root password for a specified
|
||||
// server.
|
||||
func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) {
|
||||
b := map[string]interface{}{
|
||||
"changePassword": map[string]string{
|
||||
"adminPass": newPassword,
|
||||
},
|
||||
}
|
||||
resp, err := client.Post(actionURL(client, id), b, nil, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// RebootMethod describes the mechanisms by which a server reboot can be requested.
|
||||
type RebootMethod string
|
||||
|
||||
// These constants determine how a server should be rebooted.
|
||||
// See the Reboot() function for further details.
|
||||
const (
|
||||
SoftReboot RebootMethod = "SOFT"
|
||||
HardReboot RebootMethod = "HARD"
|
||||
OSReboot = SoftReboot
|
||||
PowerCycle = HardReboot
|
||||
)
|
||||
|
||||
// RebootOptsBuilder allows extensions to add additional parameters to the
|
||||
// reboot request.
|
||||
type RebootOptsBuilder interface {
|
||||
ToServerRebootMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// RebootOpts provides options to the reboot request.
|
||||
type RebootOpts struct {
|
||||
// Type is the type of reboot to perform on the server.
|
||||
Type RebootMethod `json:"type" required:"true"`
|
||||
}
|
||||
|
||||
// ToServerRebootMap builds a body for the reboot request.
|
||||
func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "reboot")
|
||||
}
|
||||
|
||||
/*
|
||||
Reboot requests that a given server reboot.
|
||||
|
||||
Two methods exist for rebooting a server:
|
||||
|
||||
HardReboot (aka PowerCycle) starts the server instance by physically cutting
|
||||
power to the machine, or if a VM, terminating it at the hypervisor level.
|
||||
It's done. Caput. Full stop.
|
||||
Then, after a brief while, power is restored or the VM instance restarted.
|
||||
|
||||
SoftReboot (aka OSReboot) simply tells the OS to restart under its own
|
||||
procedure.
|
||||
E.g., in Linux, asking it to enter runlevel 6, or executing
|
||||
"sudo shutdown -r now", or by asking Windows to rtart the machine.
|
||||
*/
|
||||
func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) {
|
||||
b, err := opts.ToServerRebootMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(actionURL(client, id), b, nil, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// RebuildOptsBuilder allows extensions to provide additional parameters to the
|
||||
// rebuild request.
|
||||
type RebuildOptsBuilder interface {
|
||||
ToServerRebuildMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// RebuildOpts represents the configuration options used in a server rebuild
|
||||
// operation.
|
||||
type RebuildOpts struct {
|
||||
// AdminPass is the server's admin password
|
||||
AdminPass string `json:"adminPass,omitempty"`
|
||||
|
||||
// ImageRef is the ID of the image you want your server to be provisioned on.
|
||||
ImageRef string `json:"imageRef"`
|
||||
|
||||
// Name to set the server to
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// AccessIPv4 [optional] provides a new IPv4 address for the instance.
|
||||
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||
|
||||
// AccessIPv6 [optional] provides a new IPv6 address for the instance.
|
||||
AccessIPv6 string `json:"accessIPv6,omitempty"`
|
||||
|
||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each)
|
||||
// to attach to the server.
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
|
||||
// Personality [optional] includes files to inject into the server at launch.
|
||||
// Rebuild will base64-encode file contents for you.
|
||||
Personality Personality `json:"personality,omitempty"`
|
||||
|
||||
// ServiceClient will allow calls to be made to retrieve an image or
|
||||
// flavor ID by name.
|
||||
ServiceClient *gophercloud.ServiceClient `json:"-"`
|
||||
}
|
||||
|
||||
// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
|
||||
func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{"rebuild": b}, nil
|
||||
}
|
||||
|
||||
// Rebuild will reprovision the server according to the configuration options
|
||||
// provided in the RebuildOpts struct.
|
||||
func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) {
|
||||
b, err := opts.ToServerRebuildMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(actionURL(client, id), b, &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ResizeOptsBuilder allows extensions to add additional parameters to the
|
||||
// resize request.
|
||||
type ResizeOptsBuilder interface {
|
||||
ToServerResizeMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ResizeOpts represents the configuration options used to control a Resize
|
||||
// operation.
|
||||
type ResizeOpts struct {
|
||||
// FlavorRef is the ID of the flavor you wish your server to become.
|
||||
FlavorRef string `json:"flavorRef" required:"true"`
|
||||
}
|
||||
|
||||
// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON
|
||||
// request body for the Resize request.
|
||||
func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "resize")
|
||||
}
|
||||
|
||||
// Resize instructs the provider to change the flavor of the server.
|
||||
//
|
||||
// Note that this implies rebuilding it.
|
||||
//
|
||||
// Unfortunately, one cannot pass rebuild parameters to the resize function.
|
||||
// When the resize completes, the server will be in VERIFY_RESIZE state.
|
||||
// While in this state, you can explore the use of the new server's
|
||||
// configuration. If you like it, call ConfirmResize() to commit the resize
|
||||
// permanently. Otherwise, call RevertResize() to restore the old configuration.
|
||||
func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) {
|
||||
b, err := opts.ToServerResizeMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(actionURL(client, id), b, nil, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ConfirmResize confirms a previous resize operation on a server.
|
||||
// See Resize() for more details.
|
||||
func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) {
|
||||
resp, err := client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{201, 202, 204},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// RevertResize cancels a previous resize operation on a server.
|
||||
// See Resize() for more details.
|
||||
func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) {
|
||||
resp, err := client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ResetMetadataOptsBuilder allows extensions to add additional parameters to
|
||||
// the Reset request.
|
||||
type ResetMetadataOptsBuilder interface {
|
||||
ToMetadataResetMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// MetadataOpts is a map that contains key-value pairs.
|
||||
type MetadataOpts map[string]string
|
||||
|
||||
// ToMetadataResetMap assembles a body for a Reset request based on the contents
|
||||
// of a MetadataOpts.
|
||||
func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{"metadata": opts}, nil
|
||||
}
|
||||
|
||||
// ToMetadataUpdateMap assembles a body for an Update request based on the
|
||||
// contents of a MetadataOpts.
|
||||
func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) {
|
||||
return map[string]interface{}{"metadata": opts}, nil
|
||||
}
|
||||
|
||||
// ResetMetadata will create multiple new key-value pairs for the given server
|
||||
// ID.
|
||||
// Note: Using this operation will erase any already-existing metadata and
|
||||
// create the new metadata provided. To keep any already-existing metadata,
|
||||
// use the UpdateMetadatas or UpdateMetadata function.
|
||||
func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) {
|
||||
b, err := opts.ToMetadataResetMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Metadata requests all the metadata for the given server ID.
|
||||
func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) {
|
||||
resp, err := client.Get(metadataURL(client, id), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateMetadataOptsBuilder allows extensions to add additional parameters to
|
||||
// the Create request.
|
||||
type UpdateMetadataOptsBuilder interface {
|
||||
ToMetadataUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateMetadata updates (or creates) all the metadata specified by opts for
|
||||
// the given server ID. This operation does not affect already-existing metadata
|
||||
// that is not specified by opts.
|
||||
func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
|
||||
b, err := opts.ToMetadataUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// MetadatumOptsBuilder allows extensions to add additional parameters to the
|
||||
// Create request.
|
||||
type MetadatumOptsBuilder interface {
|
||||
ToMetadatumCreateMap() (map[string]interface{}, string, error)
|
||||
}
|
||||
|
||||
// MetadatumOpts is a map of length one that contains a key-value pair.
|
||||
type MetadatumOpts map[string]string
|
||||
|
||||
// ToMetadatumCreateMap assembles a body for a Create request based on the
|
||||
// contents of a MetadataumOpts.
|
||||
func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) {
|
||||
if len(opts) != 1 {
|
||||
err := gophercloud.ErrInvalidInput{}
|
||||
err.Argument = "servers.MetadatumOpts"
|
||||
err.Info = "Must have 1 and only 1 key-value pair"
|
||||
return nil, "", err
|
||||
}
|
||||
metadatum := map[string]interface{}{"meta": opts}
|
||||
var key string
|
||||
for k := range metadatum["meta"].(MetadatumOpts) {
|
||||
key = k
|
||||
}
|
||||
return metadatum, key, nil
|
||||
}
|
||||
|
||||
// CreateMetadatum will create or update the key-value pair with the given key
|
||||
// for the given server ID.
|
||||
func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) {
|
||||
b, key, err := opts.ToMetadatumCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Metadatum requests the key-value pair with the given key for the given
|
||||
// server ID.
|
||||
func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) {
|
||||
resp, err := client.Get(metadatumURL(client, id, key), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteMetadatum will delete the key-value pair with the given key for the
|
||||
// given server ID.
|
||||
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) {
|
||||
resp, err := client.Delete(metadatumURL(client, id, key), nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ListAddresses makes a request against the API to list the servers IP
|
||||
// addresses.
|
||||
func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager {
|
||||
return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page {
|
||||
return AddressPage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
|
||||
// ListAddressesByNetwork makes a request against the API to list the servers IP
|
||||
// addresses for the given network.
|
||||
func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager {
|
||||
return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page {
|
||||
return NetworkAddressPage{pagination.SinglePageBase(r)}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateImageOptsBuilder allows extensions to add additional parameters to the
|
||||
// CreateImage request.
|
||||
type CreateImageOptsBuilder interface {
|
||||
ToServerCreateImageMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateImageOpts provides options to pass to the CreateImage request.
|
||||
type CreateImageOpts struct {
|
||||
// Name of the image/snapshot.
|
||||
Name string `json:"name" required:"true"`
|
||||
|
||||
// Metadata contains key-value pairs (up to 255 bytes each) to attach to
|
||||
// the created image.
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerCreateImageMap formats a CreateImageOpts structure into a request
|
||||
// body.
|
||||
func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "createImage")
|
||||
}
|
||||
|
||||
// CreateImage makes a request against the nova API to schedule an image to be
|
||||
// created of the server
|
||||
func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) {
|
||||
b, err := opts.ToServerCreateImageMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{202},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// GetPassword makes a request against the nova API to get the encrypted
|
||||
// administrative password.
|
||||
func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) {
|
||||
resp, err := client.Get(passwordURL(client, serverId), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be
|
||||
// used as ShowConsoleOutput options
|
||||
type ShowConsoleOutputOptsBuilder interface {
|
||||
ToServerShowConsoleOutputMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder
|
||||
type ShowConsoleOutputOpts struct {
|
||||
// The number of lines to fetch from the end of console log.
|
||||
// All lines will be returned if this is not specified.
|
||||
Length int `json:"length,omitempty"`
|
||||
}
|
||||
|
||||
// ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body.
|
||||
func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput")
|
||||
}
|
||||
|
||||
// ShowConsoleOutput makes a request against the nova API to get console log from the server
|
||||
func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) {
|
||||
b, err := opts.ToServerShowConsoleOutputMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
425
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
Normal file
425
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
package servers
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
type serverResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets any serverResult as a Server, if possible.
|
||||
func (r serverResult) Extract() (*Server, error) {
|
||||
var s Server
|
||||
err := r.ExtractInto(&s)
|
||||
return &s, err
|
||||
}
|
||||
|
||||
func (r serverResult) ExtractInto(v interface{}) error {
|
||||
return r.Result.ExtractIntoStructPtr(v, "server")
|
||||
}
|
||||
|
||||
func ExtractServersInto(r pagination.Page, v interface{}) error {
|
||||
return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers")
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create operation. Call its Extract
|
||||
// method to interpret it as a Server.
|
||||
type CreateResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get operation. Call its Extract
|
||||
// method to interpret it as a Server.
|
||||
type GetResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// UpdateResult is the response from an Update operation. Call its Extract
|
||||
// method to interpret it as a Server.
|
||||
type UpdateResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
||||
// method to determine if the call succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// RebuildResult is the response from a Rebuild operation. Call its Extract
|
||||
// method to interpret it as a Server.
|
||||
type RebuildResult struct {
|
||||
serverResult
|
||||
}
|
||||
|
||||
// ActionResult represents the result of server action operations, like reboot.
|
||||
// Call its ExtractErr method to determine if the action succeeded or failed.
|
||||
type ActionResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// CreateImageResult is the response from a CreateImage operation. Call its
|
||||
// ExtractImageID method to retrieve the ID of the newly created image.
|
||||
type CreateImageResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// ShowConsoleOutputResult represents the result of console output from a server
|
||||
type ShowConsoleOutputResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract will return the console output from a ShowConsoleOutput request.
|
||||
func (r ShowConsoleOutputResult) Extract() (string, error) {
|
||||
var s struct {
|
||||
Output string `json:"output"`
|
||||
}
|
||||
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Output, err
|
||||
}
|
||||
|
||||
// GetPasswordResult represent the result of a get os-server-password operation.
|
||||
// Call its ExtractPassword method to retrieve the password.
|
||||
type GetPasswordResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// ExtractPassword gets the encrypted password.
|
||||
// If privateKey != nil the password is decrypted with the private key.
|
||||
// If privateKey == nil the encrypted password is returned and can be decrypted
|
||||
// with:
|
||||
// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
|
||||
func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
|
||||
var s struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err == nil && privateKey != nil && s.Password != "" {
|
||||
return decryptPassword(s.Password, privateKey)
|
||||
}
|
||||
return s.Password, err
|
||||
}
|
||||
|
||||
func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
|
||||
b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
|
||||
|
||||
n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
|
||||
}
|
||||
password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to decrypt password: %s", err)
|
||||
}
|
||||
|
||||
return string(password), nil
|
||||
}
|
||||
|
||||
// ExtractImageID gets the ID of the newly created server image from the header.
|
||||
func (r CreateImageResult) ExtractImageID() (string, error) {
|
||||
if r.Err != nil {
|
||||
return "", r.Err
|
||||
}
|
||||
// Get the image id from the header
|
||||
u, err := url.ParseRequestURI(r.Header.Get("Location"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imageID := path.Base(u.Path)
|
||||
if imageID == "." || imageID == "/" {
|
||||
return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
|
||||
}
|
||||
return imageID, nil
|
||||
}
|
||||
|
||||
// Server represents a server/instance in the OpenStack cloud.
|
||||
type Server struct {
|
||||
// ID uniquely identifies this server amongst all other servers,
|
||||
// including those not accessible to the current tenant.
|
||||
ID string `json:"id"`
|
||||
|
||||
// TenantID identifies the tenant owning this server resource.
|
||||
TenantID string `json:"tenant_id"`
|
||||
|
||||
// UserID uniquely identifies the user account owning the tenant.
|
||||
UserID string `json:"user_id"`
|
||||
|
||||
// Name contains the human-readable name for the server.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Updated and Created contain ISO-8601 timestamps of when the state of the
|
||||
// server last changed, and when it was created.
|
||||
Updated time.Time `json:"updated"`
|
||||
Created time.Time `json:"created"`
|
||||
|
||||
// HostID is the host where the server is located in the cloud.
|
||||
HostID string `json:"hostid"`
|
||||
|
||||
// Status contains the current operational status of the server,
|
||||
// such as IN_PROGRESS or ACTIVE.
|
||||
Status string `json:"status"`
|
||||
|
||||
// Progress ranges from 0..100.
|
||||
// A request made against the server completes only once Progress reaches 100.
|
||||
Progress int `json:"progress"`
|
||||
|
||||
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server,
|
||||
// suitable for remote access for administration.
|
||||
AccessIPv4 string `json:"accessIPv4"`
|
||||
AccessIPv6 string `json:"accessIPv6"`
|
||||
|
||||
// Image refers to a JSON object, which itself indicates the OS image used to
|
||||
// deploy the server.
|
||||
Image map[string]interface{} `json:"-"`
|
||||
|
||||
// Flavor refers to a JSON object, which itself indicates the hardware
|
||||
// configuration of the deployed server.
|
||||
Flavor map[string]interface{} `json:"flavor"`
|
||||
|
||||
// Addresses includes a list of all IP addresses assigned to the server,
|
||||
// keyed by pool.
|
||||
Addresses map[string]interface{} `json:"addresses"`
|
||||
|
||||
// Metadata includes a list of all user-specified key-value pairs attached
|
||||
// to the server.
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
|
||||
// Links includes HTTP references to the itself, useful for passing along to
|
||||
// other APIs that might want a server reference.
|
||||
Links []interface{} `json:"links"`
|
||||
|
||||
// KeyName indicates which public key was injected into the server on launch.
|
||||
KeyName string `json:"key_name"`
|
||||
|
||||
// AdminPass will generally be empty (""). However, it will contain the
|
||||
// administrative password chosen when provisioning a new server without a
|
||||
// set AdminPass setting in the first place.
|
||||
// Note that this is the ONLY time this field will be valid.
|
||||
AdminPass string `json:"adminPass"`
|
||||
|
||||
// SecurityGroups includes the security groups that this instance has applied
|
||||
// to it.
|
||||
SecurityGroups []map[string]interface{} `json:"security_groups"`
|
||||
|
||||
// AttachedVolumes includes the volume attachments of this instance
|
||||
AttachedVolumes []AttachedVolume `json:"os-extended-volumes:volumes_attached"`
|
||||
|
||||
// Fault contains failure information about a server.
|
||||
Fault Fault `json:"fault"`
|
||||
|
||||
// Tags is a slice/list of string tags in a server.
|
||||
// The requires microversion 2.26 or later.
|
||||
Tags *[]string `json:"tags"`
|
||||
}
|
||||
|
||||
type AttachedVolume struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type Fault struct {
|
||||
Code int `json:"code"`
|
||||
Created time.Time `json:"created"`
|
||||
Details string `json:"details"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (r *Server) UnmarshalJSON(b []byte) error {
|
||||
type tmp Server
|
||||
var s struct {
|
||||
tmp
|
||||
Image interface{} `json:"image"`
|
||||
}
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*r = Server(s.tmp)
|
||||
|
||||
switch t := s.Image.(type) {
|
||||
case map[string]interface{}:
|
||||
r.Image = t
|
||||
case string:
|
||||
switch t {
|
||||
case "":
|
||||
r.Image = nil
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ServerPage abstracts the raw results of making a List() request against
|
||||
// the API. As OpenStack extensions may freely alter the response bodies of
|
||||
// structures returned to the client, you may only safely access the data
|
||||
// provided through the ExtractServers call.
|
||||
type ServerPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty returns true if a page contains no Server results.
|
||||
func (r ServerPage) IsEmpty() (bool, error) {
|
||||
s, err := ExtractServers(r)
|
||||
return len(s) == 0, err
|
||||
}
|
||||
|
||||
// NextPageURL uses the response's embedded link reference to navigate to the
|
||||
// next page of results.
|
||||
func (r ServerPage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links []gophercloud.Link `json:"servers_links"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gophercloud.ExtractNextURL(s.Links)
|
||||
}
|
||||
|
||||
// ExtractServers interprets the results of a single page from a List() call,
|
||||
// producing a slice of Server entities.
|
||||
func ExtractServers(r pagination.Page) ([]Server, error) {
|
||||
var s []Server
|
||||
err := ExtractServersInto(r, &s)
|
||||
return s, err
|
||||
}
|
||||
|
||||
// MetadataResult contains the result of a call for (potentially) multiple
|
||||
// key-value pairs. Call its Extract method to interpret it as a
|
||||
// map[string]interface.
|
||||
type MetadataResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// GetMetadataResult contains the result of a Get operation. Call its Extract
|
||||
// method to interpret it as a map[string]interface.
|
||||
type GetMetadataResult struct {
|
||||
MetadataResult
|
||||
}
|
||||
|
||||
// ResetMetadataResult contains the result of a Reset operation. Call its
|
||||
// Extract method to interpret it as a map[string]interface.
|
||||
type ResetMetadataResult struct {
|
||||
MetadataResult
|
||||
}
|
||||
|
||||
// UpdateMetadataResult contains the result of an Update operation. Call its
|
||||
// Extract method to interpret it as a map[string]interface.
|
||||
type UpdateMetadataResult struct {
|
||||
MetadataResult
|
||||
}
|
||||
|
||||
// MetadatumResult contains the result of a call for individual a single
|
||||
// key-value pair.
|
||||
type MetadatumResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// GetMetadatumResult contains the result of a Get operation. Call its Extract
|
||||
// method to interpret it as a map[string]interface.
|
||||
type GetMetadatumResult struct {
|
||||
MetadatumResult
|
||||
}
|
||||
|
||||
// CreateMetadatumResult contains the result of a Create operation. Call its
|
||||
// Extract method to interpret it as a map[string]interface.
|
||||
type CreateMetadatumResult struct {
|
||||
MetadatumResult
|
||||
}
|
||||
|
||||
// DeleteMetadatumResult contains the result of a Delete operation. Call its
|
||||
// ExtractErr method to determine if the call succeeded or failed.
|
||||
type DeleteMetadatumResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// Extract interprets any MetadataResult as a Metadata, if possible.
|
||||
func (r MetadataResult) Extract() (map[string]string, error) {
|
||||
var s struct {
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Metadata, err
|
||||
}
|
||||
|
||||
// Extract interprets any MetadatumResult as a Metadatum, if possible.
|
||||
func (r MetadatumResult) Extract() (map[string]string, error) {
|
||||
var s struct {
|
||||
Metadatum map[string]string `json:"meta"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Metadatum, err
|
||||
}
|
||||
|
||||
// Address represents an IP address.
|
||||
type Address struct {
|
||||
Version int `json:"version"`
|
||||
Address string `json:"addr"`
|
||||
}
|
||||
|
||||
// AddressPage abstracts the raw results of making a ListAddresses() request
|
||||
// against the API. As OpenStack extensions may freely alter the response bodies
|
||||
// of structures returned to the client, you may only safely access the data
|
||||
// provided through the ExtractAddresses call.
|
||||
type AddressPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty returns true if an AddressPage contains no networks.
|
||||
func (r AddressPage) IsEmpty() (bool, error) {
|
||||
addresses, err := ExtractAddresses(r)
|
||||
return len(addresses) == 0, err
|
||||
}
|
||||
|
||||
// ExtractAddresses interprets the results of a single page from a
|
||||
// ListAddresses() call, producing a map of addresses.
|
||||
func ExtractAddresses(r pagination.Page) (map[string][]Address, error) {
|
||||
var s struct {
|
||||
Addresses map[string][]Address `json:"addresses"`
|
||||
}
|
||||
err := (r.(AddressPage)).ExtractInto(&s)
|
||||
return s.Addresses, err
|
||||
}
|
||||
|
||||
// NetworkAddressPage abstracts the raw results of making a
|
||||
// ListAddressesByNetwork() request against the API.
|
||||
// As OpenStack extensions may freely alter the response bodies of structures
|
||||
// returned to the client, you may only safely access the data provided through
|
||||
// the ExtractAddresses call.
|
||||
type NetworkAddressPage struct {
|
||||
pagination.SinglePageBase
|
||||
}
|
||||
|
||||
// IsEmpty returns true if a NetworkAddressPage contains no addresses.
|
||||
func (r NetworkAddressPage) IsEmpty() (bool, error) {
|
||||
addresses, err := ExtractNetworkAddresses(r)
|
||||
return len(addresses) == 0, err
|
||||
}
|
||||
|
||||
// ExtractNetworkAddresses interprets the results of a single page from a
|
||||
// ListAddressesByNetwork() call, producing a slice of addresses.
|
||||
func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) {
|
||||
var s map[string][]Address
|
||||
err := (r.(NetworkAddressPage)).ExtractInto(&s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var key string
|
||||
for k := range s {
|
||||
key = k
|
||||
}
|
||||
|
||||
return s[key], err
|
||||
}
|
||||
51
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go
generated
vendored
Normal file
51
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package servers
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func createURL(client *gophercloud.ServiceClient) string {
|
||||
return client.ServiceURL("servers")
|
||||
}
|
||||
|
||||
func listURL(client *gophercloud.ServiceClient) string {
|
||||
return createURL(client)
|
||||
}
|
||||
|
||||
func listDetailURL(client *gophercloud.ServiceClient) string {
|
||||
return client.ServiceURL("servers", "detail")
|
||||
}
|
||||
|
||||
func deleteURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return client.ServiceURL("servers", id)
|
||||
}
|
||||
|
||||
func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return deleteURL(client, id)
|
||||
}
|
||||
|
||||
func updateURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return deleteURL(client, id)
|
||||
}
|
||||
|
||||
func actionURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return client.ServiceURL("servers", id, "action")
|
||||
}
|
||||
|
||||
func metadatumURL(client *gophercloud.ServiceClient, id, key string) string {
|
||||
return client.ServiceURL("servers", id, "metadata", key)
|
||||
}
|
||||
|
||||
func metadataURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return client.ServiceURL("servers", id, "metadata")
|
||||
}
|
||||
|
||||
func listAddressesURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return client.ServiceURL("servers", id, "ips")
|
||||
}
|
||||
|
||||
func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string {
|
||||
return client.ServiceURL("servers", id, "ips", network)
|
||||
}
|
||||
|
||||
func passwordURL(client *gophercloud.ServiceClient, id string) string {
|
||||
return client.ServiceURL("servers", id, "os-server-password")
|
||||
}
|
||||
21
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go
generated
vendored
Normal file
21
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package servers
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
// WaitForStatus will continually poll a server until it successfully
|
||||
// transitions to a specified status. It will do this for at most the number
|
||||
// of seconds specified.
|
||||
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
||||
return gophercloud.WaitFor(secs, func() (bool, error) {
|
||||
current, err := Get(c, id).Extract()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if current.Status == status {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
14
vendor/github.com/gophercloud/gophercloud/openstack/doc.go
generated
vendored
Normal file
14
vendor/github.com/gophercloud/gophercloud/openstack/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
Package openstack contains resources for the individual OpenStack projects
|
||||
supported in Gophercloud. It also includes functions to authenticate to an
|
||||
OpenStack cloud and for provisioning various service-level clients.
|
||||
|
||||
Example of Creating a Service Client
|
||||
|
||||
ao, err := openstack.AuthOptionsFromEnv()
|
||||
provider, err := openstack.AuthenticatedClient(ao)
|
||||
client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
|
||||
Region: os.Getenv("OS_REGION_NAME"),
|
||||
})
|
||||
*/
|
||||
package openstack
|
||||
111
vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
generated
vendored
Normal file
111
vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
generated
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||
)
|
||||
|
||||
/*
|
||||
V2EndpointURL discovers the endpoint URL for a specific service from a
|
||||
ServiceCatalog acquired during the v2 identity service.
|
||||
|
||||
The specified EndpointOpts are used to identify a unique, unambiguous endpoint
|
||||
to return. It's an error both when multiple endpoints match the provided
|
||||
criteria and when none do. The minimum that can be specified is a Type, but you
|
||||
will also often need to specify a Name and/or a Region depending on what's
|
||||
available on your OpenStack deployment.
|
||||
*/
|
||||
func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) {
|
||||
// Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided.
|
||||
var endpoints = make([]tokens2.Endpoint, 0, 1)
|
||||
for _, entry := range catalog.Entries {
|
||||
if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
|
||||
for _, endpoint := range entry.Endpoints {
|
||||
if opts.Region == "" || endpoint.Region == opts.Region {
|
||||
endpoints = append(endpoints, endpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If multiple endpoints were found, use the first result
|
||||
// and disregard the other endpoints.
|
||||
//
|
||||
// This behavior matches the Python library. See GH-1764.
|
||||
if len(endpoints) > 1 {
|
||||
endpoints = endpoints[0:1]
|
||||
}
|
||||
|
||||
// Extract the appropriate URL from the matching Endpoint.
|
||||
for _, endpoint := range endpoints {
|
||||
switch opts.Availability {
|
||||
case gophercloud.AvailabilityPublic:
|
||||
return gophercloud.NormalizeURL(endpoint.PublicURL), nil
|
||||
case gophercloud.AvailabilityInternal:
|
||||
return gophercloud.NormalizeURL(endpoint.InternalURL), nil
|
||||
case gophercloud.AvailabilityAdmin:
|
||||
return gophercloud.NormalizeURL(endpoint.AdminURL), nil
|
||||
default:
|
||||
err := &ErrInvalidAvailabilityProvided{}
|
||||
err.Argument = "Availability"
|
||||
err.Value = opts.Availability
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Report an error if there were no matching endpoints.
|
||||
err := &gophercloud.ErrEndpointNotFound{}
|
||||
return "", err
|
||||
}
|
||||
|
||||
/*
|
||||
V3EndpointURL discovers the endpoint URL for a specific service from a Catalog
|
||||
acquired during the v3 identity service.
|
||||
|
||||
The specified EndpointOpts are used to identify a unique, unambiguous endpoint
|
||||
to return. It's an error both when multiple endpoints match the provided
|
||||
criteria and when none do. The minimum that can be specified is a Type, but you
|
||||
will also often need to specify a Name and/or a Region depending on what's
|
||||
available on your OpenStack deployment.
|
||||
*/
|
||||
func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) {
|
||||
// Extract Endpoints from the catalog entries that match the requested Type, Interface,
|
||||
// Name if provided, and Region if provided.
|
||||
var endpoints = make([]tokens3.Endpoint, 0, 1)
|
||||
for _, entry := range catalog.Entries {
|
||||
if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
|
||||
for _, endpoint := range entry.Endpoints {
|
||||
if opts.Availability != gophercloud.AvailabilityAdmin &&
|
||||
opts.Availability != gophercloud.AvailabilityPublic &&
|
||||
opts.Availability != gophercloud.AvailabilityInternal {
|
||||
err := &ErrInvalidAvailabilityProvided{}
|
||||
err.Argument = "Availability"
|
||||
err.Value = opts.Availability
|
||||
return "", err
|
||||
}
|
||||
if (opts.Availability == gophercloud.Availability(endpoint.Interface)) &&
|
||||
(opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) {
|
||||
endpoints = append(endpoints, endpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If multiple endpoints were found, use the first result
|
||||
// and disregard the other endpoints.
|
||||
//
|
||||
// This behavior matches the Python library. See GH-1764.
|
||||
if len(endpoints) > 1 {
|
||||
endpoints = endpoints[0:1]
|
||||
}
|
||||
|
||||
// Extract the URL from the matching Endpoint.
|
||||
for _, endpoint := range endpoints {
|
||||
return gophercloud.NormalizeURL(endpoint.URL), nil
|
||||
}
|
||||
|
||||
// Report an error if there were no matching endpoints.
|
||||
err := &gophercloud.ErrEndpointNotFound{}
|
||||
return "", err
|
||||
}
|
||||
47
vendor/github.com/gophercloud/gophercloud/openstack/errors.go
generated
vendored
Normal file
47
vendor/github.com/gophercloud/gophercloud/openstack/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
// ErrEndpointNotFound is the error when no suitable endpoint can be found
|
||||
// in the user's catalog
|
||||
type ErrEndpointNotFound struct{ gophercloud.BaseError }
|
||||
|
||||
func (e ErrEndpointNotFound) Error() string {
|
||||
return "No suitable endpoint could be found in the service catalog."
|
||||
}
|
||||
|
||||
// ErrInvalidAvailabilityProvided is the error when an invalid endpoint
|
||||
// availability is provided
|
||||
type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput }
|
||||
|
||||
func (e ErrInvalidAvailabilityProvided) Error() string {
|
||||
return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value)
|
||||
}
|
||||
|
||||
// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not
|
||||
// found
|
||||
type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput }
|
||||
|
||||
func (e ErrNoAuthURL) Error() string {
|
||||
return "Environment variable OS_AUTH_URL needs to be set."
|
||||
}
|
||||
|
||||
// ErrNoUsername is the error when the OS_USERNAME environment variable is not
|
||||
// found
|
||||
type ErrNoUsername struct{ gophercloud.ErrInvalidInput }
|
||||
|
||||
func (e ErrNoUsername) Error() string {
|
||||
return "Environment variable OS_USERNAME needs to be set."
|
||||
}
|
||||
|
||||
// ErrNoPassword is the error when the OS_PASSWORD environment variable is not
|
||||
// found
|
||||
type ErrNoPassword struct{ gophercloud.ErrInvalidInput }
|
||||
|
||||
func (e ErrNoPassword) Error() string {
|
||||
return "Environment variable OS_PASSWORD needs to be set."
|
||||
}
|
||||
65
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go
generated
vendored
Normal file
65
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Package tenants provides information and interaction with the
|
||||
tenants API resource for the OpenStack Identity service.
|
||||
|
||||
See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
|
||||
and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants
|
||||
for more information.
|
||||
|
||||
Example to List Tenants
|
||||
|
||||
listOpts := tenants.ListOpts{
|
||||
Limit: 2,
|
||||
}
|
||||
|
||||
allPages, err := tenants.List(identityClient, listOpts).AllPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
allTenants, err := tenants.ExtractTenants(allPages)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, tenant := range allTenants {
|
||||
fmt.Printf("%+v\n", tenant)
|
||||
}
|
||||
|
||||
Example to Create a Tenant
|
||||
|
||||
createOpts := tenants.CreateOpts{
|
||||
Name: "tenant_name",
|
||||
Description: "this is a tenant",
|
||||
Enabled: gophercloud.Enabled,
|
||||
}
|
||||
|
||||
tenant, err := tenants.Create(identityClient, createOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Update a Tenant
|
||||
|
||||
tenantID := "e6db6ed6277c461a853458589063b295"
|
||||
|
||||
updateOpts := tenants.UpdateOpts{
|
||||
Description: "this is a new description",
|
||||
Enabled: gophercloud.Disabled,
|
||||
}
|
||||
|
||||
tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Delete a Tenant
|
||||
|
||||
tenantID := "e6db6ed6277c461a853458589063b295"
|
||||
|
||||
err := tenants.Delete(identitYClient, tenantID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package tenants
|
||||
120
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
Normal file
120
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package tenants
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOpts filters the Tenants that are returned by the List call.
|
||||
type ListOpts struct {
|
||||
// Marker is the ID of the last Tenant on the previous page.
|
||||
Marker string `q:"marker"`
|
||||
|
||||
// Limit specifies the page size.
|
||||
Limit int `q:"limit"`
|
||||
}
|
||||
|
||||
// List enumerates the Tenants to which the current token has access.
|
||||
func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
|
||||
url := listURL(client)
|
||||
if opts != nil {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += q.String()
|
||||
}
|
||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||
return TenantPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOpts represents the options needed when creating new tenant.
|
||||
type CreateOpts struct {
|
||||
// Name is the name of the tenant.
|
||||
Name string `json:"name" required:"true"`
|
||||
|
||||
// Description is the description of the tenant.
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Enabled sets the tenant status to enabled or disabled.
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
// CreateOptsBuilder enables extensions to add additional parameters to the
|
||||
// Create request.
|
||||
type CreateOptsBuilder interface {
|
||||
ToTenantCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ToTenantCreateMap assembles a request body based on the contents of
|
||||
// a CreateOpts.
|
||||
func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "tenant")
|
||||
}
|
||||
|
||||
// Create is the operation responsible for creating new tenant.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToTenantCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 201},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get requests details on a single tenant by ID.
|
||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
resp, err := client.Get(getURL(client, id), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||
// Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
ToTenantUpdateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts specifies the base attributes that may be updated on an existing
|
||||
// tenant.
|
||||
type UpdateOpts struct {
|
||||
// Name is the name of the tenant.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Description is the description of the tenant.
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled sets the tenant status to enabled or disabled.
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
// ToTenantUpdateMap formats an UpdateOpts structure into a request body.
|
||||
func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "tenant")
|
||||
}
|
||||
|
||||
// Update is the operation responsible for updating exist tenants by their TenantID.
|
||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToTenantUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete is the operation responsible for permanently deleting a tenant.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||
resp, err := client.Delete(deleteURL(client, id), nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
91
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
generated
vendored
Normal file
91
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package tenants
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Tenant is a grouping of users in the identity service.
|
||||
type Tenant struct {
|
||||
// ID is a unique identifier for this tenant.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Name is a friendlier user-facing name for this tenant.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Description is a human-readable explanation of this Tenant's purpose.
|
||||
Description string `json:"description"`
|
||||
|
||||
// Enabled indicates whether or not a tenant is active.
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// TenantPage is a single page of Tenant results.
|
||||
type TenantPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a page of Tenants contains any results.
|
||||
func (r TenantPage) IsEmpty() (bool, error) {
|
||||
tenants, err := ExtractTenants(r)
|
||||
return len(tenants) == 0, err
|
||||
}
|
||||
|
||||
// NextPageURL extracts the "next" link from the tenants_links section of the result.
|
||||
func (r TenantPage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links []gophercloud.Link `json:"tenants_links"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gophercloud.ExtractNextURL(s.Links)
|
||||
}
|
||||
|
||||
// ExtractTenants returns a slice of Tenants contained in a single page of
|
||||
// results.
|
||||
func ExtractTenants(r pagination.Page) ([]Tenant, error) {
|
||||
var s struct {
|
||||
Tenants []Tenant `json:"tenants"`
|
||||
}
|
||||
err := (r.(TenantPage)).ExtractInto(&s)
|
||||
return s.Tenants, err
|
||||
}
|
||||
|
||||
type tenantResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets any tenantResults as a Tenant.
|
||||
func (r tenantResult) Extract() (*Tenant, error) {
|
||||
var s struct {
|
||||
Tenant *Tenant `json:"tenant"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Tenant, err
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get request. Call its Extract method to
|
||||
// interpret it as a Tenant.
|
||||
type GetResult struct {
|
||||
tenantResult
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create request. Call its Extract method
|
||||
// to interpret it as a Tenant.
|
||||
type CreateResult struct {
|
||||
tenantResult
|
||||
}
|
||||
|
||||
// DeleteResult is the response from a Get request. Call its ExtractErr method
|
||||
// to determine if the call succeeded or failed.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// UpdateResult is the response from a Update request. Call its Extract method
|
||||
// to interpret it as a Tenant.
|
||||
type UpdateResult struct {
|
||||
tenantResult
|
||||
}
|
||||
23
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
generated
vendored
Normal file
23
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package tenants
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func listURL(client *gophercloud.ServiceClient) string {
|
||||
return client.ServiceURL("tenants")
|
||||
}
|
||||
|
||||
func getURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||
return client.ServiceURL("tenants", tenantID)
|
||||
}
|
||||
|
||||
func createURL(client *gophercloud.ServiceClient) string {
|
||||
return client.ServiceURL("tenants")
|
||||
}
|
||||
|
||||
func deleteURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||
return client.ServiceURL("tenants", tenantID)
|
||||
}
|
||||
|
||||
func updateURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||
return client.ServiceURL("tenants", tenantID)
|
||||
}
|
||||
46
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go
generated
vendored
Normal file
46
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Package tokens provides information and interaction with the token API
|
||||
resource for the OpenStack Identity service.
|
||||
|
||||
For more information, see:
|
||||
http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
|
||||
|
||||
Example to Create an Unscoped Token from a Password
|
||||
|
||||
authOpts := gophercloud.AuthOptions{
|
||||
Username: "user",
|
||||
Password: "pass"
|
||||
}
|
||||
|
||||
token, err := tokens.Create(identityClient, authOpts).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token from a Tenant ID and Password
|
||||
|
||||
authOpts := gophercloud.AuthOptions{
|
||||
Username: "user",
|
||||
Password: "password",
|
||||
TenantID: "fc394f2ab2df4114bde39905f800dc57"
|
||||
}
|
||||
|
||||
token, err := tokens.Create(identityClient, authOpts).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token from a Tenant Name and Password
|
||||
|
||||
authOpts := gophercloud.AuthOptions{
|
||||
Username: "user",
|
||||
Password: "password",
|
||||
TenantName: "tenantname"
|
||||
}
|
||||
|
||||
token, err := tokens.Create(identityClient, authOpts).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package tokens
|
||||
105
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
generated
vendored
Normal file
105
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package tokens
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
// PasswordCredentialsV2 represents the required options to authenticate
|
||||
// with a username and password.
|
||||
type PasswordCredentialsV2 struct {
|
||||
Username string `json:"username" required:"true"`
|
||||
Password string `json:"password" required:"true"`
|
||||
}
|
||||
|
||||
// TokenCredentialsV2 represents the required options to authenticate
|
||||
// with a token.
|
||||
type TokenCredentialsV2 struct {
|
||||
ID string `json:"id,omitempty" required:"true"`
|
||||
}
|
||||
|
||||
// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the
|
||||
// AuthOptionsBuilder interface.
|
||||
type AuthOptionsV2 struct {
|
||||
PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
|
||||
|
||||
// The TenantID and TenantName fields are optional for the Identity V2 API.
|
||||
// Some providers allow you to specify a TenantName instead of the TenantId.
|
||||
// Some require both. Your provider's authentication policies will determine
|
||||
// how these fields influence authentication.
|
||||
TenantID string `json:"tenantId,omitempty"`
|
||||
TenantName string `json:"tenantName,omitempty"`
|
||||
|
||||
// TokenCredentials allows users to authenticate (possibly as another user)
|
||||
// with an authentication token ID.
|
||||
TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"`
|
||||
}
|
||||
|
||||
// AuthOptionsBuilder allows extensions to add additional parameters to the
|
||||
// token create request.
|
||||
type AuthOptionsBuilder interface {
|
||||
// ToTokenCreateMap assembles the Create request body, returning an error
|
||||
// if parameters are missing or inconsistent.
|
||||
ToTokenV2CreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// AuthOptions are the valid options for Openstack Identity v2 authentication.
|
||||
// For field descriptions, see gophercloud.AuthOptions.
|
||||
type AuthOptions struct {
|
||||
IdentityEndpoint string `json:"-"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
TenantID string `json:"tenantId,omitempty"`
|
||||
TenantName string `json:"tenantName,omitempty"`
|
||||
AllowReauth bool `json:"-"`
|
||||
TokenID string
|
||||
}
|
||||
|
||||
// ToTokenV2CreateMap builds a token request body from the given AuthOptions.
|
||||
func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
|
||||
v2Opts := AuthOptionsV2{
|
||||
TenantID: opts.TenantID,
|
||||
TenantName: opts.TenantName,
|
||||
}
|
||||
|
||||
if opts.Password != "" {
|
||||
v2Opts.PasswordCredentials = &PasswordCredentialsV2{
|
||||
Username: opts.Username,
|
||||
Password: opts.Password,
|
||||
}
|
||||
} else {
|
||||
v2Opts.TokenCredentials = &TokenCredentialsV2{
|
||||
ID: opts.TokenID,
|
||||
}
|
||||
}
|
||||
|
||||
b, err := gophercloud.BuildRequestBody(v2Opts, "auth")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Create authenticates to the identity service and attempts to acquire a Token.
|
||||
// Generally, rather than interact with this call directly, end users should
|
||||
// call openstack.AuthenticatedClient(), which abstracts all of the gory details
|
||||
// about navigating service catalogs and such.
|
||||
func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) {
|
||||
b, err := auth.ToTokenV2CreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 203},
|
||||
MoreHeaders: map[string]string{"X-Auth-Token": ""},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get validates and retrieves information for user's token.
|
||||
func Get(client *gophercloud.ServiceClient, token string) (r GetResult) {
|
||||
resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200, 203},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
174
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
Normal file
174
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package tokens
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
|
||||
)
|
||||
|
||||
// Token provides only the most basic information related to an authentication
|
||||
// token.
|
||||
type Token struct {
|
||||
// ID provides the primary means of identifying a user to the OpenStack API.
|
||||
// OpenStack defines this field as an opaque value, so do not depend on its
|
||||
// content. It is safe, however, to compare for equality.
|
||||
ID string
|
||||
|
||||
// ExpiresAt provides a timestamp in ISO 8601 format, indicating when the
|
||||
// authentication token becomes invalid. After this point in time, future
|
||||
// API requests made using this authentication token will respond with
|
||||
// errors. Either the caller will need to reauthenticate manually, or more
|
||||
// preferably, the caller should exploit automatic re-authentication.
|
||||
// See the AuthOptions structure for more details.
|
||||
ExpiresAt time.Time
|
||||
|
||||
// Tenant provides information about the tenant to which this token grants
|
||||
// access.
|
||||
Tenant tenants.Tenant
|
||||
}
|
||||
|
||||
// Role is a role for a user.
|
||||
type Role struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// User is an OpenStack user.
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserName string `json:"username"`
|
||||
Roles []Role `json:"roles"`
|
||||
}
|
||||
|
||||
// Endpoint represents a single API endpoint offered by a service.
|
||||
// It provides the public and internal URLs, if supported, along with a region
|
||||
// specifier, again if provided.
|
||||
//
|
||||
// The significance of the Region field will depend upon your provider.
|
||||
//
|
||||
// In addition, the interface offered by the service will have version
|
||||
// information associated with it through the VersionId, VersionInfo, and
|
||||
// VersionList fields, if provided or supported.
|
||||
//
|
||||
// In all cases, fields which aren't supported by the provider and service
|
||||
// combined will assume a zero-value ("").
|
||||
type Endpoint struct {
|
||||
TenantID string `json:"tenantId"`
|
||||
PublicURL string `json:"publicURL"`
|
||||
InternalURL string `json:"internalURL"`
|
||||
AdminURL string `json:"adminURL"`
|
||||
Region string `json:"region"`
|
||||
VersionID string `json:"versionId"`
|
||||
VersionInfo string `json:"versionInfo"`
|
||||
VersionList string `json:"versionList"`
|
||||
}
|
||||
|
||||
// CatalogEntry provides a type-safe interface to an Identity API V2 service
|
||||
// catalog listing.
|
||||
//
|
||||
// Each class of service, such as cloud DNS or block storage services, will have
|
||||
// a single CatalogEntry representing it.
|
||||
//
|
||||
// Note: when looking for the desired service, try, whenever possible, to key
|
||||
// off the type field. Otherwise, you'll tie the representation of the service
|
||||
// to a specific provider.
|
||||
type CatalogEntry struct {
|
||||
// Name will contain the provider-specified name for the service.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Type will contain a type string if OpenStack defines a type for the
|
||||
// service. Otherwise, for provider-specific services, the provider may assign
|
||||
// their own type strings.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Endpoints will let the caller iterate over all the different endpoints that
|
||||
// may exist for the service.
|
||||
Endpoints []Endpoint `json:"endpoints"`
|
||||
}
|
||||
|
||||
// ServiceCatalog provides a view into the service catalog from a previous,
|
||||
// successful authentication.
|
||||
type ServiceCatalog struct {
|
||||
Entries []CatalogEntry
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create request. Use ExtractToken() to
|
||||
// interpret it as a Token, or ExtractServiceCatalog() to interpret it as a
|
||||
// service catalog.
|
||||
type CreateResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// GetResult is the deferred response from a Get call, which is the same with a
|
||||
// Created token. Use ExtractUser() to interpret it as a User.
|
||||
type GetResult struct {
|
||||
CreateResult
|
||||
}
|
||||
|
||||
// ExtractToken returns the just-created Token from a CreateResult.
|
||||
func (r CreateResult) ExtractToken() (*Token, error) {
|
||||
var s struct {
|
||||
Access struct {
|
||||
Token struct {
|
||||
Expires string `json:"expires"`
|
||||
ID string `json:"id"`
|
||||
Tenant tenants.Tenant `json:"tenant"`
|
||||
} `json:"token"`
|
||||
} `json:"access"`
|
||||
}
|
||||
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Token{
|
||||
ID: s.Access.Token.ID,
|
||||
ExpiresAt: expiresTs,
|
||||
Tenant: s.Access.Token.Tenant,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExtractTokenID implements the gophercloud.AuthResult interface. The returned
|
||||
// string is the same as the ID field of the Token struct returned from
|
||||
// ExtractToken().
|
||||
func (r CreateResult) ExtractTokenID() (string, error) {
|
||||
var s struct {
|
||||
Access struct {
|
||||
Token struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"token"`
|
||||
} `json:"access"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Access.Token.ID, err
|
||||
}
|
||||
|
||||
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||
// with the user's Token.
|
||||
func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||
var s struct {
|
||||
Access struct {
|
||||
Entries []CatalogEntry `json:"serviceCatalog"`
|
||||
} `json:"access"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return &ServiceCatalog{Entries: s.Access.Entries}, err
|
||||
}
|
||||
|
||||
// ExtractUser returns the User from a GetResult.
|
||||
func (r GetResult) ExtractUser() (*User, error) {
|
||||
var s struct {
|
||||
Access struct {
|
||||
User User `json:"user"`
|
||||
} `json:"access"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return &s.Access.User, err
|
||||
}
|
||||
13
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go
generated
vendored
Normal file
13
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package tokens
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
// CreateURL generates the URL used to create new Tokens.
|
||||
func CreateURL(client *gophercloud.ServiceClient) string {
|
||||
return client.ServiceURL("tokens")
|
||||
}
|
||||
|
||||
// GetURL generates the URL used to Validate Tokens.
|
||||
func GetURL(client *gophercloud.ServiceClient, token string) string {
|
||||
return client.ServiceURL("tokens", token)
|
||||
}
|
||||
41
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go
generated
vendored
Normal file
41
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Package tokens provides information and interaction with the EC2 token API
|
||||
resource for the OpenStack Identity service.
|
||||
|
||||
For more information, see:
|
||||
https://docs.openstack.org/api-ref/identity/v2-ext/
|
||||
|
||||
Example to Create a Token From an EC2 access and secret keys
|
||||
|
||||
var authOptions tokens.AuthOptionsBuilder
|
||||
authOptions = &ec2tokens.AuthOptions{
|
||||
Access: "a7f1e798b7c2417cba4a02de97dc3cdc",
|
||||
Secret: "18f4f6761ada4e3795fa5273c30349b9",
|
||||
}
|
||||
|
||||
token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to auth a client using EC2 access and secret keys
|
||||
|
||||
client, err := openstack.NewClient("http://localhost:5000/v3")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var authOptions tokens.AuthOptionsBuilder
|
||||
authOptions = &ec2tokens.AuthOptions{
|
||||
Access: "a7f1e798b7c2417cba4a02de97dc3cdc",
|
||||
Secret: "18f4f6761ada4e3795fa5273c30349b9",
|
||||
AllowReauth: true,
|
||||
}
|
||||
|
||||
err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
*/
|
||||
package ec2tokens
|
||||
377
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go
generated
vendored
Normal file
377
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
package ec2tokens
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||
)
|
||||
|
||||
const (
|
||||
// EC2CredentialsAwsRequestV4 is a constant, used to generate AWS
|
||||
// Credential V4.
|
||||
EC2CredentialsAwsRequestV4 = "aws4_request"
|
||||
// EC2CredentialsHmacSha1V2 is a HMAC SHA1 signature method. Used to
|
||||
// generate AWS Credential V2.
|
||||
EC2CredentialsHmacSha1V2 = "HmacSHA1"
|
||||
// EC2CredentialsHmacSha256V2 is a HMAC SHA256 signature method. Used
|
||||
// to generate AWS Credential V2.
|
||||
EC2CredentialsHmacSha256V2 = "HmacSHA256"
|
||||
// EC2CredentialsAwsHmacV4 is an AWS signature V4 signing method.
|
||||
// More details:
|
||||
// https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
||||
EC2CredentialsAwsHmacV4 = "AWS4-HMAC-SHA256"
|
||||
// EC2CredentialsTimestampFormatV4 is an AWS signature V4 timestamp
|
||||
// format.
|
||||
EC2CredentialsTimestampFormatV4 = "20060102T150405Z"
|
||||
// EC2CredentialsDateFormatV4 is an AWS signature V4 date format.
|
||||
EC2CredentialsDateFormatV4 = "20060102"
|
||||
)
|
||||
|
||||
// AuthOptions represents options for authenticating a user using EC2 credentials.
|
||||
type AuthOptions struct {
|
||||
// Access is the EC2 Credential Access ID.
|
||||
Access string `json:"access" required:"true"`
|
||||
// Secret is the EC2 Credential Secret, used to calculate signature.
|
||||
// Not used, when a Signature is is.
|
||||
Secret string `json:"-"`
|
||||
// Host is a HTTP request Host header. Used to calculate an AWS
|
||||
// signature V2. For signature V4 set the Host inside Headers map.
|
||||
// Optional.
|
||||
Host string `json:"host"`
|
||||
// Path is a HTTP request path. Optional.
|
||||
Path string `json:"path"`
|
||||
// Verb is a HTTP request method. Optional.
|
||||
Verb string `json:"verb"`
|
||||
// Headers is a map of HTTP request headers. Optional.
|
||||
Headers map[string]string `json:"headers"`
|
||||
// Region is a region name to calculate an AWS signature V4. Optional.
|
||||
Region string `json:"-"`
|
||||
// Service is a service name to calculate an AWS signature V4. Optional.
|
||||
Service string `json:"-"`
|
||||
// Params is a map of GET method parameters. Optional.
|
||||
Params map[string]string `json:"params"`
|
||||
// AllowReauth allows Gophercloud to re-authenticate automatically
|
||||
// if/when your token expires.
|
||||
AllowReauth bool `json:"-"`
|
||||
// Signature can be either a []byte (encoded to base64 automatically) or
|
||||
// a string. You can set the singature explicitly, when you already know
|
||||
// it. In this case default Params won't be automatically set. Optional.
|
||||
Signature interface{} `json:"signature"`
|
||||
// BodyHash is a HTTP request body sha256 hash. When nil and Signature
|
||||
// is not set, a random hash is generated. Optional.
|
||||
BodyHash *string `json:"body_hash"`
|
||||
// Timestamp is a timestamp to calculate a V4 signature. Optional.
|
||||
Timestamp *time.Time `json:"-"`
|
||||
// Token is a []byte string (encoded to base64 automatically) which was
|
||||
// signed by an EC2 secret key. Used by S3 tokens for validation only.
|
||||
// Token must be set with a Signature. If a Signature is not provided,
|
||||
// a Token will be generated automatically along with a Signature.
|
||||
Token []byte `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string
|
||||
// for an AWS signature V2.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L133
|
||||
func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string {
|
||||
var keys []string
|
||||
for k := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var pairs []string
|
||||
for _, k := range keys {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k])))
|
||||
}
|
||||
|
||||
return strings.Join(pairs, "&")
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature
|
||||
// V2.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148
|
||||
func EC2CredentialsBuildStringToSignV2(opts AuthOptions) []byte {
|
||||
stringToSign := strings.Join([]string{
|
||||
opts.Verb,
|
||||
opts.Host,
|
||||
opts.Path,
|
||||
}, "\n")
|
||||
|
||||
return []byte(strings.Join([]string{
|
||||
stringToSign,
|
||||
EC2CredentialsBuildCanonicalQueryStringV2(opts.Params),
|
||||
}, "\n"))
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string
|
||||
// for an AWS signature V4.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L244
|
||||
func EC2CredentialsBuildCanonicalQueryStringV4(verb string, params map[string]string) string {
|
||||
if verb == "POST" {
|
||||
return ""
|
||||
}
|
||||
return EC2CredentialsBuildCanonicalQueryStringV2(params)
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildCanonicalHeadersV4 builds a canonical string based on
|
||||
// "headers" map and "signedHeaders" string parameters.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L216
|
||||
func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHeaders string) string {
|
||||
headersLower := make(map[string]string, len(headers))
|
||||
for k, v := range headers {
|
||||
headersLower[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
var headersList []string
|
||||
for _, h := range strings.Split(signedHeaders, ";") {
|
||||
if v, ok := headersLower[h]; ok {
|
||||
headersList = append(headersList, h+":"+v)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(headersList, "\n") + "\n"
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on
|
||||
// input parameters.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169
|
||||
func EC2CredentialsBuildSignatureKeyV4(secret, region, service string, date time.Time) []byte {
|
||||
kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date.Format(EC2CredentialsDateFormatV4)))
|
||||
kRegion := sumHMAC256(kDate, []byte(region))
|
||||
kService := sumHMAC256(kRegion, []byte(service))
|
||||
return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4))
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildStringToSignV4 builds an AWS v4 signature string to sign
|
||||
// based on input parameters.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251
|
||||
func EC2CredentialsBuildStringToSignV4(opts AuthOptions, signedHeaders string, bodyHash string, date time.Time) []byte {
|
||||
scope := strings.Join([]string{
|
||||
date.Format(EC2CredentialsDateFormatV4),
|
||||
opts.Region,
|
||||
opts.Service,
|
||||
EC2CredentialsAwsRequestV4,
|
||||
}, "/")
|
||||
|
||||
canonicalRequest := strings.Join([]string{
|
||||
opts.Verb,
|
||||
opts.Path,
|
||||
EC2CredentialsBuildCanonicalQueryStringV4(opts.Verb, opts.Params),
|
||||
EC2CredentialsBuildCanonicalHeadersV4(opts.Headers, signedHeaders),
|
||||
signedHeaders,
|
||||
bodyHash,
|
||||
}, "\n")
|
||||
hash := sha256.Sum256([]byte(canonicalRequest))
|
||||
|
||||
return []byte(strings.Join([]string{
|
||||
EC2CredentialsAwsHmacV4,
|
||||
date.Format(EC2CredentialsTimestampFormatV4),
|
||||
scope,
|
||||
hex.EncodeToString(hash[:]),
|
||||
}, "\n"))
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input
|
||||
// parameters.
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L285..L286
|
||||
func EC2CredentialsBuildSignatureV4(key []byte, stringToSign []byte) string {
|
||||
return hex.EncodeToString(sumHMAC256(key, stringToSign))
|
||||
}
|
||||
|
||||
// EC2CredentialsBuildAuthorizationHeaderV4 builds an AWS v4 Authorization
|
||||
// header based on auth parameters, date and signature
|
||||
func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders string, signature string, date time.Time) string {
|
||||
return fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s",
|
||||
EC2CredentialsAwsHmacV4,
|
||||
opts.Access,
|
||||
date.Format(EC2CredentialsDateFormatV4),
|
||||
opts.Region,
|
||||
opts.Service,
|
||||
EC2CredentialsAwsRequestV4,
|
||||
signedHeaders,
|
||||
signature)
|
||||
}
|
||||
|
||||
// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder
|
||||
// interface.
|
||||
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||
// interface in the v3 tokens package.
|
||||
func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface
|
||||
func (opts *AuthOptions) CanReauth() bool {
|
||||
return opts.AllowReauth
|
||||
}
|
||||
|
||||
// ToTokenV3CreateMap formats an AuthOptions into a create request.
|
||||
func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "credentials")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Signature != nil {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// calculate signature, when it is not set
|
||||
c, _ := b["credentials"].(map[string]interface{})
|
||||
h := interfaceToMap(c, "headers")
|
||||
p := interfaceToMap(c, "params")
|
||||
|
||||
// detect and process a signature v2
|
||||
if v, ok := p["SignatureVersion"]; ok && v == "2" {
|
||||
if _, ok := c["body_hash"]; ok {
|
||||
delete(c, "body_hash")
|
||||
}
|
||||
if _, ok := c["headers"]; ok {
|
||||
delete(c, "headers")
|
||||
}
|
||||
if v, ok := p["SignatureMethod"]; ok {
|
||||
// params is a map of strings
|
||||
strToSign := EC2CredentialsBuildStringToSignV2(*opts)
|
||||
switch v {
|
||||
case EC2CredentialsHmacSha1V2:
|
||||
// keystone uses this method only when HmacSHA256 is not available on the server side
|
||||
// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156
|
||||
c["signature"] = sumHMAC1([]byte(opts.Secret), strToSign)
|
||||
return b, nil
|
||||
case EC2CredentialsHmacSha256V2:
|
||||
c["signature"] = sumHMAC256([]byte(opts.Secret), strToSign)
|
||||
return b, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported signature method: %s", v)
|
||||
}
|
||||
return nil, fmt.Errorf("signature method must be provided")
|
||||
} else if ok {
|
||||
return nil, fmt.Errorf("unsupported signature version: %s", v)
|
||||
}
|
||||
|
||||
// it is not a signature v2, but a signature v4
|
||||
date := time.Now().UTC()
|
||||
if opts.Timestamp != nil {
|
||||
date = *opts.Timestamp
|
||||
}
|
||||
if v, _ := c["body_hash"]; v == nil {
|
||||
// when body_hash is not set, generate a random one
|
||||
c["body_hash"] = randomBodyHash()
|
||||
}
|
||||
|
||||
signedHeaders, _ := h["X-Amz-SignedHeaders"]
|
||||
|
||||
stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date)
|
||||
key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date)
|
||||
c["signature"] = EC2CredentialsBuildSignatureV4(key, stringToSign)
|
||||
h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4)
|
||||
h["Authorization"] = EC2CredentialsBuildAuthorizationHeaderV4(*opts, signedHeaders, c["signature"].(string), date)
|
||||
|
||||
// token is only used for S3 tokens validation and will be removed when using EC2 validation
|
||||
c["token"] = stringToSign
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Create authenticates and either generates a new token from EC2 credentials
|
||||
func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
|
||||
b, err := opts.ToTokenV3CreateMap(nil)
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
// delete "token" element, since it is used in s3tokens
|
||||
deleteBodyElements(b, "token")
|
||||
|
||||
resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{
|
||||
MoreHeaders: map[string]string{"X-Auth-Token": ""},
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't
|
||||
// generate a new token ID, but returns a tokens.CreateResult.
|
||||
func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
|
||||
b, err := opts.ToTokenV3CreateMap(nil)
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
// delete unused element, since it is used in ec2tokens only
|
||||
deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb")
|
||||
|
||||
resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{
|
||||
MoreHeaders: map[string]string{"X-Auth-Token": ""},
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// The following are small helper functions used to help build the signature.
|
||||
|
||||
// sumHMAC1 is a func to implement the HMAC SHA1 signature method.
|
||||
func sumHMAC1(key []byte, data []byte) []byte {
|
||||
hash := hmac.New(sha1.New, key)
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
// sumHMAC256 is a func to implement the HMAC SHA256 signature method.
|
||||
func sumHMAC256(key []byte, data []byte) []byte {
|
||||
hash := hmac.New(sha256.New, key)
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
// randomBodyHash is a func to generate a random sha256 hexdigest.
|
||||
func randomBodyHash() string {
|
||||
h := make([]byte, 64)
|
||||
rand.Read(h)
|
||||
return hex.EncodeToString(h)
|
||||
}
|
||||
|
||||
// interfaceToMap is a func used to represent a "credentials" map element as a
|
||||
// "map[string]string"
|
||||
func interfaceToMap(c map[string]interface{}, key string) map[string]string {
|
||||
// convert map[string]interface{} to map[string]string
|
||||
m := make(map[string]string)
|
||||
if v, _ := c[key].(map[string]interface{}); v != nil {
|
||||
for k, v := range v {
|
||||
m[k] = v.(string)
|
||||
}
|
||||
}
|
||||
|
||||
c[key] = m
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// deleteBodyElements deletes map body elements
|
||||
func deleteBodyElements(b map[string]interface{}, elements ...string) {
|
||||
if c, ok := b["credentials"].(map[string]interface{}); ok {
|
||||
for _, k := range elements {
|
||||
if _, ok := c[k]; ok {
|
||||
delete(c, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go
generated
vendored
Normal file
11
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package ec2tokens
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func ec2tokensURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("ec2tokens")
|
||||
}
|
||||
|
||||
func s3tokensURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("s3tokens")
|
||||
}
|
||||
123
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go
generated
vendored
Normal file
123
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Package oauth1 enables management of OpenStack OAuth1 tokens and Authentication.
|
||||
|
||||
Example to Create an OAuth1 Consumer
|
||||
|
||||
createConsumerOpts := oauth1.CreateConsumerOpts{
|
||||
Description: "My consumer",
|
||||
}
|
||||
consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE: Consumer secret is available only on create response
|
||||
fmt.Printf("Consumer: %+v\n", consumer)
|
||||
|
||||
Example to Request an unauthorized OAuth1 token
|
||||
|
||||
requestTokenOpts := oauth1.RequestTokenOpts{
|
||||
OAuthConsumerKey: consumer.ID,
|
||||
OAuthConsumerSecret: consumer.Secret,
|
||||
OAuthSignatureMethod: oauth1.HMACSHA1,
|
||||
RequestedProjectID: projectID,
|
||||
}
|
||||
requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE: Request token secret is available only on request response
|
||||
fmt.Printf("Request token: %+v\n", requestToken)
|
||||
|
||||
Example to Authorize an unauthorized OAuth1 token
|
||||
|
||||
authorizeTokenOpts := oauth1.AuthorizeTokenOpts{
|
||||
Roles: []oauth1.Role{
|
||||
{Name: "member"},
|
||||
},
|
||||
}
|
||||
authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Verifier ID of the unauthorized Token: %+v\n", authToken.OAuthVerifier)
|
||||
|
||||
Example to Create an OAuth1 Access Token
|
||||
|
||||
accessTokenOpts := oauth1.CreateAccessTokenOpts{
|
||||
OAuthConsumerKey: consumer.ID,
|
||||
OAuthConsumerSecret: consumer.Secret,
|
||||
OAuthToken: requestToken.OAuthToken,
|
||||
OAuthTokenSecret: requestToken.OAuthTokenSecret,
|
||||
OAuthVerifier: authToken.OAuthVerifier,
|
||||
OAuthSignatureMethod: oauth1.HMACSHA1,
|
||||
}
|
||||
accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE: Access token secret is available only on create response
|
||||
fmt.Printf("OAuth1 Access Token: %+v\n", accessToken)
|
||||
|
||||
Example to List User's OAuth1 Access Tokens
|
||||
|
||||
allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
accessTokens, err := oauth1.ExtractAccessTokens(allPages)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, accessToken := range accessTokens {
|
||||
fmt.Printf("Access Token: %+v\n", accessToken)
|
||||
}
|
||||
|
||||
Example to Authenticate a client using OAuth1 method
|
||||
|
||||
client, err := openstack.NewClient("http://localhost:5000/v3")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
authOptions := &oauth1.AuthOptions{
|
||||
// consumer token, created earlier
|
||||
OAuthConsumerKey: consumer.ID,
|
||||
OAuthConsumerSecret: consumer.Secret,
|
||||
// access token, created earlier
|
||||
OAuthToken: accessToken.OAuthToken,
|
||||
OAuthTokenSecret: accessToken.OAuthTokenSecret,
|
||||
OAuthSignatureMethod: oauth1.HMACSHA1,
|
||||
}
|
||||
err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token using OAuth1 method
|
||||
|
||||
var oauth1Token struct {
|
||||
tokens.Token
|
||||
oauth1.TokenExt
|
||||
}
|
||||
|
||||
createOpts := &oauth1.AuthOptions{
|
||||
// consumer token, created earlier
|
||||
OAuthConsumerKey: consumer.ID,
|
||||
OAuthConsumerSecret: consumer.Secret,
|
||||
// access token, created earlier
|
||||
OAuthToken: accessToken.OAuthToken,
|
||||
OAuthTokenSecret: accessToken.OAuthTokenSecret,
|
||||
OAuthSignatureMethod: oauth1.HMACSHA1,
|
||||
}
|
||||
err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
*/
|
||||
package oauth1
|
||||
587
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go
generated
vendored
Normal file
587
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
package oauth1
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Type SignatureMethod is a OAuth1 SignatureMethod type.
|
||||
type SignatureMethod string
|
||||
|
||||
const (
|
||||
// HMACSHA1 is a recommended OAuth1 signature method.
|
||||
HMACSHA1 SignatureMethod = "HMAC-SHA1"
|
||||
|
||||
// PLAINTEXT signature method is not recommended to be used in
|
||||
// production environment.
|
||||
PLAINTEXT SignatureMethod = "PLAINTEXT"
|
||||
|
||||
// OAuth1TokenContentType is a supported content type for an OAuth1
|
||||
// token.
|
||||
OAuth1TokenContentType = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
// AuthOptions represents options for authenticating a user using OAuth1 tokens.
|
||||
type AuthOptions struct {
|
||||
// OAuthConsumerKey is the OAuth1 Consumer Key.
|
||||
OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
|
||||
|
||||
// OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
|
||||
// an OAuth1 request signature.
|
||||
OAuthConsumerSecret string `required:"true"`
|
||||
|
||||
// OAuthToken is the OAuth1 Request Token.
|
||||
OAuthToken string `q:"oauth_token" required:"true"`
|
||||
|
||||
// OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate
|
||||
// an OAuth1 request signature.
|
||||
OAuthTokenSecret string `required:"true"`
|
||||
|
||||
// OAuthSignatureMethod is the OAuth1 signature method the Consumer used
|
||||
// to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
|
||||
// "PLAINTEXT" is not recommended for production usage.
|
||||
OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
|
||||
|
||||
// OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
|
||||
// timestamp will be used.
|
||||
OAuthTimestamp *time.Time
|
||||
|
||||
// OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
|
||||
// uniquely generated for each request. Will be generated automatically
|
||||
// when it is not set.
|
||||
OAuthNonce string `q:"oauth_nonce"`
|
||||
|
||||
// AllowReauth allows Gophercloud to re-authenticate automatically
|
||||
// if/when your token expires.
|
||||
AllowReauth bool
|
||||
}
|
||||
|
||||
// ToTokenV3HeadersMap builds the headers required for an OAuth1-based create
|
||||
// request.
|
||||
func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) {
|
||||
q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret}
|
||||
|
||||
method := headerOpts["method"].(string)
|
||||
u := headerOpts["url"].(string)
|
||||
stringToSign := buildStringToSign(method, u, q.Query())
|
||||
signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
|
||||
|
||||
authHeader := buildAuthHeader(q.Query(), signature)
|
||||
|
||||
headers := map[string]string{
|
||||
"Authorization": authHeader,
|
||||
"X-Auth-Token": "",
|
||||
}
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
// ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder
|
||||
// interface.
|
||||
func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder
|
||||
// interface.
|
||||
func (opts AuthOptions) CanReauth() bool {
|
||||
return opts.AllowReauth
|
||||
}
|
||||
|
||||
// ToTokenV3CreateMap builds a create request body.
|
||||
func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) {
|
||||
// identityReq defines the "identity" portion of an OAuth1-based authentication
|
||||
// create request body.
|
||||
type identityReq struct {
|
||||
Methods []string `json:"methods"`
|
||||
OAuth1 struct{} `json:"oauth1"`
|
||||
}
|
||||
|
||||
// authReq defines the "auth" portion of an OAuth1-based authentication
|
||||
// create request body.
|
||||
type authReq struct {
|
||||
Identity identityReq `json:"identity"`
|
||||
}
|
||||
|
||||
// oauth1Request defines how an OAuth1-based authentication create
|
||||
// request body looks.
|
||||
type oauth1Request struct {
|
||||
Auth authReq `json:"auth"`
|
||||
}
|
||||
|
||||
var req oauth1Request
|
||||
|
||||
req.Auth.Identity.Methods = []string{"oauth1"}
|
||||
return gophercloud.BuildRequestBody(req, "")
|
||||
}
|
||||
|
||||
// Create authenticates and either generates a new OpenStack token from an
|
||||
// OAuth1 token.
|
||||
func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
|
||||
b, err := opts.ToTokenV3CreateMap(nil)
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
headerOpts := map[string]interface{}{
|
||||
"method": "POST",
|
||||
"url": authURL(client),
|
||||
}
|
||||
|
||||
h, err := opts.ToTokenV3HeadersMap(headerOpts)
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||
MoreHeaders: h,
|
||||
OkCodes: []int{201},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateConsumerOptsBuilder allows extensions to add additional parameters to
|
||||
// the CreateConsumer request.
|
||||
type CreateConsumerOptsBuilder interface {
|
||||
ToOAuth1CreateConsumerMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateConsumerOpts provides options used to create a new Consumer.
|
||||
type CreateConsumerOpts struct {
|
||||
// Description is the consumer description.
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request.
|
||||
func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "consumer")
|
||||
}
|
||||
|
||||
// Create creates a new Consumer.
|
||||
func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) {
|
||||
b, err := opts.ToOAuth1CreateConsumerMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{201},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes a Consumer.
|
||||
func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) {
|
||||
resp, err := client.Delete(consumerURL(client, id), nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// List enumerates Consumers.
|
||||
func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager {
|
||||
return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page {
|
||||
return ConsumersPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// GetConsumer retrieves details on a single Consumer by ID.
|
||||
func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) {
|
||||
resp, err := client.Get(consumerURL(client, id), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateConsumerOpts provides options used to update a consumer.
|
||||
type UpdateConsumerOpts struct {
|
||||
// Description is the consumer description.
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update
|
||||
// request.
|
||||
func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "consumer")
|
||||
}
|
||||
|
||||
// UpdateConsumer updates an existing Consumer.
|
||||
func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) {
|
||||
b, err := opts.ToOAuth1UpdateConsumerMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// RequestTokenOptsBuilder allows extensions to add additional parameters to the
|
||||
// RequestToken request.
|
||||
type RequestTokenOptsBuilder interface {
|
||||
ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error)
|
||||
}
|
||||
|
||||
// RequestTokenOpts provides options used to get a consumer unauthorized
|
||||
// request token.
|
||||
type RequestTokenOpts struct {
|
||||
// OAuthConsumerKey is the OAuth1 Consumer Key.
|
||||
OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
|
||||
|
||||
// OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
|
||||
// an OAuth1 request signature.
|
||||
OAuthConsumerSecret string `required:"true"`
|
||||
|
||||
// OAuthSignatureMethod is the OAuth1 signature method the Consumer used
|
||||
// to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
|
||||
// "PLAINTEXT" is not recommended for production usage.
|
||||
OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
|
||||
|
||||
// OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
|
||||
// timestamp will be used.
|
||||
OAuthTimestamp *time.Time
|
||||
|
||||
// OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
|
||||
// uniquely generated for each request. Will be generated automatically
|
||||
// when it is not set.
|
||||
OAuthNonce string `q:"oauth_nonce"`
|
||||
|
||||
// RequestedProjectID is a Project ID a consumer user requested an
|
||||
// access to.
|
||||
RequestedProjectID string `h:"Requested-Project-Id"`
|
||||
}
|
||||
|
||||
// ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request
|
||||
// headers.
|
||||
func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) {
|
||||
q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h, err := gophercloud.BuildHeaders(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signatureKeys := []string{opts.OAuthConsumerSecret}
|
||||
stringToSign := buildStringToSign(method, u, q.Query())
|
||||
signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
|
||||
authHeader := buildAuthHeader(q.Query(), signature)
|
||||
|
||||
h["Authorization"] = authHeader
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// RequestToken requests an unauthorized OAuth1 Token.
|
||||
func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) {
|
||||
h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client))
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{
|
||||
MoreHeaders: h,
|
||||
OkCodes: []int{201},
|
||||
KeepResponseBody: true,
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType {
|
||||
r.Err = fmt.Errorf("unsupported Content-Type: %q", v)
|
||||
return
|
||||
}
|
||||
r.Body, r.Err = ioutil.ReadAll(resp.Body)
|
||||
return
|
||||
}
|
||||
|
||||
// AuthorizeTokenOptsBuilder allows extensions to add additional parameters to
|
||||
// the AuthorizeToken request.
|
||||
type AuthorizeTokenOptsBuilder interface {
|
||||
ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// AuthorizeTokenOpts provides options used to authorize a request token.
|
||||
type AuthorizeTokenOpts struct {
|
||||
Roles []Role `json:"roles"`
|
||||
}
|
||||
|
||||
// Role is a struct representing a role object in a AuthorizeTokenOpts struct.
|
||||
type Role struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token
|
||||
// request.
|
||||
func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) {
|
||||
for _, r := range opts.Roles {
|
||||
if r == (Role{}) {
|
||||
return nil, fmt.Errorf("role must not be empty")
|
||||
}
|
||||
}
|
||||
return gophercloud.BuildRequestBody(opts, "")
|
||||
}
|
||||
|
||||
// AuthorizeToken authorizes an unauthorized consumer token.
|
||||
func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) {
|
||||
b, err := opts.ToOAuth1AuthorizeTokenMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateAccessTokenOptsBuilder allows extensions to add additional parameters
|
||||
// to the CreateAccessToken request.
|
||||
type CreateAccessTokenOptsBuilder interface {
|
||||
ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error)
|
||||
}
|
||||
|
||||
// CreateAccessTokenOpts provides options used to create an OAuth1 token.
|
||||
type CreateAccessTokenOpts struct {
|
||||
// OAuthConsumerKey is the OAuth1 Consumer Key.
|
||||
OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
|
||||
|
||||
// OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
|
||||
// an OAuth1 request signature.
|
||||
OAuthConsumerSecret string `required:"true"`
|
||||
|
||||
// OAuthToken is the OAuth1 Request Token.
|
||||
OAuthToken string `q:"oauth_token" required:"true"`
|
||||
|
||||
// OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate
|
||||
// an OAuth1 request signature.
|
||||
OAuthTokenSecret string `required:"true"`
|
||||
|
||||
// OAuthVerifier is the OAuth1 verification code.
|
||||
OAuthVerifier string `q:"oauth_verifier" required:"true"`
|
||||
|
||||
// OAuthSignatureMethod is the OAuth1 signature method the Consumer used
|
||||
// to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
|
||||
// "PLAINTEXT" is not recommended for production usage.
|
||||
OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
|
||||
|
||||
// OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
|
||||
// timestamp will be used.
|
||||
OAuthTimestamp *time.Time
|
||||
|
||||
// OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
|
||||
// uniquely generated for each request. Will be generated automatically
|
||||
// when it is not set.
|
||||
OAuthNonce string `q:"oauth_nonce"`
|
||||
}
|
||||
|
||||
// ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of
|
||||
// request headers.
|
||||
func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) {
|
||||
q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret}
|
||||
stringToSign := buildStringToSign(method, u, q.Query())
|
||||
signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
|
||||
authHeader := buildAuthHeader(q.Query(), signature)
|
||||
|
||||
headers := map[string]string{
|
||||
"Authorization": authHeader,
|
||||
}
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
// CreateAccessToken creates a new OAuth1 Access Token
|
||||
func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) {
|
||||
h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client))
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{
|
||||
MoreHeaders: h,
|
||||
OkCodes: []int{201},
|
||||
KeepResponseBody: true,
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType {
|
||||
r.Err = fmt.Errorf("unsupported Content-Type: %q", v)
|
||||
return
|
||||
}
|
||||
r.Body, r.Err = ioutil.ReadAll(resp.Body)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAccessToken retrieves details on a single OAuth1 access token by an ID.
|
||||
func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) {
|
||||
resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// RevokeAccessToken revokes an OAuth1 access token.
|
||||
func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) {
|
||||
resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// ListAccessTokens enumerates authorized access tokens.
|
||||
func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager {
|
||||
url := userAccessTokensURL(client, userID)
|
||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||
return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// ListAccessTokenRoles enumerates authorized access token roles.
|
||||
func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager {
|
||||
url := userAccessTokenRolesURL(client, userID, id)
|
||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||
return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
||||
// GetAccessTokenRole retrieves details on a single OAuth1 access token role by
|
||||
// an ID.
|
||||
func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) {
|
||||
resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// The following are small helper functions used to help build the signature.
|
||||
|
||||
// buildOAuth1QueryString builds a URLEncoded parameters string specific for
|
||||
// OAuth1-based requests.
|
||||
func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := q.Query()
|
||||
|
||||
if timestamp != nil {
|
||||
// use provided timestamp
|
||||
query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10))
|
||||
} else {
|
||||
// use current timestamp
|
||||
query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10))
|
||||
}
|
||||
|
||||
if query.Get("oauth_nonce") == "" {
|
||||
// when nonce is not set, generate a random one
|
||||
query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp"))
|
||||
}
|
||||
|
||||
if callback != "" {
|
||||
query.Set("oauth_callback", callback)
|
||||
}
|
||||
query.Set("oauth_version", "1.0")
|
||||
|
||||
return &url.URL{RawQuery: query.Encode()}, nil
|
||||
}
|
||||
|
||||
// buildStringToSign builds a string to be signed.
|
||||
func buildStringToSign(method string, u string, query url.Values) []byte {
|
||||
parsedURL, _ := url.Parse(u)
|
||||
p := parsedURL.Port()
|
||||
s := parsedURL.Scheme
|
||||
|
||||
// Default scheme port must be stripped
|
||||
if s == "http" && p == "80" || s == "https" && p == "443" {
|
||||
parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p)
|
||||
}
|
||||
|
||||
// Ensure that URL doesn't contain queries
|
||||
parsedURL.RawQuery = ""
|
||||
|
||||
v := strings.Join(
|
||||
[]string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&")
|
||||
|
||||
return []byte(v)
|
||||
}
|
||||
|
||||
// signString signs a string using an OAuth1 signature method.
|
||||
func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string {
|
||||
var key []byte
|
||||
for i, k := range signatureKeys {
|
||||
key = append(key, []byte(url.QueryEscape(k))...)
|
||||
if i == 0 {
|
||||
key = append(key, '&')
|
||||
}
|
||||
}
|
||||
|
||||
var signedString string
|
||||
switch signatureMethod {
|
||||
case PLAINTEXT:
|
||||
signedString = string(key)
|
||||
default:
|
||||
h := hmac.New(sha1.New, key)
|
||||
h.Write(strToSign)
|
||||
signedString = base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
return signedString
|
||||
}
|
||||
|
||||
// buildAuthHeader generates an OAuth1 Authorization header with a signature
|
||||
// calculated using an OAuth1 signature method.
|
||||
func buildAuthHeader(query url.Values, signature string) string {
|
||||
var authHeader []string
|
||||
var keys []string
|
||||
for k := range query {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
for _, v := range query[k] {
|
||||
authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v)))
|
||||
}
|
||||
}
|
||||
|
||||
authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature))
|
||||
|
||||
return "OAuth " + strings.Join(authHeader, ", ")
|
||||
}
|
||||
305
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go
generated
vendored
Normal file
305
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
package oauth1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Consumer represents a delegated authorization request between two
|
||||
// identities.
|
||||
type Consumer struct {
|
||||
ID string `json:"id"`
|
||||
Secret string `json:"secret"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type consumerResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// CreateConsumerResult is the response from a Create operation. Call its
|
||||
// Extract method to interpret it as a Consumer.
|
||||
type CreateConsumerResult struct {
|
||||
consumerResult
|
||||
}
|
||||
|
||||
// UpdateConsumerResult is the response from a Create operation. Call its
|
||||
// Extract method to interpret it as a Consumer.
|
||||
type UpdateConsumerResult struct {
|
||||
consumerResult
|
||||
}
|
||||
|
||||
// DeleteConsumerResult is the response from a Delete operation. Call its
|
||||
// ExtractErr to determine if the request succeeded or failed.
|
||||
type DeleteConsumerResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// ConsumersPage is a single page of Region results.
|
||||
type ConsumersPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// GetConsumerResult is the response from a Get operation. Call its Extract
|
||||
// method to interpret it as a Consumer.
|
||||
type GetConsumerResult struct {
|
||||
consumerResult
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a page of Consumers contains any results.
|
||||
func (c ConsumersPage) IsEmpty() (bool, error) {
|
||||
consumers, err := ExtractConsumers(c)
|
||||
return len(consumers) == 0, err
|
||||
}
|
||||
|
||||
// NextPageURL extracts the "next" link from the links section of the result.
|
||||
func (c ConsumersPage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links struct {
|
||||
Next string `json:"next"`
|
||||
Previous string `json:"previous"`
|
||||
} `json:"links"`
|
||||
}
|
||||
err := c.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.Links.Next, err
|
||||
}
|
||||
|
||||
// ExtractConsumers returns a slice of Consumers contained in a single page of
|
||||
// results.
|
||||
func ExtractConsumers(r pagination.Page) ([]Consumer, error) {
|
||||
var s struct {
|
||||
Consumers []Consumer `json:"consumers"`
|
||||
}
|
||||
err := (r.(ConsumersPage)).ExtractInto(&s)
|
||||
return s.Consumers, err
|
||||
}
|
||||
|
||||
// Extract interprets any consumer result as a Consumer.
|
||||
func (c consumerResult) Extract() (*Consumer, error) {
|
||||
var s struct {
|
||||
Consumer *Consumer `json:"consumer"`
|
||||
}
|
||||
err := c.ExtractInto(&s)
|
||||
return s.Consumer, err
|
||||
}
|
||||
|
||||
// Token contains an OAuth1 token.
|
||||
type Token struct {
|
||||
// OAuthToken is the key value for the oauth token that the Identity API returns.
|
||||
OAuthToken string `q:"oauth_token"`
|
||||
// OAuthTokenSecret is the secret value associated with the OAuth Token.
|
||||
OAuthTokenSecret string `q:"oauth_token_secret"`
|
||||
// OAUthExpiresAt is the date and time when an OAuth token expires.
|
||||
OAUthExpiresAt *time.Time `q:"-"`
|
||||
}
|
||||
|
||||
// TokenResult is a struct to handle
|
||||
// "Content-Type: application/x-www-form-urlencoded" response.
|
||||
type TokenResult struct {
|
||||
gophercloud.Result
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// Extract interprets any OAuth1 token result as a Token.
|
||||
func (r TokenResult) Extract() (*Token, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
values, err := url.ParseQuery(string(r.Body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
OAuthToken: values.Get("oauth_token"),
|
||||
OAuthTokenSecret: values.Get("oauth_token_secret"),
|
||||
}
|
||||
|
||||
if v := values.Get("oauth_expires_at"); v != "" {
|
||||
if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
token.OAUthExpiresAt = &t
|
||||
}
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// AuthorizedToken contains an OAuth1 authorized token info.
|
||||
type AuthorizedToken struct {
|
||||
// OAuthVerifier is the ID of the token verifier.
|
||||
OAuthVerifier string `json:"oauth_verifier"`
|
||||
}
|
||||
|
||||
type AuthorizeTokenResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets AuthorizeTokenResult result as a AuthorizedToken.
|
||||
func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) {
|
||||
var s struct {
|
||||
AuthorizedToken *AuthorizedToken `json:"token"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.AuthorizedToken, err
|
||||
}
|
||||
|
||||
// AccessToken represents an AccessToken response as a struct.
|
||||
type AccessToken struct {
|
||||
ID string `json:"id"`
|
||||
ConsumerID string `json:"consumer_id"`
|
||||
ProjectID string `json:"project_id"`
|
||||
AuthorizingUserID string `json:"authorizing_user_id"`
|
||||
ExpiresAt *time.Time `json:"-"`
|
||||
}
|
||||
|
||||
func (r *AccessToken) UnmarshalJSON(b []byte) error {
|
||||
type tmp AccessToken
|
||||
var s struct {
|
||||
tmp
|
||||
ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"`
|
||||
}
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = AccessToken(s.tmp)
|
||||
|
||||
if s.ExpiresAt != nil {
|
||||
t := time.Time(*s.ExpiresAt)
|
||||
r.ExpiresAt = &t
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetAccessTokenResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets any GetAccessTokenResult result as an AccessToken.
|
||||
func (r GetAccessTokenResult) Extract() (*AccessToken, error) {
|
||||
var s struct {
|
||||
AccessToken *AccessToken `json:"access_token"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.AccessToken, err
|
||||
}
|
||||
|
||||
// RevokeAccessTokenResult is the response from a Delete operation. Call its
|
||||
// ExtractErr to determine if the request succeeded or failed.
|
||||
type RevokeAccessTokenResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// AccessTokensPage is a single page of Access Tokens results.
|
||||
type AccessTokensPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a an AccessTokensPage contains any results.
|
||||
func (r AccessTokensPage) IsEmpty() (bool, error) {
|
||||
accessTokens, err := ExtractAccessTokens(r)
|
||||
return len(accessTokens) == 0, err
|
||||
}
|
||||
|
||||
// NextPageURL extracts the "next" link from the links section of the result.
|
||||
func (r AccessTokensPage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links struct {
|
||||
Next string `json:"next"`
|
||||
Previous string `json:"previous"`
|
||||
} `json:"links"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.Links.Next, err
|
||||
}
|
||||
|
||||
// ExtractAccessTokens returns a slice of AccessTokens contained in a single
|
||||
// page of results.
|
||||
func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) {
|
||||
var s struct {
|
||||
AccessTokens []AccessToken `json:"access_tokens"`
|
||||
}
|
||||
err := (r.(AccessTokensPage)).ExtractInto(&s)
|
||||
return s.AccessTokens, err
|
||||
}
|
||||
|
||||
// AccessTokenRole represents an Access Token Role struct.
|
||||
type AccessTokenRole struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DomainID string `json:"domain_id"`
|
||||
}
|
||||
|
||||
// AccessTokenRolesPage is a single page of Access Token roles results.
|
||||
type AccessTokenRolesPage struct {
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty determines whether or not a an AccessTokensPage contains any results.
|
||||
func (r AccessTokenRolesPage) IsEmpty() (bool, error) {
|
||||
accessTokenRoles, err := ExtractAccessTokenRoles(r)
|
||||
return len(accessTokenRoles) == 0, err
|
||||
}
|
||||
|
||||
// NextPageURL extracts the "next" link from the links section of the result.
|
||||
func (r AccessTokenRolesPage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Links struct {
|
||||
Next string `json:"next"`
|
||||
Previous string `json:"previous"`
|
||||
} `json:"links"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.Links.Next, err
|
||||
}
|
||||
|
||||
// ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a
|
||||
// single page of results.
|
||||
func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) {
|
||||
var s struct {
|
||||
AccessTokenRoles []AccessTokenRole `json:"roles"`
|
||||
}
|
||||
err := (r.(AccessTokenRolesPage)).ExtractInto(&s)
|
||||
return s.AccessTokenRoles, err
|
||||
}
|
||||
|
||||
type GetAccessTokenRoleResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole.
|
||||
func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) {
|
||||
var s struct {
|
||||
AccessTokenRole *AccessTokenRole `json:"role"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.AccessTokenRole, err
|
||||
}
|
||||
|
||||
// OAuth1 is an OAuth1 object, returned in OAuth1 token result.
|
||||
type OAuth1 struct {
|
||||
AccessTokenID string `json:"access_token_id"`
|
||||
ConsumerID string `json:"consumer_id"`
|
||||
}
|
||||
|
||||
// TokenExt represents an extension of the base token result.
|
||||
type TokenExt struct {
|
||||
OAuth1 OAuth1 `json:"OS-OAUTH1"`
|
||||
}
|
||||
43
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go
generated
vendored
Normal file
43
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package oauth1
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func consumersURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("OS-OAUTH1", "consumers")
|
||||
}
|
||||
|
||||
func consumerURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL("OS-OAUTH1", "consumers", id)
|
||||
}
|
||||
|
||||
func requestTokenURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("OS-OAUTH1", "request_token")
|
||||
}
|
||||
|
||||
func authorizeTokenURL(c *gophercloud.ServiceClient, id string) string {
|
||||
return c.ServiceURL("OS-OAUTH1", "authorize", id)
|
||||
}
|
||||
|
||||
func createAccessTokenURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("OS-OAUTH1", "access_token")
|
||||
}
|
||||
|
||||
func userAccessTokensURL(c *gophercloud.ServiceClient, userID string) string {
|
||||
return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens")
|
||||
}
|
||||
|
||||
func userAccessTokenURL(c *gophercloud.ServiceClient, userID string, id string) string {
|
||||
return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id)
|
||||
}
|
||||
|
||||
func userAccessTokenRolesURL(c *gophercloud.ServiceClient, userID string, id string) string {
|
||||
return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles")
|
||||
}
|
||||
|
||||
func userAccessTokenRoleURL(c *gophercloud.ServiceClient, userID string, id string, roleID string) string {
|
||||
return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles", roleID)
|
||||
}
|
||||
|
||||
func authURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("auth", "tokens")
|
||||
}
|
||||
108
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go
generated
vendored
Normal file
108
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
Package tokens provides information and interaction with the token API
|
||||
resource for the OpenStack Identity service.
|
||||
|
||||
For more information, see:
|
||||
http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3
|
||||
|
||||
Example to Create a Token From a Username and Password
|
||||
|
||||
authOptions := tokens.AuthOptions{
|
||||
UserID: "username",
|
||||
Password: "password",
|
||||
}
|
||||
|
||||
token, err := tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token From a Username, Password, and Domain
|
||||
|
||||
authOptions := tokens.AuthOptions{
|
||||
UserID: "username",
|
||||
Password: "password",
|
||||
DomainID: "default",
|
||||
}
|
||||
|
||||
token, err := tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
authOptions = tokens.AuthOptions{
|
||||
UserID: "username",
|
||||
Password: "password",
|
||||
DomainName: "default",
|
||||
}
|
||||
|
||||
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token From a Token
|
||||
|
||||
authOptions := tokens.AuthOptions{
|
||||
TokenID: "token_id",
|
||||
}
|
||||
|
||||
token, err := tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token from a Username and Password with Project ID Scope
|
||||
|
||||
scope := tokens.Scope{
|
||||
ProjectID: "0fe36e73809d46aeae6705c39077b1b3",
|
||||
}
|
||||
|
||||
authOptions := tokens.AuthOptions{
|
||||
Scope: &scope,
|
||||
UserID: "username",
|
||||
Password: "password",
|
||||
}
|
||||
|
||||
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token from a Username and Password with Domain ID Scope
|
||||
|
||||
scope := tokens.Scope{
|
||||
DomainID: "default",
|
||||
}
|
||||
|
||||
authOptions := tokens.AuthOptions{
|
||||
Scope: &scope,
|
||||
UserID: "username",
|
||||
Password: "password",
|
||||
}
|
||||
|
||||
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Create a Token from a Username and Password with Project Name Scope
|
||||
|
||||
scope := tokens.Scope{
|
||||
ProjectName: "project_name",
|
||||
DomainID: "default",
|
||||
}
|
||||
|
||||
authOptions := tokens.AuthOptions{
|
||||
Scope: &scope,
|
||||
UserID: "username",
|
||||
Password: "password",
|
||||
}
|
||||
|
||||
token, err = tokens.Create(identityClient, authOptions).ExtractToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
*/
|
||||
package tokens
|
||||
174
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
Normal file
174
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package tokens
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
// Scope allows a created token to be limited to a specific domain or project.
|
||||
type Scope struct {
|
||||
ProjectID string
|
||||
ProjectName string
|
||||
DomainID string
|
||||
DomainName string
|
||||
System bool
|
||||
}
|
||||
|
||||
// AuthOptionsBuilder provides the ability for extensions to add additional
|
||||
// parameters to AuthOptions. Extensions must satisfy all required methods.
|
||||
type AuthOptionsBuilder interface {
|
||||
// ToTokenV3CreateMap assembles the Create request body, returning an error
|
||||
// if parameters are missing or inconsistent.
|
||||
ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error)
|
||||
ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error)
|
||||
ToTokenV3ScopeMap() (map[string]interface{}, error)
|
||||
CanReauth() bool
|
||||
}
|
||||
|
||||
// AuthOptions represents options for authenticating a user.
|
||||
type AuthOptions struct {
|
||||
// IdentityEndpoint specifies the HTTP endpoint that is required to work with
|
||||
// the Identity API of the appropriate version. While it's ultimately needed
|
||||
// by all of the identity services, it will often be populated by a
|
||||
// provider-level function.
|
||||
IdentityEndpoint string `json:"-"`
|
||||
|
||||
// Username is required if using Identity V2 API. Consult with your provider's
|
||||
// control panel to discover your account's username. In Identity V3, either
|
||||
// UserID or a combination of Username and DomainID or DomainName are needed.
|
||||
Username string `json:"username,omitempty"`
|
||||
UserID string `json:"id,omitempty"`
|
||||
|
||||
Password string `json:"password,omitempty"`
|
||||
|
||||
// Passcode is used in TOTP authentication method
|
||||
Passcode string `json:"passcode,omitempty"`
|
||||
|
||||
// At most one of DomainID and DomainName must be provided if using Username
|
||||
// with Identity V3. Otherwise, either are optional.
|
||||
DomainID string `json:"-"`
|
||||
DomainName string `json:"name,omitempty"`
|
||||
|
||||
// AllowReauth should be set to true if you grant permission for Gophercloud
|
||||
// to cache your credentials in memory, and to allow Gophercloud to attempt
|
||||
// to re-authenticate automatically if/when your token expires. If you set
|
||||
// it to false, it will not cache these settings, but re-authentication will
|
||||
// not be possible. This setting defaults to false.
|
||||
AllowReauth bool `json:"-"`
|
||||
|
||||
// TokenID allows users to authenticate (possibly as another user) with an
|
||||
// authentication token ID.
|
||||
TokenID string `json:"-"`
|
||||
|
||||
// Authentication through Application Credentials requires supplying name, project and secret
|
||||
// For project we can use TenantID
|
||||
ApplicationCredentialID string `json:"-"`
|
||||
ApplicationCredentialName string `json:"-"`
|
||||
ApplicationCredentialSecret string `json:"-"`
|
||||
|
||||
Scope Scope `json:"-"`
|
||||
}
|
||||
|
||||
// ToTokenV3CreateMap builds a request body from AuthOptions.
|
||||
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
|
||||
gophercloudAuthOpts := gophercloud.AuthOptions{
|
||||
Username: opts.Username,
|
||||
UserID: opts.UserID,
|
||||
Password: opts.Password,
|
||||
Passcode: opts.Passcode,
|
||||
DomainID: opts.DomainID,
|
||||
DomainName: opts.DomainName,
|
||||
AllowReauth: opts.AllowReauth,
|
||||
TokenID: opts.TokenID,
|
||||
ApplicationCredentialID: opts.ApplicationCredentialID,
|
||||
ApplicationCredentialName: opts.ApplicationCredentialName,
|
||||
ApplicationCredentialSecret: opts.ApplicationCredentialSecret,
|
||||
}
|
||||
|
||||
return gophercloudAuthOpts.ToTokenV3CreateMap(scope)
|
||||
}
|
||||
|
||||
// ToTokenV3ScopeMap builds a scope request body from AuthOptions.
|
||||
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||
scope := gophercloud.AuthScope(opts.Scope)
|
||||
|
||||
gophercloudAuthOpts := gophercloud.AuthOptions{
|
||||
Scope: &scope,
|
||||
DomainID: opts.DomainID,
|
||||
DomainName: opts.DomainName,
|
||||
}
|
||||
|
||||
return gophercloudAuthOpts.ToTokenV3ScopeMap()
|
||||
}
|
||||
|
||||
func (opts *AuthOptions) CanReauth() bool {
|
||||
if opts.Passcode != "" {
|
||||
// cannot reauth using TOTP passcode
|
||||
return false
|
||||
}
|
||||
|
||||
return opts.AllowReauth
|
||||
}
|
||||
|
||||
// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||
// interface in the v3 tokens package.
|
||||
func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func subjectTokenHeaders(subjectToken string) map[string]string {
|
||||
return map[string]string{
|
||||
"X-Subject-Token": subjectToken,
|
||||
}
|
||||
}
|
||||
|
||||
// Create authenticates and either generates a new token, or changes the Scope
|
||||
// of an existing token.
|
||||
func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) {
|
||||
scope, err := opts.ToTokenV3ScopeMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
b, err := opts.ToTokenV3CreateMap(scope)
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{
|
||||
MoreHeaders: map[string]string{"X-Auth-Token": ""},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get validates and retrieves information about another token.
|
||||
func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
|
||||
resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
|
||||
MoreHeaders: subjectTokenHeaders(token),
|
||||
OkCodes: []int{200, 203},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate determines if a specified token is valid or not.
|
||||
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
|
||||
resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{
|
||||
MoreHeaders: subjectTokenHeaders(token),
|
||||
OkCodes: []int{200, 204, 404},
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return resp.StatusCode == 200 || resp.StatusCode == 204, nil
|
||||
}
|
||||
|
||||
// Revoke immediately makes specified token invalid.
|
||||
func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) {
|
||||
resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{
|
||||
MoreHeaders: subjectTokenHeaders(token),
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
194
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
Normal file
194
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
package tokens
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
// Endpoint represents a single API endpoint offered by a service.
|
||||
// It matches either a public, internal or admin URL.
|
||||
// If supported, it contains a region specifier, again if provided.
|
||||
// The significance of the Region field will depend upon your provider.
|
||||
type Endpoint struct {
|
||||
ID string `json:"id"`
|
||||
Region string `json:"region"`
|
||||
RegionID string `json:"region_id"`
|
||||
Interface string `json:"interface"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// CatalogEntry provides a type-safe interface to an Identity API V3 service
|
||||
// catalog listing. Each class of service, such as cloud DNS or block storage
|
||||
// services, could have multiple CatalogEntry representing it (one by interface
|
||||
// type, e.g public, admin or internal).
|
||||
//
|
||||
// Note: when looking for the desired service, try, whenever possible, to key
|
||||
// off the type field. Otherwise, you'll tie the representation of the service
|
||||
// to a specific provider.
|
||||
type CatalogEntry struct {
|
||||
// Service ID
|
||||
ID string `json:"id"`
|
||||
|
||||
// Name will contain the provider-specified name for the service.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Type will contain a type string if OpenStack defines a type for the
|
||||
// service. Otherwise, for provider-specific services, the provider may
|
||||
// assign their own type strings.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Endpoints will let the caller iterate over all the different endpoints that
|
||||
// may exist for the service.
|
||||
Endpoints []Endpoint `json:"endpoints"`
|
||||
}
|
||||
|
||||
// ServiceCatalog provides a view into the service catalog from a previous,
|
||||
// successful authentication.
|
||||
type ServiceCatalog struct {
|
||||
Entries []CatalogEntry `json:"catalog"`
|
||||
}
|
||||
|
||||
// Domain provides information about the domain to which this token grants
|
||||
// access.
|
||||
type Domain struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// User represents a user resource that exists in the Identity Service.
|
||||
type User struct {
|
||||
Domain Domain `json:"domain"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Role provides information about roles to which User is authorized.
|
||||
type Role struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Project provides information about project to which User is authorized.
|
||||
type Project struct {
|
||||
Domain Domain `json:"domain"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// commonResult is the response from a request. A commonResult has various
|
||||
// methods which can be used to extract different details about the result.
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract is a shortcut for ExtractToken.
|
||||
// This function is deprecated and still present for backward compatibility.
|
||||
func (r commonResult) Extract() (*Token, error) {
|
||||
return r.ExtractToken()
|
||||
}
|
||||
|
||||
// ExtractToken interprets a commonResult as a Token.
|
||||
func (r commonResult) ExtractToken() (*Token, error) {
|
||||
var s Token
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the token itself from the stored headers.
|
||||
s.ID = r.Header.Get("X-Subject-Token")
|
||||
|
||||
return &s, err
|
||||
}
|
||||
|
||||
// ExtractTokenID implements the gophercloud.AuthResult interface. The returned
|
||||
// string is the same as the ID field of the Token struct returned from
|
||||
// ExtractToken().
|
||||
func (r CreateResult) ExtractTokenID() (string, error) {
|
||||
return r.Header.Get("X-Subject-Token"), r.Err
|
||||
}
|
||||
|
||||
// ExtractTokenID implements the gophercloud.AuthResult interface. The returned
|
||||
// string is the same as the ID field of the Token struct returned from
|
||||
// ExtractToken().
|
||||
func (r GetResult) ExtractTokenID() (string, error) {
|
||||
return r.Header.Get("X-Subject-Token"), r.Err
|
||||
}
|
||||
|
||||
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||
// with the user's Token.
|
||||
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||
var s ServiceCatalog
|
||||
err := r.ExtractInto(&s)
|
||||
return &s, err
|
||||
}
|
||||
|
||||
// ExtractUser returns the User that is the owner of the Token.
|
||||
func (r commonResult) ExtractUser() (*User, error) {
|
||||
var s struct {
|
||||
User *User `json:"user"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.User, err
|
||||
}
|
||||
|
||||
// ExtractRoles returns Roles to which User is authorized.
|
||||
func (r commonResult) ExtractRoles() ([]Role, error) {
|
||||
var s struct {
|
||||
Roles []Role `json:"roles"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Roles, err
|
||||
}
|
||||
|
||||
// ExtractProject returns Project to which User is authorized.
|
||||
func (r commonResult) ExtractProject() (*Project, error) {
|
||||
var s struct {
|
||||
Project *Project `json:"project"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Project, err
|
||||
}
|
||||
|
||||
// ExtractDomain returns Domain to which User is authorized.
|
||||
func (r commonResult) ExtractDomain() (*Domain, error) {
|
||||
var s struct {
|
||||
Domain *Domain `json:"domain"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Domain, err
|
||||
}
|
||||
|
||||
// CreateResult is the response from a Create request. Use ExtractToken()
|
||||
// to interpret it as a Token, or ExtractServiceCatalog() to interpret it
|
||||
// as a service catalog.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult is the response from a Get request. Use ExtractToken()
|
||||
// to interpret it as a Token, or ExtractServiceCatalog() to interpret it
|
||||
// as a service catalog.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// RevokeResult is response from a Revoke request.
|
||||
type RevokeResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// Token is a string that grants a user access to a controlled set of services
|
||||
// in an OpenStack provider. Each Token is valid for a set length of time.
|
||||
type Token struct {
|
||||
// ID is the issued token.
|
||||
ID string `json:"id"`
|
||||
|
||||
// ExpiresAt is the timestamp at which this token will no longer be accepted.
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
func (r commonResult) ExtractInto(v interface{}) error {
|
||||
return r.ExtractIntoStructPtr(v, "token")
|
||||
}
|
||||
7
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go
generated
vendored
Normal file
7
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package tokens
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
func tokenURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("auth", "tokens")
|
||||
}
|
||||
51
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go
generated
vendored
Normal file
51
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Package imagedata enables management of image data.
|
||||
|
||||
Example to Upload Image Data
|
||||
|
||||
imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea"
|
||||
|
||||
imageData, err := os.Open("/path/to/image/file")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer imageData.Close()
|
||||
|
||||
err = imagedata.Upload(imageClient, imageID, imageData).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Stage Image Data
|
||||
|
||||
imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea"
|
||||
|
||||
imageData, err := os.Open("/path/to/image/file")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer imageData.Close()
|
||||
|
||||
err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Download Image Data
|
||||
|
||||
imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea"
|
||||
|
||||
image, err := imagedata.Download(imageClient, imageID).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// close the reader, when reading has finished
|
||||
defer image.Close()
|
||||
|
||||
imageData, err := ioutil.ReadAll(image)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package imagedata
|
||||
38
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go
generated
vendored
Normal file
38
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package imagedata
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
// Upload uploads an image file.
|
||||
func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) {
|
||||
resp, err := client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{
|
||||
MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"},
|
||||
OkCodes: []int{204},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Stage performs PUT call on the existing image object in the Imageservice with
|
||||
// the provided file.
|
||||
// Existing image object must be in the "queued" status.
|
||||
func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) {
|
||||
resp, err := client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{
|
||||
MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"},
|
||||
OkCodes: []int{204},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Download retrieves an image.
|
||||
func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) {
|
||||
resp, err := client.Get(downloadURL(client, id), nil, &gophercloud.RequestOpts{
|
||||
KeepResponseBody: true,
|
||||
})
|
||||
r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
34
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go
generated
vendored
Normal file
34
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package imagedata
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
// UploadResult is the result of an upload image operation. Call its ExtractErr
|
||||
// method to determine if the request succeeded or failed.
|
||||
type UploadResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// StageResult is the result of a stage image operation. Call its ExtractErr
|
||||
// method to determine if the request succeeded or failed.
|
||||
type StageResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// DownloadResult is the result of a download image operation. Call its Extract
|
||||
// method to gain access to the image data.
|
||||
type DownloadResult struct {
|
||||
gophercloud.Result
|
||||
Body io.ReadCloser
|
||||
}
|
||||
|
||||
// Extract builds images model from io.Reader
|
||||
func (r DownloadResult) Extract() (io.ReadCloser, error) {
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
return r.Body, nil
|
||||
}
|
||||
23
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go
generated
vendored
Normal file
23
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package imagedata
|
||||
|
||||
import "github.com/gophercloud/gophercloud"
|
||||
|
||||
const (
|
||||
rootPath = "images"
|
||||
uploadPath = "file"
|
||||
stagePath = "stage"
|
||||
)
|
||||
|
||||
// `imageDataURL(c,i)` is the URL for the binary image data for the
|
||||
// image identified by ID `i` in the service `c`.
|
||||
func uploadURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return c.ServiceURL(rootPath, imageID, uploadPath)
|
||||
}
|
||||
|
||||
func stageURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return c.ServiceURL(rootPath, imageID, stagePath)
|
||||
}
|
||||
|
||||
func downloadURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return uploadURL(c, imageID)
|
||||
}
|
||||
60
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go
generated
vendored
Normal file
60
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Package images enables management and retrieval of images from the OpenStack
|
||||
Image Service.
|
||||
|
||||
Example to List Images
|
||||
|
||||
images.ListOpts{
|
||||
Owner: "a7509e1ae65945fda83f3e52c6296017",
|
||||
}
|
||||
|
||||
allPages, err := images.List(imagesClient, listOpts).AllPages()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
allImages, err := images.ExtractImages(allPages)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, image := range allImages {
|
||||
fmt.Printf("%+v\n", image)
|
||||
}
|
||||
|
||||
Example to Create an Image
|
||||
|
||||
createOpts := images.CreateOpts{
|
||||
Name: "image_name",
|
||||
Visibility: images.ImageVisibilityPrivate,
|
||||
}
|
||||
|
||||
image, err := images.Create(imageClient, createOpts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Update an Image
|
||||
|
||||
imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27"
|
||||
|
||||
updateOpts := images.UpdateOpts{
|
||||
images.ReplaceImageName{
|
||||
NewName: "new_name",
|
||||
},
|
||||
}
|
||||
|
||||
image, err := images.Update(imageClient, imageID, updateOpts).Extract()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Example to Delete an Image
|
||||
|
||||
imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27"
|
||||
err := images.Delete(imageClient, imageID).ExtractErr()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
package images
|
||||
384
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go
generated
vendored
Normal file
384
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go
generated
vendored
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToImageListQuery() (string, error)
|
||||
}
|
||||
|
||||
// ListOpts allows the filtering and sorting of paginated collections through
|
||||
// the API. Filtering is achieved by passing in struct field values that map to
|
||||
// the server attributes you want to see returned. Marker and Limit are used
|
||||
// for pagination.
|
||||
//
|
||||
// http://developer.openstack.org/api-ref-image-v2.html
|
||||
type ListOpts struct {
|
||||
// ID is the ID of the image.
|
||||
// Multiple IDs can be specified by constructing a string
|
||||
// such as "in:uuid1,uuid2,uuid3".
|
||||
ID string `q:"id"`
|
||||
|
||||
// Integer value for the limit of values to return.
|
||||
Limit int `q:"limit"`
|
||||
|
||||
// UUID of the server at which you want to set a marker.
|
||||
Marker string `q:"marker"`
|
||||
|
||||
// Name filters on the name of the image.
|
||||
// Multiple names can be specified by constructing a string
|
||||
// such as "in:name1,name2,name3".
|
||||
Name string `q:"name"`
|
||||
|
||||
// Visibility filters on the visibility of the image.
|
||||
Visibility ImageVisibility `q:"visibility"`
|
||||
|
||||
// MemberStatus filters on the member status of the image.
|
||||
MemberStatus ImageMemberStatus `q:"member_status"`
|
||||
|
||||
// Owner filters on the project ID of the image.
|
||||
Owner string `q:"owner"`
|
||||
|
||||
// Status filters on the status of the image.
|
||||
// Multiple statuses can be specified by constructing a string
|
||||
// such as "in:saving,queued".
|
||||
Status ImageStatus `q:"status"`
|
||||
|
||||
// SizeMin filters on the size_min image property.
|
||||
SizeMin int64 `q:"size_min"`
|
||||
|
||||
// SizeMax filters on the size_max image property.
|
||||
SizeMax int64 `q:"size_max"`
|
||||
|
||||
// Sort sorts the results using the new style of sorting. See the OpenStack
|
||||
// Image API reference for the exact syntax.
|
||||
//
|
||||
// Sort cannot be used with the classic sort options (sort_key and sort_dir).
|
||||
Sort string `q:"sort"`
|
||||
|
||||
// SortKey will sort the results based on a specified image property.
|
||||
SortKey string `q:"sort_key"`
|
||||
|
||||
// SortDir will sort the list results either ascending or decending.
|
||||
SortDir string `q:"sort_dir"`
|
||||
|
||||
// Tags filters on specific image tags.
|
||||
Tags []string `q:"tag"`
|
||||
|
||||
// CreatedAtQuery filters images based on their creation date.
|
||||
CreatedAtQuery *ImageDateQuery
|
||||
|
||||
// UpdatedAtQuery filters images based on their updated date.
|
||||
UpdatedAtQuery *ImageDateQuery
|
||||
|
||||
// ContainerFormat filters images based on the container_format.
|
||||
// Multiple container formats can be specified by constructing a
|
||||
// string such as "in:bare,ami".
|
||||
ContainerFormat string `q:"container_format"`
|
||||
|
||||
// DiskFormat filters images based on the disk_format.
|
||||
// Multiple disk formats can be specified by constructing a string
|
||||
// such as "in:qcow2,iso".
|
||||
DiskFormat string `q:"disk_format"`
|
||||
}
|
||||
|
||||
// ToImageListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToImageListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
params := q.Query()
|
||||
|
||||
if opts.CreatedAtQuery != nil {
|
||||
createdAt := opts.CreatedAtQuery.Date.Format(time.RFC3339)
|
||||
if v := opts.CreatedAtQuery.Filter; v != "" {
|
||||
createdAt = fmt.Sprintf("%s:%s", v, createdAt)
|
||||
}
|
||||
|
||||
params.Add("created_at", createdAt)
|
||||
}
|
||||
|
||||
if opts.UpdatedAtQuery != nil {
|
||||
updatedAt := opts.UpdatedAtQuery.Date.Format(time.RFC3339)
|
||||
if v := opts.UpdatedAtQuery.Filter; v != "" {
|
||||
updatedAt = fmt.Sprintf("%s:%s", v, updatedAt)
|
||||
}
|
||||
|
||||
params.Add("updated_at", updatedAt)
|
||||
}
|
||||
|
||||
q = &url.URL{RawQuery: params.Encode()}
|
||||
|
||||
return q.String(), err
|
||||
}
|
||||
|
||||
// List implements image list request.
|
||||
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||
url := listURL(c)
|
||||
if opts != nil {
|
||||
query, err := opts.ToImageListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
imagePage := ImagePage{
|
||||
serviceURL: c.ServiceURL(),
|
||||
LinkedPageBase: pagination.LinkedPageBase{PageResult: r},
|
||||
}
|
||||
|
||||
return imagePage
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOptsBuilder allows extensions to add parameters to the Create request.
|
||||
type CreateOptsBuilder interface {
|
||||
// Returns value that can be passed to json.Marshal
|
||||
ToImageCreateMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// CreateOpts represents options used to create an image.
|
||||
type CreateOpts struct {
|
||||
// Name is the name of the new image.
|
||||
Name string `json:"name" required:"true"`
|
||||
|
||||
// Id is the the image ID.
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// Visibility defines who can see/use the image.
|
||||
Visibility *ImageVisibility `json:"visibility,omitempty"`
|
||||
|
||||
// Tags is a set of image tags.
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
|
||||
// ContainerFormat is the format of the
|
||||
// container. Valid values are ami, ari, aki, bare, and ovf.
|
||||
ContainerFormat string `json:"container_format,omitempty"`
|
||||
|
||||
// DiskFormat is the format of the disk. If set,
|
||||
// valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi,
|
||||
// and iso.
|
||||
DiskFormat string `json:"disk_format,omitempty"`
|
||||
|
||||
// MinDisk is the amount of disk space in
|
||||
// GB that is required to boot the image.
|
||||
MinDisk int `json:"min_disk,omitempty"`
|
||||
|
||||
// MinRAM is the amount of RAM in MB that
|
||||
// is required to boot the image.
|
||||
MinRAM int `json:"min_ram,omitempty"`
|
||||
|
||||
// protected is whether the image is not deletable.
|
||||
Protected *bool `json:"protected,omitempty"`
|
||||
|
||||
// properties is a set of properties, if any, that
|
||||
// are associated with the image.
|
||||
Properties map[string]string `json:"-"`
|
||||
}
|
||||
|
||||
// ToImageCreateMap assembles a request body based on the contents of
|
||||
// a CreateOpts.
|
||||
func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) {
|
||||
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Properties != nil {
|
||||
for k, v := range opts.Properties {
|
||||
b[k] = v
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Create implements create image request.
|
||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||
b, err := opts.ToImageCreateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return r
|
||||
}
|
||||
resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete implements image delete request.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||
resp, err := client.Delete(deleteURL(client, id), nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get implements image get request.
|
||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
resp, err := client.Get(getURL(client, id), &r.Body, nil)
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update implements image updated request.
|
||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||
b, err := opts.ToImageUpdateMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return r
|
||||
}
|
||||
resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
MoreHeaders: map[string]string{"Content-Type": "application/openstack-images-v2.1-json-patch"},
|
||||
})
|
||||
_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||
// Update request.
|
||||
type UpdateOptsBuilder interface {
|
||||
// returns value implementing json.Marshaler which when marshaled matches
|
||||
// the patch schema:
|
||||
// http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html
|
||||
ToImageUpdateMap() ([]interface{}, error)
|
||||
}
|
||||
|
||||
// UpdateOpts implements UpdateOpts
|
||||
type UpdateOpts []Patch
|
||||
|
||||
// ToImageUpdateMap assembles a request body based on the contents of
|
||||
// UpdateOpts.
|
||||
func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) {
|
||||
m := make([]interface{}, len(opts))
|
||||
for i, patch := range opts {
|
||||
patchJSON := patch.ToImagePatchMap()
|
||||
m[i] = patchJSON
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Patch represents a single update to an existing image. Multiple updates
|
||||
// to an image can be submitted at the same time.
|
||||
type Patch interface {
|
||||
ToImagePatchMap() map[string]interface{}
|
||||
}
|
||||
|
||||
// UpdateVisibility represents an updated visibility property request.
|
||||
type UpdateVisibility struct {
|
||||
Visibility ImageVisibility
|
||||
}
|
||||
|
||||
// ToImagePatchMap assembles a request body based on UpdateVisibility.
|
||||
func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": "replace",
|
||||
"path": "/visibility",
|
||||
"value": r.Visibility,
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceImageName represents an updated image_name property request.
|
||||
type ReplaceImageName struct {
|
||||
NewName string
|
||||
}
|
||||
|
||||
// ToImagePatchMap assembles a request body based on ReplaceImageName.
|
||||
func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": "replace",
|
||||
"path": "/name",
|
||||
"value": r.NewName,
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceImageChecksum represents an updated checksum property request.
|
||||
type ReplaceImageChecksum struct {
|
||||
Checksum string
|
||||
}
|
||||
|
||||
// ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum.
|
||||
func (r ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": "replace",
|
||||
"path": "/checksum",
|
||||
"value": r.Checksum,
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceImageTags represents an updated tags property request.
|
||||
type ReplaceImageTags struct {
|
||||
NewTags []string
|
||||
}
|
||||
|
||||
// ToImagePatchMap assembles a request body based on ReplaceImageTags.
|
||||
func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": "replace",
|
||||
"path": "/tags",
|
||||
"value": r.NewTags,
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceImageMinDisk represents an updated min_disk property request.
|
||||
type ReplaceImageMinDisk struct {
|
||||
NewMinDisk int
|
||||
}
|
||||
|
||||
// ToImagePatchMap assembles a request body based on ReplaceImageTags.
|
||||
func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": "replace",
|
||||
"path": "/min_disk",
|
||||
"value": r.NewMinDisk,
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceImageMinRam represents an updated min_ram property request.
|
||||
type ReplaceImageMinRam struct {
|
||||
NewMinRam int
|
||||
}
|
||||
|
||||
// ToImagePatchMap assembles a request body based on ReplaceImageTags.
|
||||
func (r ReplaceImageMinRam) ToImagePatchMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"op": "replace",
|
||||
"path": "/min_ram",
|
||||
"value": r.NewMinRam,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateOp represents a valid update operation.
|
||||
type UpdateOp string
|
||||
|
||||
const (
|
||||
AddOp UpdateOp = "add"
|
||||
ReplaceOp UpdateOp = "replace"
|
||||
RemoveOp UpdateOp = "remove"
|
||||
)
|
||||
|
||||
// UpdateImageProperty represents an update property request.
|
||||
type UpdateImageProperty struct {
|
||||
Op UpdateOp
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// ToImagePatchMap assembles a request body based on UpdateImageProperty.
|
||||
func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} {
|
||||
updateMap := map[string]interface{}{
|
||||
"op": r.Op,
|
||||
"path": fmt.Sprintf("/%s", r.Name),
|
||||
}
|
||||
|
||||
if r.Op != RemoveOp {
|
||||
updateMap["value"] = r.Value
|
||||
}
|
||||
|
||||
return updateMap
|
||||
}
|
||||
240
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go
generated
vendored
Normal file
240
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go
generated
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/internal"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// Image represents an image found in the OpenStack Image service.
|
||||
type Image struct {
|
||||
// ID is the image UUID.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Name is the human-readable display name for the image.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Status is the image status. It can be "queued" or "active"
|
||||
// See imageservice/v2/images/type.go
|
||||
Status ImageStatus `json:"status"`
|
||||
|
||||
// Tags is a list of image tags. Tags are arbitrarily defined strings
|
||||
// attached to an image.
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
// ContainerFormat is the format of the container.
|
||||
// Valid values are ami, ari, aki, bare, and ovf.
|
||||
ContainerFormat string `json:"container_format"`
|
||||
|
||||
// DiskFormat is the format of the disk.
|
||||
// If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi,
|
||||
// and iso.
|
||||
DiskFormat string `json:"disk_format"`
|
||||
|
||||
// MinDiskGigabytes is the amount of disk space in GB that is required to
|
||||
// boot the image.
|
||||
MinDiskGigabytes int `json:"min_disk"`
|
||||
|
||||
// MinRAMMegabytes [optional] is the amount of RAM in MB that is required to
|
||||
// boot the image.
|
||||
MinRAMMegabytes int `json:"min_ram"`
|
||||
|
||||
// Owner is the tenant ID the image belongs to.
|
||||
Owner string `json:"owner"`
|
||||
|
||||
// Protected is whether the image is deletable or not.
|
||||
Protected bool `json:"protected"`
|
||||
|
||||
// Visibility defines who can see/use the image.
|
||||
Visibility ImageVisibility `json:"visibility"`
|
||||
|
||||
// Checksum is the checksum of the data that's associated with the image.
|
||||
Checksum string `json:"checksum"`
|
||||
|
||||
// SizeBytes is the size of the data that's associated with the image.
|
||||
SizeBytes int64 `json:"-"`
|
||||
|
||||
// Metadata is a set of metadata associated with the image.
|
||||
// Image metadata allow for meaningfully define the image properties
|
||||
// and tags.
|
||||
// See http://docs.openstack.org/developer/glance/metadefs-concepts.html.
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
|
||||
// Properties is a set of key-value pairs, if any, that are associated with
|
||||
// the image.
|
||||
Properties map[string]interface{}
|
||||
|
||||
// CreatedAt is the date when the image has been created.
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// UpdatedAt is the date when the last change has been made to the image or
|
||||
// it's properties.
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// File is the trailing path after the glance endpoint that represent the
|
||||
// location of the image or the path to retrieve it.
|
||||
File string `json:"file"`
|
||||
|
||||
// Schema is the path to the JSON-schema that represent the image or image
|
||||
// entity.
|
||||
Schema string `json:"schema"`
|
||||
|
||||
// VirtualSize is the virtual size of the image
|
||||
VirtualSize int64 `json:"virtual_size"`
|
||||
|
||||
// OpenStackImageImportMethods is a slice listing the types of import
|
||||
// methods available in the cloud.
|
||||
OpenStackImageImportMethods []string `json:"-"`
|
||||
// OpenStackImageStoreIDs is a slice listing the store IDs available in
|
||||
// the cloud.
|
||||
OpenStackImageStoreIDs []string `json:"-"`
|
||||
}
|
||||
|
||||
func (r *Image) UnmarshalJSON(b []byte) error {
|
||||
type tmp Image
|
||||
var s struct {
|
||||
tmp
|
||||
SizeBytes interface{} `json:"size"`
|
||||
OpenStackImageImportMethods string `json:"openstack-image-import-methods"`
|
||||
OpenStackImageStoreIDs string `json:"openstack-image-store-ids"`
|
||||
}
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = Image(s.tmp)
|
||||
|
||||
switch t := s.SizeBytes.(type) {
|
||||
case nil:
|
||||
r.SizeBytes = 0
|
||||
case float32:
|
||||
r.SizeBytes = int64(t)
|
||||
case float64:
|
||||
r.SizeBytes = int64(t)
|
||||
default:
|
||||
return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t)
|
||||
}
|
||||
|
||||
// Bundle all other fields into Properties
|
||||
var result interface{}
|
||||
err = json.Unmarshal(b, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resultMap, ok := result.(map[string]interface{}); ok {
|
||||
delete(resultMap, "self")
|
||||
delete(resultMap, "size")
|
||||
delete(resultMap, "openstack-image-import-methods")
|
||||
delete(resultMap, "openstack-image-store-ids")
|
||||
r.Properties = internal.RemainingKeys(Image{}, resultMap)
|
||||
}
|
||||
|
||||
if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 {
|
||||
r.OpenStackImageImportMethods = v
|
||||
}
|
||||
if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageStoreIDs), splitFunc); len(v) > 0 {
|
||||
r.OpenStackImageStoreIDs = v
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
gophercloud.Result
|
||||
}
|
||||
|
||||
// Extract interprets any commonResult as an Image.
|
||||
func (r commonResult) Extract() (*Image, error) {
|
||||
var s *Image
|
||||
if v, ok := r.Body.(map[string]interface{}); ok {
|
||||
for k, h := range r.Header {
|
||||
if strings.ToLower(k) == "openstack-image-import-methods" {
|
||||
for _, s := range h {
|
||||
v["openstack-image-import-methods"] = s
|
||||
}
|
||||
}
|
||||
if strings.ToLower(k) == "openstack-image-store-ids" {
|
||||
for _, s := range h {
|
||||
v["openstack-image-store-ids"] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s, err
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a Create operation. Call its Extract
|
||||
// method to interpret it as an Image.
|
||||
type CreateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// UpdateResult represents the result of an Update operation. Call its Extract
|
||||
// method to interpret it as an Image.
|
||||
type UpdateResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// GetResult represents the result of a Get operation. Call its Extract
|
||||
// method to interpret it as an Image.
|
||||
type GetResult struct {
|
||||
commonResult
|
||||
}
|
||||
|
||||
// DeleteResult represents the result of a Delete operation. Call its
|
||||
// ExtractErr method to interpret it as an Image.
|
||||
type DeleteResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// ImagePage represents the results of a List request.
|
||||
type ImagePage struct {
|
||||
serviceURL string
|
||||
pagination.LinkedPageBase
|
||||
}
|
||||
|
||||
// IsEmpty returns true if an ImagePage contains no Images results.
|
||||
func (r ImagePage) IsEmpty() (bool, error) {
|
||||
images, err := ExtractImages(r)
|
||||
return len(images) == 0, err
|
||||
}
|
||||
|
||||
// NextPageURL uses the response's embedded link reference to navigate to
|
||||
// the next page of results.
|
||||
func (r ImagePage) NextPageURL() (string, error) {
|
||||
var s struct {
|
||||
Next string `json:"next"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if s.Next == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return nextPageURL(r.serviceURL, s.Next)
|
||||
}
|
||||
|
||||
// ExtractImages interprets the results of a single page from a List() call,
|
||||
// producing a slice of Image entities.
|
||||
func ExtractImages(r pagination.Page) ([]Image, error) {
|
||||
var s struct {
|
||||
Images []Image `json:"images"`
|
||||
}
|
||||
err := (r.(ImagePage)).ExtractInto(&s)
|
||||
return s.Images, err
|
||||
}
|
||||
|
||||
// splitFunc is a helper function used to avoid a slice of empty strings.
|
||||
func splitFunc(c rune) bool {
|
||||
return c == ','
|
||||
}
|
||||
108
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go
generated
vendored
Normal file
108
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ImageStatus image statuses
|
||||
// http://docs.openstack.org/developer/glance/statuses.html
|
||||
type ImageStatus string
|
||||
|
||||
const (
|
||||
// ImageStatusQueued is a status for an image which identifier has
|
||||
// been reserved for an image in the image registry.
|
||||
ImageStatusQueued ImageStatus = "queued"
|
||||
|
||||
// ImageStatusSaving denotes that an image’s raw data is currently being
|
||||
// uploaded to Glance
|
||||
ImageStatusSaving ImageStatus = "saving"
|
||||
|
||||
// ImageStatusActive denotes an image that is fully available in Glance.
|
||||
ImageStatusActive ImageStatus = "active"
|
||||
|
||||
// ImageStatusKilled denotes that an error occurred during the uploading
|
||||
// of an image’s data, and that the image is not readable.
|
||||
ImageStatusKilled ImageStatus = "killed"
|
||||
|
||||
// ImageStatusDeleted is used for an image that is no longer available to use.
|
||||
// The image information is retained in the image registry.
|
||||
ImageStatusDeleted ImageStatus = "deleted"
|
||||
|
||||
// ImageStatusPendingDelete is similar to Delete, but the image is not yet
|
||||
// deleted.
|
||||
ImageStatusPendingDelete ImageStatus = "pending_delete"
|
||||
|
||||
// ImageStatusDeactivated denotes that access to image data is not allowed to
|
||||
// any non-admin user.
|
||||
ImageStatusDeactivated ImageStatus = "deactivated"
|
||||
|
||||
// ImageStatusImporting denotes that an import call has been made but that
|
||||
// the image is not yet ready for use.
|
||||
ImageStatusImporting ImageStatus = "importing"
|
||||
)
|
||||
|
||||
// ImageVisibility denotes an image that is fully available in Glance.
|
||||
// This occurs when the image data is uploaded, or the image size is explicitly
|
||||
// set to zero on creation.
|
||||
// According to design
|
||||
// https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design
|
||||
type ImageVisibility string
|
||||
|
||||
const (
|
||||
// ImageVisibilityPublic all users
|
||||
ImageVisibilityPublic ImageVisibility = "public"
|
||||
|
||||
// ImageVisibilityPrivate users with tenantId == tenantId(owner)
|
||||
ImageVisibilityPrivate ImageVisibility = "private"
|
||||
|
||||
// ImageVisibilityShared images are visible to:
|
||||
// - users with tenantId == tenantId(owner)
|
||||
// - users with tenantId in the member-list of the image
|
||||
// - users with tenantId in the member-list with member_status == 'accepted'
|
||||
ImageVisibilityShared ImageVisibility = "shared"
|
||||
|
||||
// ImageVisibilityCommunity images:
|
||||
// - all users can see and boot it
|
||||
// - users with tenantId in the member-list of the image with
|
||||
// member_status == 'accepted' have this image in their default image-list.
|
||||
ImageVisibilityCommunity ImageVisibility = "community"
|
||||
)
|
||||
|
||||
// MemberStatus is a status for adding a new member (tenant) to an image
|
||||
// member list.
|
||||
type ImageMemberStatus string
|
||||
|
||||
const (
|
||||
// ImageMemberStatusAccepted is the status for an accepted image member.
|
||||
ImageMemberStatusAccepted ImageMemberStatus = "accepted"
|
||||
|
||||
// ImageMemberStatusPending shows that the member addition is pending
|
||||
ImageMemberStatusPending ImageMemberStatus = "pending"
|
||||
|
||||
// ImageMemberStatusAccepted is the status for a rejected image member
|
||||
ImageMemberStatusRejected ImageMemberStatus = "rejected"
|
||||
|
||||
// ImageMemberStatusAll
|
||||
ImageMemberStatusAll ImageMemberStatus = "all"
|
||||
)
|
||||
|
||||
// ImageDateFilter represents a valid filter to use for filtering
|
||||
// images by their date during a List.
|
||||
type ImageDateFilter string
|
||||
|
||||
const (
|
||||
FilterGT ImageDateFilter = "gt"
|
||||
FilterGTE ImageDateFilter = "gte"
|
||||
FilterLT ImageDateFilter = "lt"
|
||||
FilterLTE ImageDateFilter = "lte"
|
||||
FilterNEQ ImageDateFilter = "neq"
|
||||
FilterEQ ImageDateFilter = "eq"
|
||||
)
|
||||
|
||||
// ImageDateQuery represents a date field to be used for listing images.
|
||||
// If no filter is specified, the query will act as though FilterEQ was
|
||||
// set.
|
||||
type ImageDateQuery struct {
|
||||
Date time.Time
|
||||
Filter ImageDateFilter
|
||||
}
|
||||
65
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go
generated
vendored
Normal file
65
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||
)
|
||||
|
||||
// `listURL` is a pure function. `listURL(c)` is a URL for which a GET
|
||||
// request will respond with a list of images in the service `c`.
|
||||
func listURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("images")
|
||||
}
|
||||
|
||||
func createURL(c *gophercloud.ServiceClient) string {
|
||||
return c.ServiceURL("images")
|
||||
}
|
||||
|
||||
// `imageURL(c,i)` is the URL for the image identified by ID `i` in
|
||||
// the service `c`.
|
||||
func imageURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return c.ServiceURL("images", imageID)
|
||||
}
|
||||
|
||||
// `getURL(c,i)` is a URL for which a GET request will respond with
|
||||
// information about the image identified by ID `i` in the service
|
||||
// `c`.
|
||||
func getURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return imageURL(c, imageID)
|
||||
}
|
||||
|
||||
func updateURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return imageURL(c, imageID)
|
||||
}
|
||||
|
||||
func deleteURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||
return imageURL(c, imageID)
|
||||
}
|
||||
|
||||
// builds next page full url based on current url
|
||||
func nextPageURL(serviceURL, requestedNext string) (string, error) {
|
||||
base, err := utils.BaseEndpoint(serviceURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
requestedNextURL, err := url.Parse(requestedNext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
base = gophercloud.NormalizeURL(base)
|
||||
nextPath := base + strings.TrimPrefix(requestedNextURL.Path, "/")
|
||||
|
||||
nextURL, err := url.Parse(nextPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nextURL.RawQuery = requestedNextURL.RawQuery
|
||||
|
||||
return nextURL.String(), nil
|
||||
}
|
||||
28
vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go
generated
vendored
Normal file
28
vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BaseEndpoint will return a URL without the /vX.Y
|
||||
// portion of the URL.
|
||||
func BaseEndpoint(endpoint string) (string, error) {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.RawQuery, u.Fragment = "", ""
|
||||
|
||||
path := u.Path
|
||||
versionRe := regexp.MustCompile("v[0-9.]+/?")
|
||||
|
||||
if version := versionRe.FindString(path); version != "" {
|
||||
versionIndex := strings.Index(path, version)
|
||||
u.Path = path[:versionIndex]
|
||||
}
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
111
vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go
generated
vendored
Normal file
111
vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go
generated
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
)
|
||||
|
||||
// Version is a supported API version, corresponding to a vN package within the appropriate service.
|
||||
type Version struct {
|
||||
ID string
|
||||
Suffix string
|
||||
Priority int
|
||||
}
|
||||
|
||||
var goodStatus = map[string]bool{
|
||||
"current": true,
|
||||
"supported": true,
|
||||
"stable": true,
|
||||
}
|
||||
|
||||
// ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's
|
||||
// published versions.
|
||||
// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint.
|
||||
func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) {
|
||||
type linkResp struct {
|
||||
Href string `json:"href"`
|
||||
Rel string `json:"rel"`
|
||||
}
|
||||
|
||||
type valueResp struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Links []linkResp `json:"links"`
|
||||
}
|
||||
|
||||
type versionsResp struct {
|
||||
Values []valueResp `json:"values"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Versions versionsResp `json:"versions"`
|
||||
}
|
||||
|
||||
normalize := func(endpoint string) string {
|
||||
if !strings.HasSuffix(endpoint, "/") {
|
||||
return endpoint + "/"
|
||||
}
|
||||
return endpoint
|
||||
}
|
||||
identityEndpoint := normalize(client.IdentityEndpoint)
|
||||
|
||||
// If a full endpoint is specified, check version suffixes for a match first.
|
||||
for _, v := range recognized {
|
||||
if strings.HasSuffix(identityEndpoint, v.Suffix) {
|
||||
return v, identityEndpoint, nil
|
||||
}
|
||||
}
|
||||
|
||||
var resp response
|
||||
_, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{
|
||||
JSONResponse: &resp,
|
||||
OkCodes: []int{200, 300},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var highest *Version
|
||||
var endpoint string
|
||||
|
||||
for _, value := range resp.Versions.Values {
|
||||
href := ""
|
||||
for _, link := range value.Links {
|
||||
if link.Rel == "self" {
|
||||
href = normalize(link.Href)
|
||||
}
|
||||
}
|
||||
|
||||
for _, version := range recognized {
|
||||
if strings.Contains(value.ID, version.ID) {
|
||||
// Prefer a version that exactly matches the provided endpoint.
|
||||
if href == identityEndpoint {
|
||||
if href == "" {
|
||||
return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase)
|
||||
}
|
||||
return version, href, nil
|
||||
}
|
||||
|
||||
// Otherwise, find the highest-priority version with a whitelisted status.
|
||||
if goodStatus[strings.ToLower(value.Status)] {
|
||||
if highest == nil || version.Priority > highest.Priority {
|
||||
highest = version
|
||||
endpoint = href
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if highest == nil {
|
||||
return nil, "", fmt.Errorf("No supported version available from endpoint %s", client.IdentityBase)
|
||||
}
|
||||
if endpoint == "" {
|
||||
return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, client.IdentityBase)
|
||||
}
|
||||
|
||||
return highest, endpoint, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue