Merge pull request #51795 from dims/bug-fix-51755

Automatic merge from submit-queue (batch tested with PRs 51984, 51351, 51873, 51795, 51634)

Bug Fix - Adding an allowed address pair wipes port security groups

**What this PR does / why we need it**:

Fix for cloud routes enabled instances will have their security groups
removed when the allowed address pair is added to the instance's port.

Upstream bug report is in:
https://github.com/gophercloud/gophercloud/issues/509

Upstream bug fix is in:
https://github.com/gophercloud/gophercloud/pull/510

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

Fixes #51755

**Special notes for your reviewer**:

Just an fix in vendored code. minimal changes needed in OpenStack cloud provider

**Release note**:

```release-note
NONE
```

Kubernetes-commit: 9a8cb435b77085fa7d518c4428a02eae316b1003
This commit is contained in:
Kubernetes Publisher
2017-09-05 18:40:31 -07:00
152 changed files with 5362 additions and 20200 deletions

View File

@@ -9,12 +9,32 @@ ProviderClient representing an active session on that provider.
Its fields are the union of those recognized by each identity implementation and
provider.
An example of manually providing authentication information:
opts := gophercloud.AuthOptions{
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
Username: "{username}",
Password: "{password}",
TenantID: "{tenant_id}",
}
provider, err := openstack.AuthenticatedClient(opts)
An example of using AuthOptionsFromEnv(), where the environment variables can
be read from a file, such as a standard openrc file:
opts, err := openstack.AuthOptionsFromEnv()
provider, err := openstack.AuthenticatedClient(opts)
*/
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.
//
// The IdentityEndpoint is typically referred to as the "auth_url" or
// "OS_AUTH_URL" in the information provided by the cloud operator.
IdentityEndpoint string `json:"-"`
// Username is required if using Identity V2 API. Consult with your provider's
@@ -39,7 +59,7 @@ type AuthOptions struct {
// If DomainID or DomainName are provided, they will also apply to TenantName.
// It is not currently possible to authenticate with Username and a Domain
// and scope to a Project in a different Domain by using TenantName. To
// accomplish that, the ProjectID will need to be provided to the TenantID
// accomplish that, the ProjectID will need to be provided as the TenantID
// option.
TenantID string `json:"tenantId,omitempty"`
TenantName string `json:"tenantName,omitempty"`
@@ -50,10 +70,12 @@ type AuthOptions struct {
// false, it will not cache these settings, but re-authentication will not be
// possible. This setting defaults to false.
//
// NOTE: The reauth function will try to re-authenticate endlessly if left unchecked.
// The way to limit the number of attempts is to provide a custom HTTP client to the provider client
// and provide a transport that implements the RoundTripper interface and stores the number of failed retries.
// For an example of this, see here: https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
// NOTE: The reauth function will try to re-authenticate endlessly if left
// unchecked. The way to limit the number of attempts is to provide a custom
// HTTP client to the provider client and provide a transport that implements
// the RoundTripper interface and stores the number of failed retries. For an
// example of this, see here:
// https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
AllowReauth bool `json:"-"`
// TokenID allows users to authenticate (possibly as another user) with an
@@ -316,7 +338,12 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
},
}, nil
} else if scope.DomainName != "" {
return nil, ErrScopeDomainName{}
// DomainName
return map[string]interface{}{
"domain": map[string]interface{}{
"name": &scope.DomainName,
},
}, nil
}
return nil, nil

View File

@@ -3,11 +3,17 @@ Package gophercloud provides a multi-vendor interface to OpenStack-compatible
clouds. The library has a three-level hierarchy: providers, services, and
resources.
Provider structs represent the service providers that offer and manage a
collection of services. The IdentityEndpoint is typically refered to as
"auth_url" in information provided by the cloud operator. Additionally,
the cloud may refer to TenantID or TenantName as project_id and project_name.
These are defined like so:
Authenticating with Providers
Provider structs represent the cloud providers that offer and manage a
collection of services. You will generally want to create one Provider
client per OpenStack cloud.
Use your OpenStack credentials to create a Provider client. The
IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in
information provided by the cloud operator. Additionally, the cloud may refer to
TenantID or TenantName as project_id and project_name. Credentials are
specified like so:
opts := gophercloud.AuthOptions{
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
@@ -18,6 +24,16 @@ These are defined like so:
provider, err := openstack.AuthenticatedClient(opts)
You may also use the openstack.AuthOptionsFromEnv() helper function. This
function reads in standard environment variables frequently found in an
OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant"
instead of "project".
opts, err := openstack.AuthOptionsFromEnv()
provider, err := openstack.AuthenticatedClient(opts)
Service Clients
Service structs are specific to a provider and handle all of the logic and
operations for a particular OpenStack service. Examples of services include:
Compute, Object Storage, Block Storage. In order to define one, you need to
@@ -27,6 +43,8 @@ pass in the parent provider, like so:
client := openstack.NewComputeV2(provider, opts)
Resources
Resource structs are the domain models that services make use of in order
to work with and represent the state of API resources:
@@ -62,6 +80,12 @@ of results:
return true, nil
})
If you want to obtain the entire collection of pages without doing any
intermediary processing on each page, you can use the AllPages method:
allPages, err := servers.List(client, nil).AllPages()
allServers, err := servers.ExtractServers(allPages)
This top-level package contains utility functions and data types that are used
throughout the provider and service packages. Of particular note for end users
are the AuthOptions and EndpointOpts structs.

View File

@@ -27,7 +27,7 @@ const (
// unambiguously identify one, and only one, endpoint within the catalog.
//
// Usually, these are passed to service client factory functions in a provider
// package, like "rackspace.NewComputeV2()".
// package, like "openstack.NewComputeV2()".
type EndpointOpts struct {
// Type [required] is the service type for the client (e.g., "compute",
// "object-store"). Generally, this will be supplied by the service client

View File

@@ -393,13 +393,6 @@ func (e ErrScopeProjectIDAlone) Error() string {
return "ProjectID must be supplied alone in a Scope"
}
// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope.
type ErrScopeDomainName struct{ BaseError }
func (e ErrScopeDomainName) Error() string {
return "DomainName must be supplied with a ProjectName or ProjectID in a Scope"
}
// ErrScopeEmpty indicates that no credentials were provided in a Scope.
type ErrScopeEmpty struct{ BaseError }

View File

@@ -8,10 +8,22 @@ import (
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, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must
// have settings, or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional.
/*
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, OS_TENANT_ID, and OS_TENANT_NAME.
Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings,
or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional.
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")

View File

@@ -16,10 +16,20 @@ const (
v30 = "v3.0"
)
// NewClient prepares an unauthenticated ProviderClient instance.
// Most users will probably prefer using the AuthenticatedClient function instead.
// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
// for example.
/*
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) {
u, err := url.Parse(endpoint)
if err != nil {
@@ -45,10 +55,26 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
}, nil
}
// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
// returns a Client instance that's ready to operate.
// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
// the most recent identity service available to proceed.
/*
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(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
*/
func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
client, err := NewClient(options.IdentityEndpoint)
if err != nil {
@@ -62,7 +88,8 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider
return client, nil
}
// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
// Authenticate or re-authenticate against the most recent identity service
// supported at the provided endpoint.
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
versions := []*utils.Version{
{ID: v20, Priority: 20, Suffix: "/v2.0/"},
@@ -179,7 +206,8 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
return nil
}
// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
// 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"
@@ -199,7 +227,8 @@ func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp
}, nil
}
// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
// 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"
@@ -232,34 +261,40 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO
return sc, nil
}
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
// 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.
// 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.
// 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.
// 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.
// 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")
}
// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
// 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")
}
@@ -270,7 +305,8 @@ func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (
return initClientOpts(client, eo, "cdn")
}
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
// 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")
}
@@ -280,14 +316,16 @@ func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*
return initClientOpts(client, eo, "database")
}
// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS service.
// 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.
// 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/"

View 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(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
*/
package openstack

View File

@@ -6,12 +6,16 @@ import (
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.
/*
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)
@@ -54,12 +58,16 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt
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.
/*
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.

View File

@@ -1,7 +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.
/*
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

View File

@@ -9,6 +9,7 @@ import (
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"`
}
@@ -32,18 +33,22 @@ func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
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 describes struct types that can be accepted by the Create call.
// 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.
// 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")
}
@@ -67,17 +72,21 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
return
}
// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
// 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 server.
// 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"`
}
@@ -100,7 +109,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
return
}
// Delete is the operation responsible for permanently deleting an API tenant.
// Delete is the operation responsible for permanently deleting a tenant.
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
_, r.Err = client.Delete(deleteURL(client, id), nil)
return

View File

@@ -43,7 +43,8 @@ func (r TenantPage) NextPageURL() (string, error) {
return gophercloud.ExtractNextURL(s.Links)
}
// ExtractTenants returns a slice of Tenants contained in a single page of results.
// 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"`
@@ -56,7 +57,7 @@ type tenantResult struct {
gophercloud.Result
}
// Extract interprets any tenantResults as a tenant.
// Extract interprets any tenantResults as a Tenant.
func (r tenantResult) Extract() (*Tenant, error) {
var s struct {
Tenant *Tenant `json:"tenant"`
@@ -65,22 +66,26 @@ func (r tenantResult) Extract() (*Tenant, error) {
return s.Tenant, err
}
// GetResult temporarily contains the response from the Get call.
// GetResult is the response from a Get request. Call its Extract method to
// interpret it as a Tenant.
type GetResult struct {
tenantResult
}
// CreateResult temporarily contains the reponse from the Create call.
// CreateResult is the response from a Create request. Call its Extract method
// to interpret it as a Tenant.
type CreateResult struct {
tenantResult
}
// DeleteResult temporarily contains the response from the Delete call.
// 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 temporarily contains the response from the Update call.
// UpdateResult is the response from a Update request. Call its Extract method
// to interpret it as a Tenant.
type UpdateResult struct {
tenantResult
}

View File

@@ -1,5 +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
/*
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

View File

@@ -2,17 +2,21 @@ 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.
// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the
// AuthOptionsBuilder interface.
type AuthOptionsV2 struct {
PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
@@ -23,15 +27,16 @@ type AuthOptionsV2 struct {
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 allows users to authenticate (possibly as another user)
// with an authentication token ID.
TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"`
}
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
// 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.
// ToTokenCreateMap assembles the Create request body, returning an error
// if parameters are missing or inconsistent.
ToTokenV2CreateMap() (map[string]interface{}, error)
}
@@ -47,8 +52,7 @@ type AuthOptions struct {
TokenID string
}
// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
// interface in the v2 tokens package
// ToTokenV2CreateMap builds a token request body from the given AuthOptions.
func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
v2Opts := AuthOptionsV2{
TenantID: opts.TenantID,
@@ -74,9 +78,9 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
}
// Create authenticates to the identity service and attempts to acquire a Token.
// If successful, the CreateResult
// 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.
// 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 {

View File

@@ -7,20 +7,24 @@ import (
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
)
// Token provides only the most basic information related to an authentication token.
// 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.
// 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.
// 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 provides information about the tenant to which this token grants
// access.
Tenant tenants.Tenant
}
@@ -38,13 +42,17 @@ type User struct {
}
// 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.
// 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 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 ("").
// 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"`
@@ -56,38 +64,44 @@ type Endpoint struct {
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.
// CatalogEntry provides a type-safe interface to an Identity API V2 service
// catalog listing.
//
// 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.
// 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 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 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.
// ServiceCatalog provides a view into the service catalog from a previous,
// successful authentication.
type ServiceCatalog struct {
Entries []CatalogEntry
}
// CreateResult defers the interpretation of a created token.
// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog.
// 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.
// 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
}
@@ -121,7 +135,8 @@ func (r CreateResult) ExtractToken() (*Token, error) {
}, nil
}
// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
// with the user's Token.
func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
var s struct {
Access struct {

View File

@@ -1,6 +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
/*
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

View File

@@ -10,20 +10,22 @@ type Scope struct {
DomainName string
}
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
// 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 assembles the Create request body, returning an error
// if parameters are missing or inconsistent.
ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, 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.
// 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
@@ -39,11 +41,11 @@ type AuthOptions struct {
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 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
@@ -53,6 +55,7 @@ type AuthOptions struct {
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,
@@ -67,6 +70,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
return gophercloudAuthOpts.ToTokenV3CreateMap(scope)
}
// ToTokenV3CreateMap builds a scope request body from AuthOptions.
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
if opts.Scope.ProjectName != "" {
// ProjectName provided: either DomainID or DomainName must also be supplied.
@@ -125,7 +129,12 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
},
}, nil
} else if opts.Scope.DomainName != "" {
return nil, gophercloud.ErrScopeDomainName{}
// DomainName
return map[string]interface{}{
"domain": map[string]interface{}{
"name": &opts.Scope.DomainName,
},
}, nil
}
return nil, nil
@@ -141,7 +150,8 @@ func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[
}
}
// Create authenticates and either generates a new token, or changes the Scope of an existing token.
// 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 {

View File

@@ -17,37 +17,45 @@ type Endpoint struct {
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).
// 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.
// 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 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 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.
// 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.
// 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 on the API.
// User represents a user resource that exists in the Identity Service.
type User struct {
Domain Domain `json:"domain"`
ID string `json:"id"`
@@ -67,7 +75,8 @@ type Project struct {
Name string `json:"name"`
}
// commonResult is the deferred result of a Create or a Get call.
// 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
}
@@ -92,7 +101,8 @@ func (r commonResult) ExtractToken() (*Token, error) {
return &s, err
}
// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
// 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)
@@ -126,27 +136,31 @@ func (r commonResult) ExtractProject() (*Project, error) {
return s.Project, err
}
// CreateResult defers the interpretation of a created token.
// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog.
// 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 deferred response from a Get call.
// 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 the deferred response from a Revoke call.
// 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.
// 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"`
}

View File

@@ -10,10 +10,28 @@ import (
"time"
)
// BuildRequestBody builds a map[string]interface from the given `struct`. If
// parent is not the empty string, the final map[string]interface returned will
// encapsulate the built one
//
/*
BuildRequestBody builds a map[string]interface from the given `struct`. If
parent is not an empty string, the final map[string]interface returned will
encapsulate the built one. For example:
disk := 1
createOpts := flavors.CreateOpts{
ID: "1",
Name: "m1.tiny",
Disk: &disk,
RAM: 512,
VCPUs: 1,
RxTxFactor: 1.0,
}
body, err := gophercloud.BuildRequestBody(createOpts, "flavor")
The above example can be run as-is, however it is recommended to look at how
BuildRequestBody is used within Gophercloud to more fully understand how it
fits within the request process as a whole rather than use it directly as shown
above.
*/
func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) {
optsValue := reflect.ValueOf(opts)
if optsValue.Kind() == reflect.Ptr {

View File

@@ -224,9 +224,8 @@ type HeaderResult struct {
Result
}
// ExtractHeader will return the http.Header and error from the HeaderResult.
//
// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader()
// ExtractInto allows users to provide an object into which `Extract` will
// extract the http.Header headers of the result.
func (r HeaderResult) ExtractInto(to interface{}) error {
if r.Err != nil {
return r.Err