fix: set openstack image metadata with use_blockstorage_volume
Signed-off-by: Pratyush singhal <psinghal20@gmail.com>
This commit is contained in:
parent
049811d329
commit
cf8bfa56f0
|
@ -64,6 +64,15 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
|
|||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
err = volumeactions.SetImageMetadata(blockStorageClient, volume, volumeactions.ImageMetadataOpts{
|
||||
Metadata: config.ImageMetadata,
|
||||
}).ExtractErr()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error setting image metadata: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
imageId = image.ImageID
|
||||
} else {
|
||||
imageId, err = servers.CreateImage(computeClient, server.ID, servers.CreateImageOpts{
|
||||
|
|
3
go.mod
3
go.mod
|
@ -36,7 +36,7 @@ require (
|
|||
github.com/google/go-cmp v0.2.0
|
||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
|
||||
github.com/google/uuid v1.0.0
|
||||
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06
|
||||
github.com/gophercloud/gophercloud v0.2.0
|
||||
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // indirect
|
||||
|
@ -129,5 +129,4 @@ require (
|
|||
gopkg.in/h2non/gock.v1 v1.0.12 // indirect
|
||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -154,6 +154,8 @@ github.com/googleapis/gax-go/v2 v2.0.3 h1:siORttZ36U2R/WjiJuDz8znElWBiAlO9rVt+mq
|
|||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06 h1:m7Rt/8En7PLrM7PQpykdZBPKUdgZWN6MwiA/ChVIoxs=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
||||
github.com/gophercloud/gophercloud v0.2.0 h1:lD2Bce2xBAMNNcFZ0dObTpXkGLlVIb33RPVUNVpw6ic=
|
||||
github.com/gophercloud/gophercloud v0.2.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6 h1:Cw/B8Bu7Rryomxf7bjc8zNfIyLgjxsDd91n0eGRWpuo=
|
||||
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
|
@ -455,6 +457,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d h1:adrbvkTDn9rGnXg2IJDKozEpXXLZN89pdIA+Syt4/u0=
|
||||
|
@ -511,6 +514,7 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
language: go
|
||||
sudo: false
|
||||
install:
|
||||
- go get golang.org/x/crypto/ssh
|
||||
- go get -v -tags 'fixtures acceptance' ./...
|
||||
- go get github.com/wadey/gocovmerge
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/goimports
|
||||
- GO111MODULE=off go get golang.org/x/crypto/ssh
|
||||
- GO111MODULE=off go get -v -tags 'fixtures acceptance' ./...
|
||||
- GO111MODULE=off go get github.com/wadey/gocovmerge
|
||||
- GO111MODULE=off go get github.com/mattn/goveralls
|
||||
- GO111MODULE=off go get golang.org/x/tools/cmd/goimports
|
||||
go:
|
||||
- "1.10"
|
||||
- "1.11"
|
||||
- "1.12"
|
||||
- "tip"
|
||||
env:
|
||||
global:
|
||||
- secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ="
|
||||
- GO111MODULE=on
|
||||
before_script:
|
||||
- go vet ./...
|
||||
script:
|
||||
- ./script/coverage
|
||||
- ./script/unittest
|
||||
- ./script/format
|
||||
after_success:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
|
||||
|
|
|
@ -13,6 +13,31 @@
|
|||
Run gophercloud acceptance test on master branch
|
||||
run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml
|
||||
|
||||
- job:
|
||||
name: gophercloud-acceptance-test-ironic
|
||||
parent: golang-test
|
||||
description: |
|
||||
Run gophercloud ironic acceptance test on master branch
|
||||
run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml
|
||||
|
||||
- job:
|
||||
name: gophercloud-acceptance-test-stein
|
||||
parent: gophercloud-acceptance-test
|
||||
description: |
|
||||
Run gophercloud acceptance test on stein branch
|
||||
vars:
|
||||
global_env:
|
||||
OS_BRANCH: stable/stein
|
||||
|
||||
- job:
|
||||
name: gophercloud-acceptance-test-rocky
|
||||
parent: gophercloud-acceptance-test
|
||||
description: |
|
||||
Run gophercloud acceptance test on rocky branch
|
||||
vars:
|
||||
global_env:
|
||||
OS_BRANCH: stable/rocky
|
||||
|
||||
- job:
|
||||
name: gophercloud-acceptance-test-queens
|
||||
parent: gophercloud-acceptance-test
|
||||
|
@ -65,6 +90,7 @@
|
|||
jobs:
|
||||
- gophercloud-unittest
|
||||
- gophercloud-acceptance-test
|
||||
- gophercloud-acceptance-test-ironic
|
||||
recheck-mitaka:
|
||||
jobs:
|
||||
- gophercloud-acceptance-test-mitaka
|
||||
|
@ -80,7 +106,9 @@
|
|||
recheck-queens:
|
||||
jobs:
|
||||
- gophercloud-acceptance-test-queens
|
||||
periodic:
|
||||
recheck-rocky:
|
||||
jobs:
|
||||
- gophercloud-unittest
|
||||
- gophercloud-acceptance-test
|
||||
- gophercloud-acceptance-test-rocky
|
||||
recheck-stein:
|
||||
jobs:
|
||||
- gophercloud-acceptance-test-stein
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
## 0.3.0 (Unreleaesd)
|
||||
|
||||
## 0.2.0 (June 17, 2019)
|
||||
|
||||
IMPROVEMENTS
|
||||
|
||||
* Added `networking/v2/extensions/qos/rules.ListBandwidthLimitRules` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584)
|
||||
* Added `networking/v2/extensions/qos/rules.GetBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584)
|
||||
* Added `networking/v2/extensions/qos/rules.CreateBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584)
|
||||
* Added `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` [GH-1589](https://github.com/gophercloud/gophercloud/pull/1589)
|
||||
* Added `networking/v2/extensions/qos/rules.DeleteBandwidthLimitRule` [GH-1590](https://github.com/gophercloud/gophercloud/pull/1590)
|
||||
* Added `networking/v2/extensions/qos/policies.List` [GH-1591](https://github.com/gophercloud/gophercloud/pull/1591)
|
||||
* Added `networking/v2/extensions/qos/policies.Get` [GH-1593](https://github.com/gophercloud/gophercloud/pull/1593)
|
||||
* Added `networking/v2/extensions/qos/rules.ListDSCPMarkingRules` [GH-1594](https://github.com/gophercloud/gophercloud/pull/1594)
|
||||
* Added `networking/v2/extensions/qos/policies.Create` [GH-1595](https://github.com/gophercloud/gophercloud/pull/1595)
|
||||
* Added `compute/v2/extensions/diagnostics.Get` [GH-1592](https://github.com/gophercloud/gophercloud/pull/1592)
|
||||
* Added `networking/v2/extensions/qos/policies.Update` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603)
|
||||
* Added `networking/v2/extensions/qos/policies.Delete` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603)
|
||||
* Added `networking/v2/extensions/qos/rules.CreateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605)
|
||||
* Added `networking/v2/extensions/qos/rules.UpdateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605)
|
||||
* Added `networking/v2/extensions/qos/rules.GetDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609)
|
||||
* Added `networking/v2/extensions/qos/rules.DeleteDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609)
|
||||
* Added `networking/v2/extensions/qos/rules.ListMinimumBandwidthRules` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615)
|
||||
* Added `networking/v2/extensions/qos/rules.GetMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615)
|
||||
* Added `networking/v2/extensions/qos/rules.CreateMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615)
|
||||
* Added `Hostname` to `baremetalintrospection/v1/introspection.Data` [GH-1627](https://github.com/gophercloud/gophercloud/pull/1627)
|
||||
* Added `networking/v2/extensions/qos/rules.UpdateMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624)
|
||||
* Added `networking/v2/extensions/qos/rules.DeleteMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624)
|
||||
* Added `networking/v2/extensions/qos/ruletypes.GetRuleType` [GH-1625](https://github.com/gophercloud/gophercloud/pull/1625)
|
||||
* Added `Extra` to `baremetalintrospection/v1/introspection.Data` [GH-1611](https://github.com/gophercloud/gophercloud/pull/1611)
|
||||
* Added `blockstorage/extensions/volumeactions.SetImageMetadata` [GH-1621](https://github.com/gophercloud/gophercloud/pull/1621)
|
||||
|
||||
BUG FIXES
|
||||
|
||||
* Updated `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` to use return code 200 [GH-1606](https://github.com/gophercloud/gophercloud/pull/1606)
|
||||
* Fixed bug in `compute/v2/extensions/schedulerhints.SchedulerHints.Query` where contents will now be marshalled to a string [GH-1620](https://github.com/gophercloud/gophercloud/pull/1620)
|
||||
|
||||
## 0.1.0 (May 27, 2019)
|
||||
|
||||
Initial tagged release.
|
|
@ -185,7 +185,6 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
|||
// Populate the request structure based on the provided arguments. Create and return an error
|
||||
// if insufficient or incompatible information is present.
|
||||
var req request
|
||||
var userRequest userReq
|
||||
|
||||
if opts.Password == "" {
|
||||
if opts.TokenID != "" {
|
||||
|
@ -229,25 +228,44 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
|||
if opts.ApplicationCredentialSecret == "" {
|
||||
return nil, ErrAppCredMissingSecret{}
|
||||
}
|
||||
// make sure that only one of DomainName or DomainID were provided
|
||||
if opts.DomainID == "" && opts.DomainName == "" {
|
||||
return nil, ErrDomainIDOrDomainName{}
|
||||
|
||||
var userRequest *userReq
|
||||
|
||||
if opts.UserID != "" {
|
||||
// UserID could be used without the domain information
|
||||
userRequest = &userReq{
|
||||
ID: &opts.UserID,
|
||||
}
|
||||
}
|
||||
req.Auth.Identity.Methods = []string{"application_credential"}
|
||||
if opts.DomainID != "" {
|
||||
userRequest = userReq{
|
||||
|
||||
if userRequest == nil && opts.Username == "" {
|
||||
// Make sure that Username or UserID are provided
|
||||
return nil, ErrUsernameOrUserID{}
|
||||
}
|
||||
|
||||
if userRequest == nil && opts.DomainID != "" {
|
||||
userRequest = &userReq{
|
||||
Name: &opts.Username,
|
||||
Domain: &domainReq{ID: &opts.DomainID},
|
||||
}
|
||||
} else if opts.DomainName != "" {
|
||||
userRequest = userReq{
|
||||
}
|
||||
|
||||
if userRequest == nil && opts.DomainName != "" {
|
||||
userRequest = &userReq{
|
||||
Name: &opts.Username,
|
||||
Domain: &domainReq{Name: &opts.DomainName},
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that DomainID or DomainName are provided among Username
|
||||
if userRequest == nil {
|
||||
return nil, ErrDomainIDOrDomainName{}
|
||||
}
|
||||
|
||||
req.Auth.Identity.Methods = []string{"application_credential"}
|
||||
req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{
|
||||
Name: &opts.ApplicationCredentialName,
|
||||
User: &userRequest,
|
||||
User: userRequest,
|
||||
Secret: &opts.ApplicationCredentialSecret,
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package gophercloud
|
||||
|
||||
/*
|
||||
AuthResult is the result from the request that was used to obtain a provider
|
||||
client's Keystone token. It is returned from ProviderClient.GetAuthResult().
|
||||
|
||||
The following types satisfy this interface:
|
||||
|
||||
github.com/gophercloud/gophercloud/openstack/identity/v2/tokens.CreateResult
|
||||
github.com/gophercloud/gophercloud/openstack/identity/v3/tokens.CreateResult
|
||||
|
||||
Usage example:
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud"
|
||||
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||
)
|
||||
|
||||
func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) {
|
||||
r := providerClient.GetAuthResult()
|
||||
if r == nil {
|
||||
//ProviderClient did not use openstack.Authenticate(), e.g. because token
|
||||
//was set manually with ProviderClient.SetToken()
|
||||
return "", errors.New("no AuthResult available")
|
||||
}
|
||||
switch r := r.(type) {
|
||||
case tokens2.CreateResult:
|
||||
u, err := r.ExtractUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.ID, nil
|
||||
case tokens3.CreateResult:
|
||||
u, err := r.ExtractUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.ID, nil
|
||||
default:
|
||||
panic(fmt.Sprintf("got unexpected AuthResult type %t", r))
|
||||
}
|
||||
}
|
||||
|
||||
Both implementing types share a lot of methods by name, like ExtractUser() in
|
||||
this example. But those methods cannot be part of the AuthResult interface
|
||||
because the return types are different (in this case, type tokens2.User vs.
|
||||
type tokens3.User).
|
||||
*/
|
||||
type AuthResult interface {
|
||||
ExtractTokenID() (string, error)
|
||||
}
|
|
@ -9,20 +9,37 @@ 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.
|
||||
|
||||
It is now recommended to use the `clientconfig` package found at
|
||||
https://github.com/gophercloud/utils/tree/master/openstack/clientconfig
|
||||
for all authentication purposes.
|
||||
|
||||
The below documentation is still relevant. clientconfig simply implements
|
||||
the below and presents it in an easier and more flexible way.
|
||||
|
||||
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",
|
||||
Username: "{username}",
|
||||
Password: "{password}",
|
||||
TenantID: "{tenant_id}",
|
||||
}
|
||||
opts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||
Username: "{username}",
|
||||
Password: "{password}",
|
||||
TenantID: "{tenant_id}",
|
||||
}
|
||||
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
provider, err := openstack.AuthenticatedClient(opts)
|
||||
|
||||
You can authenticate with a token by doing:
|
||||
|
||||
opts := gophercloud.AuthOptions{
|
||||
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||
TokenID: "{token_id}",
|
||||
TenantID: "{tenant_id}",
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -39,16 +56,16 @@ operations for a particular OpenStack service. Examples of services include:
|
|||
Compute, Object Storage, Block Storage. In order to define one, you need to
|
||||
pass in the parent provider, like so:
|
||||
|
||||
opts := gophercloud.EndpointOpts{Region: "RegionOne"}
|
||||
opts := gophercloud.EndpointOpts{Region: "RegionOne"}
|
||||
|
||||
client := openstack.NewComputeV2(provider, opts)
|
||||
client, err := 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:
|
||||
|
||||
server, err := servers.Get(client, "{serverId}").Extract()
|
||||
server, err := servers.Get(client, "{serverId}").Extract()
|
||||
|
||||
Intermediate Result structs are returned for API operations, which allow
|
||||
generic access to the HTTP headers, response body, and any errors associated
|
||||
|
@ -56,11 +73,11 @@ with the network transaction. To turn a result into a usable resource struct,
|
|||
you must call the Extract method which is chained to the response, or an
|
||||
Extract function from an applicable extension:
|
||||
|
||||
result := servers.Get(client, "{serverId}")
|
||||
result := servers.Get(client, "{serverId}")
|
||||
|
||||
// Attempt to extract the disk configuration from the OS-DCF disk config
|
||||
// extension:
|
||||
config, err := diskconfig.ExtractGet(result)
|
||||
// Attempt to extract the disk configuration from the OS-DCF disk config
|
||||
// extension:
|
||||
config, err := diskconfig.ExtractGet(result)
|
||||
|
||||
All requests that enumerate a collection return a Pager struct that is used to
|
||||
iterate through the results one page at a time. Use the EachPage method on that
|
||||
|
@ -68,17 +85,17 @@ Pager to handle each successive Page in a closure, then use the appropriate
|
|||
extraction method from that request's package to interpret that Page as a slice
|
||||
of results:
|
||||
|
||||
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
|
||||
s, err := servers.ExtractServers(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
|
||||
s, err := servers.ExtractServers(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Handle the []servers.Server slice.
|
||||
// Handle the []servers.Server slice.
|
||||
|
||||
// Return "false" or an error to prematurely stop fetching new pages.
|
||||
return true, nil
|
||||
})
|
||||
// Return "false" or an error to prematurely stop fetching new pages.
|
||||
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:
|
||||
|
|
|
@ -122,6 +122,11 @@ type ErrDefault408 struct {
|
|||
ErrUnexpectedResponseCode
|
||||
}
|
||||
|
||||
// ErrDefault409 is the default error type returned on a 409 HTTP response code.
|
||||
type ErrDefault409 struct {
|
||||
ErrUnexpectedResponseCode
|
||||
}
|
||||
|
||||
// ErrDefault429 is the default error type returned on a 429 HTTP response code.
|
||||
type ErrDefault429 struct {
|
||||
ErrUnexpectedResponseCode
|
||||
|
@ -211,6 +216,12 @@ type Err408er interface {
|
|||
Error408(ErrUnexpectedResponseCode) error
|
||||
}
|
||||
|
||||
// Err409er is the interface resource error types implement to override the error message
|
||||
// from a 409 error.
|
||||
type Err409er interface {
|
||||
Error409(ErrUnexpectedResponseCode) error
|
||||
}
|
||||
|
||||
// Err429er is the interface resource error types implement to override the error message
|
||||
// from a 429 error.
|
||||
type Err429er interface {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
module github.com/gophercloud/gophercloud
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -13,15 +13,19 @@ 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.
|
||||
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_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and
|
||||
OS_PROJECT_NAME are optional.
|
||||
or an error will result. OS_PROJECT_ID, is optional.
|
||||
|
||||
OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and
|
||||
OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will
|
||||
still be referred as "tenant" in Gophercloud.
|
||||
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:
|
||||
|
@ -59,11 +63,14 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
|||
return nilOptions, err
|
||||
}
|
||||
|
||||
if username == "" && userID == "" {
|
||||
err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||
EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"},
|
||||
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
|
||||
}
|
||||
return nilOptions, err
|
||||
}
|
||||
|
||||
if password == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
||||
|
@ -80,6 +87,26 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
|||
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,
|
||||
|
|
|
@ -267,3 +267,34 @@ func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteRes
|
|||
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// ImageMetadataOptsBuilder allows extensions to add additional parameters to the
|
||||
// ImageMetadataRequest request.
|
||||
type ImageMetadataOptsBuilder interface {
|
||||
ToImageMetadataMap() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
// ImageMetadataOpts contains options for setting image metadata to a volume.
|
||||
type ImageMetadataOpts struct {
|
||||
// The image metadata to add to the volume as a set of metadata key and value pairs.
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
// ToImageMetadataMap assembles a request body based on the contents of a
|
||||
// ImageMetadataOpts.
|
||||
func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "os-set_image_metadata")
|
||||
}
|
||||
|
||||
// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder.
|
||||
func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) {
|
||||
b, err := opts.ToImageMetadataMap()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -29,6 +29,12 @@ type UploadImageResult struct {
|
|||
gophercloud.Result
|
||||
}
|
||||
|
||||
// SetImageMetadataResult contains the response body and error from an SetImageMetadata
|
||||
// request.
|
||||
type SetImageMetadataResult struct {
|
||||
gophercloud.ErrResult
|
||||
}
|
||||
|
||||
// ReserveResult contains the response body and error from a Reserve request.
|
||||
type ReserveResult struct {
|
||||
gophercloud.ErrResult
|
||||
|
|
38
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go
generated
vendored
38
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go
generated
vendored
|
@ -38,6 +38,8 @@ type CreateOpts struct {
|
|||
ImageID string `json:"imageRef,omitempty"`
|
||||
// The associated volume type
|
||||
VolumeType string `json:"volume_type,omitempty"`
|
||||
// Multiattach denotes if the volume is multi-attach capable.
|
||||
Multiattach bool `json:"multiattach,omitempty"`
|
||||
}
|
||||
|
||||
// ToVolumeCreateMap assembles a request body based on the contents of a
|
||||
|
@ -61,9 +63,37 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create
|
|||
return
|
||||
}
|
||||
|
||||
// DeleteOptsBuilder allows extensions to add additional parameters to the
|
||||
// Delete request.
|
||||
type DeleteOptsBuilder interface {
|
||||
ToVolumeDeleteQuery() (string, error)
|
||||
}
|
||||
|
||||
// DeleteOpts contains options for deleting a Volume. This object is passed to
|
||||
// the volumes.Delete function.
|
||||
type DeleteOpts struct {
|
||||
// Delete all snapshots of this volume as well.
|
||||
Cascade bool `q:"cascade"`
|
||||
}
|
||||
|
||||
// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string.
|
||||
func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
return q.String(), err
|
||||
}
|
||||
|
||||
// Delete will delete the existing Volume with the provided ID.
|
||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||
func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) {
|
||||
url := deleteURL(client, id)
|
||||
if opts != nil {
|
||||
query, err := opts.ToVolumeDeleteQuery()
|
||||
if err != nil {
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
url += query
|
||||
}
|
||||
_, r.Err = client.Delete(url, nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -145,8 +175,8 @@ type UpdateOptsBuilder interface {
|
|||
// to the volumes.Update function. For more information about the parameters, see
|
||||
// the Volume object.
|
||||
type UpdateOpts struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,8 @@ type Volume struct {
|
|||
ConsistencyGroupID string `json:"consistencygroup_id"`
|
||||
// Multiattach denotes if the volume is multi-attach capable.
|
||||
Multiattach bool `json:"multiattach"`
|
||||
// Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image.
|
||||
VolumeImageMetadata map[string]string `json:"volume_image_metadata"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON another unmarshalling function
|
||||
|
|
|
@ -135,7 +135,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
|||
|
||||
result := tokens2.Create(v2Client, v2Opts)
|
||||
|
||||
token, err := result.ExtractToken()
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -150,8 +150,9 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
|||
// 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.TokenID = ""
|
||||
tac.SetTokenAndAuthResult(nil)
|
||||
tao := options
|
||||
tao.AllowReauth = false
|
||||
client.ReauthFunc = func() error {
|
||||
|
@ -159,11 +160,10 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.TokenID = tac.TokenID
|
||||
client.CopyTokenFrom(&tac)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
client.TokenID = token.ID
|
||||
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||
return V2EndpointURL(catalog, opts)
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
|||
|
||||
result := tokens3.Create(v3Client, opts)
|
||||
|
||||
token, err := result.ExtractToken()
|
||||
err = client.SetTokenAndAuthResult(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -199,15 +199,14 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
|||
return err
|
||||
}
|
||||
|
||||
client.TokenID = token.ID
|
||||
|
||||
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.TokenID = ""
|
||||
tac.SetTokenAndAuthResult(nil)
|
||||
var tao tokens3.AuthOptionsBuilder
|
||||
switch ot := opts.(type) {
|
||||
case *gophercloud.AuthOptions:
|
||||
|
@ -226,7 +225,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.TokenID = tac.TokenID
|
||||
client.CopyTokenFrom(&tac)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -305,6 +304,18 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO
|
|||
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) {
|
||||
|
|
|
@ -68,7 +68,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create
|
|||
return
|
||||
}
|
||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||
OkCodes: []int{200},
|
||||
OkCodes: []int{200, 201},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
2
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
2
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
|
@ -148,7 +148,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create
|
|||
return
|
||||
}
|
||||
|
||||
// Get retrieves details of a single flavor. Use ExtractFlavor to convert its
|
||||
// Get retrieves details of a single flavor. Use Extract to convert its
|
||||
// result into a Flavor.
|
||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||
|
|
11
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/microversions.go
generated
vendored
Normal file
11
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/microversions.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
package servers
|
||||
|
||||
// ExtractTags will extract the tags of a server.
|
||||
// This requires the client to be set to microversion 2.26 or later.
|
||||
func (r serverResult) ExtractTags() ([]string, error) {
|
||||
var s struct {
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
err := r.ExtractInto(&s)
|
||||
return s.Tags, err
|
||||
}
|
20
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
20
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
|
@ -183,12 +183,22 @@ type CreateOpts struct {
|
|||
// AccessIPv4 specifies an IPv4 address for the instance.
|
||||
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
||||
|
||||
// AccessIPv6 pecifies an IPv6 address for the instance.
|
||||
// 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
|
||||
|
@ -272,6 +282,14 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
|||
b["flavorRef"] = flavorID
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
2
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
2
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
|
@ -85,7 +85,7 @@ type UpdateOpts struct {
|
|||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Description is the description of the tenant.
|
||||
Description string `json:"description,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
// Enabled sets the tenant status to enabled or disabled.
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
|
|
15
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
15
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
|
@ -135,6 +135,21 @@ func (r CreateResult) ExtractToken() (*Token, error) {
|
|||
}, 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) {
|
||||
|
|
2
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
2
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
|
@ -134,9 +134,9 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
|
|||
OkCodes: []int{200, 203},
|
||||
})
|
||||
if resp != nil {
|
||||
r.Err = err
|
||||
r.Header = resp.Header
|
||||
}
|
||||
r.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
|
|
7
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
7
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
|
@ -102,6 +102,13 @@ func (r commonResult) ExtractToken() (*Token, error) {
|
|||
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
|
||||
}
|
||||
|
||||
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||
// with the user's Token.
|
||||
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||
|
|
14
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go
generated
vendored
14
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go
generated
vendored
|
@ -321,6 +321,20 @@ func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} {
|
|||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateOp represents a valid update operation.
|
||||
type UpdateOp string
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ Example to Disassociate a Floating IP with a Port
|
|||
fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||
|
||||
updateOpts := floatingips.UpdateOpts{
|
||||
PortID: nil,
|
||||
PortID: new(string),
|
||||
}
|
||||
|
||||
fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract()
|
||||
|
|
|
@ -5,6 +5,12 @@ import (
|
|||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// ListOptsBuilder allows extensions to add additional parameters to the
|
||||
// List request.
|
||||
type ListOptsBuilder interface {
|
||||
ToFloatingIPListQuery() (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 floating IP attributes you want to see returned. SortKey allows you to
|
||||
|
@ -12,6 +18,7 @@ import (
|
|||
// either `asc' or `desc'. Marker and Limit are used for pagination.
|
||||
type ListOpts struct {
|
||||
ID string `q:"id"`
|
||||
Description string `q:"description"`
|
||||
FloatingNetworkID string `q:"floating_network_id"`
|
||||
PortID string `q:"port_id"`
|
||||
FixedIP string `q:"fixed_ip_address"`
|
||||
|
@ -24,18 +31,31 @@ type ListOpts struct {
|
|||
SortDir string `q:"sort_dir"`
|
||||
RouterID string `q:"router_id"`
|
||||
Status string `q:"status"`
|
||||
Tags string `q:"tags"`
|
||||
TagsAny string `q:"tags-any"`
|
||||
NotTags string `q:"not-tags"`
|
||||
NotTagsAny string `q:"not-tags-any"`
|
||||
}
|
||||
|
||||
// ToNetworkListQuery formats a ListOpts into a query string.
|
||||
func (opts ListOpts) ToFloatingIPListQuery() (string, error) {
|
||||
q, err := gophercloud.BuildQueryString(opts)
|
||||
return q.String(), err
|
||||
}
|
||||
|
||||
// List returns a Pager which allows you to iterate over a collection of
|
||||
// floating IP resources. It accepts a ListOpts struct, which allows you to
|
||||
// filter and sort the returned collection for greater efficiency.
|
||||
func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
|
||||
q, err := gophercloud.BuildQueryString(&opts)
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||
url := rootURL(c)
|
||||
if opts != nil {
|
||||
query, err := opts.ToFloatingIPListQuery()
|
||||
if err != nil {
|
||||
return pagination.Pager{Err: err}
|
||||
}
|
||||
url += query
|
||||
}
|
||||
u := rootURL(c) + q.String()
|
||||
return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
|
||||
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||
return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}}
|
||||
})
|
||||
}
|
||||
|
@ -50,6 +70,7 @@ type CreateOptsBuilder interface {
|
|||
// resource. The only required fields are FloatingNetworkID and PortID which
|
||||
// refer to the external network and internal port respectively.
|
||||
type CreateOpts struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
FloatingNetworkID string `json:"floating_network_id" required:"true"`
|
||||
FloatingIP string `json:"floating_ip_address,omitempty"`
|
||||
PortID string `json:"port_id,omitempty"`
|
||||
|
@ -116,13 +137,24 @@ type UpdateOptsBuilder interface {
|
|||
// linked to. To associate the floating IP with a new internal port, provide its
|
||||
// ID. To disassociate the floating IP from all ports, provide an empty string.
|
||||
type UpdateOpts struct {
|
||||
PortID *string `json:"port_id"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
PortID *string `json:"port_id,omitempty"`
|
||||
FixedIP string `json:"fixed_ip_address,omitempty"`
|
||||
}
|
||||
|
||||
// ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
|
||||
// interface
|
||||
func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) {
|
||||
return gophercloud.BuildRequestBody(opts, "floatingip")
|
||||
b, err := gophercloud.BuildRequestBody(opts, "floatingip")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if m := b["floatingip"].(map[string]interface{}); m["port_id"] == "" {
|
||||
m["port_id"] = nil
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Update allows floating IP resources to be updated. Currently, the only way to
|
||||
|
|
|
@ -15,6 +15,9 @@ type FloatingIP struct {
|
|||
// ID is the unique identifier for the floating IP instance.
|
||||
ID string `json:"id"`
|
||||
|
||||
// Description for the floating IP instance.
|
||||
Description string `json:"description"`
|
||||
|
||||
// FloatingNetworkID is the UUID of the external network where the floating
|
||||
// IP is to be created.
|
||||
FloatingNetworkID string `json:"floating_network_id"`
|
||||
|
@ -42,6 +45,9 @@ type FloatingIP struct {
|
|||
|
||||
// RouterID is the ID of the router used for this floating IP.
|
||||
RouterID string `json:"router_id"`
|
||||
|
||||
// Tags optionally set via extensions/attributestags
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
type commonResult struct {
|
||||
|
@ -50,11 +56,13 @@ type commonResult struct {
|
|||
|
||||
// Extract will extract a FloatingIP resource from a result.
|
||||
func (r commonResult) Extract() (*FloatingIP, error) {
|
||||
var s struct {
|
||||
FloatingIP *FloatingIP `json:"floatingip"`
|
||||
}
|
||||
var s FloatingIP
|
||||
err := r.ExtractInto(&s)
|
||||
return s.FloatingIP, err
|
||||
return &s, err
|
||||
}
|
||||
|
||||
func (r commonResult) ExtractInto(v interface{}) error {
|
||||
return r.Result.ExtractIntoStructPtr(v, "floatingip")
|
||||
}
|
||||
|
||||
// CreateResult represents the result of a create operation. Call its Extract
|
||||
|
@ -117,3 +125,7 @@ func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) {
|
|||
err := (r.(FloatingIPPage)).ExtractInto(&s)
|
||||
return s.FloatingIPs, err
|
||||
}
|
||||
|
||||
func ExtractFloatingIPsInto(r pagination.Page, v interface{}) error {
|
||||
return r.(FloatingIPPage).Result.ExtractIntoSlicePtr(v, "floatingips")
|
||||
}
|
||||
|
|
3
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/doc.go
generated
vendored
3
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/doc.go
generated
vendored
|
@ -45,8 +45,9 @@ Example to Update a Network
|
|||
|
||||
networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78"
|
||||
|
||||
name := "new_name"
|
||||
updateOpts := networks.UpdateOpts{
|
||||
Name: "new_name",
|
||||
Name: &name,
|
||||
}
|
||||
|
||||
network, err := networks.Update(networkClient, networkID, updateOpts).Extract()
|
||||
|
|
13
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go
generated
vendored
13
vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go
generated
vendored
|
@ -19,6 +19,7 @@ type ListOptsBuilder interface {
|
|||
type ListOpts struct {
|
||||
Status string `q:"status"`
|
||||
Name string `q:"name"`
|
||||
Description string `q:"description"`
|
||||
AdminStateUp *bool `q:"admin_state_up"`
|
||||
TenantID string `q:"tenant_id"`
|
||||
ProjectID string `q:"project_id"`
|
||||
|
@ -28,6 +29,10 @@ type ListOpts struct {
|
|||
Limit int `q:"limit"`
|
||||
SortKey string `q:"sort_key"`
|
||||
SortDir string `q:"sort_dir"`
|
||||
Tags string `q:"tags"`
|
||||
TagsAny string `q:"tags-any"`
|
||||
NotTags string `q:"not-tags"`
|
||||
NotTagsAny string `q:"not-tags-any"`
|
||||
}
|
||||
|
||||
// ToNetworkListQuery formats a ListOpts into a query string.
|
||||
|
@ -69,6 +74,7 @@ type CreateOptsBuilder interface {
|
|||
type CreateOpts struct {
|
||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Shared *bool `json:"shared,omitempty"`
|
||||
TenantID string `json:"tenant_id,omitempty"`
|
||||
ProjectID string `json:"project_id,omitempty"`
|
||||
|
@ -105,9 +111,10 @@ type UpdateOptsBuilder interface {
|
|||
|
||||
// UpdateOpts represents options used to update a network.
|
||||
type UpdateOpts struct {
|
||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Shared *bool `json:"shared,omitempty"`
|
||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Shared *bool `json:"shared,omitempty"`
|
||||
}
|
||||
|
||||
// ToNetworkUpdateMap builds a request body from UpdateOpts.
|
||||
|
|
|
@ -52,6 +52,9 @@ type Network struct {
|
|||
// Human-readable name for the network. Might not be unique.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Description for the network
|
||||
Description string `json:"description"`
|
||||
|
||||
// The administrative state of network. If false (down), the network does not
|
||||
// forward packets.
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
|
@ -76,6 +79,9 @@ type Network struct {
|
|||
// Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others.
|
||||
// Used to make network resources highly available.
|
||||
AvailabilityZoneHints []string `json:"availability_zone_hints"`
|
||||
|
||||
// Tags optionally set via extensions/attributestags
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// NetworkPage is the page returned by a pager when traversing over a
|
||||
|
|
|
@ -120,6 +120,22 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{},
|
|||
continue
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) {
|
||||
sliceValue := v
|
||||
if sliceValue.Kind() == reflect.Ptr {
|
||||
sliceValue = sliceValue.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < sliceValue.Len(); i++ {
|
||||
element := sliceValue.Index(i)
|
||||
if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) {
|
||||
_, err := BuildRequestBody(element.Interface(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
|
||||
if zero {
|
||||
//fmt.Printf("value before change: %+v\n", optsValue.Field(i))
|
||||
|
|
|
@ -2,7 +2,9 @@ package gophercloud
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -71,26 +73,44 @@ type ProviderClient struct {
|
|||
// authentication functions for different Identity service versions.
|
||||
ReauthFunc func() error
|
||||
|
||||
// Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client
|
||||
// with the token and reauth func zeroed. Such client can be used to perform reauthorization.
|
||||
Throwaway bool
|
||||
|
||||
// Context is the context passed to the HTTP request.
|
||||
Context context.Context
|
||||
|
||||
// mut is a mutex for the client. It protects read and write access to client attributes such as getting
|
||||
// and setting the TokenID.
|
||||
mut *sync.RWMutex
|
||||
|
||||
// reauthmut is a mutex for reauthentication it attempts to ensure that only one reauthentication
|
||||
// attempt happens at one time.
|
||||
reauthmut *reauthlock
|
||||
|
||||
authResult AuthResult
|
||||
}
|
||||
|
||||
// reauthlock represents a set of attributes used to help in the reauthentication process.
|
||||
type reauthlock struct {
|
||||
sync.RWMutex
|
||||
reauthing bool
|
||||
reauthing bool
|
||||
reauthingErr error
|
||||
done *sync.Cond
|
||||
}
|
||||
|
||||
// AuthenticatedHeaders returns a map of HTTP headers that are common for all
|
||||
// authenticated service requests.
|
||||
// authenticated service requests. Blocks if Reauthenticate is in progress.
|
||||
func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) {
|
||||
if client.IsThrowaway() {
|
||||
return
|
||||
}
|
||||
if client.reauthmut != nil {
|
||||
client.reauthmut.RLock()
|
||||
if client.reauthmut.reauthing {
|
||||
client.reauthmut.RUnlock()
|
||||
return
|
||||
client.reauthmut.Lock()
|
||||
for client.reauthmut.reauthing {
|
||||
client.reauthmut.done.Wait()
|
||||
}
|
||||
client.reauthmut.RUnlock()
|
||||
client.reauthmut.Unlock()
|
||||
}
|
||||
t := client.Token()
|
||||
if t == "" {
|
||||
|
@ -106,6 +126,20 @@ func (client *ProviderClient) UseTokenLock() {
|
|||
client.reauthmut = new(reauthlock)
|
||||
}
|
||||
|
||||
// GetAuthResult returns the result from the request that was used to obtain a
|
||||
// provider client's Keystone token.
|
||||
//
|
||||
// The result is nil when authentication has not yet taken place, when the token
|
||||
// was set manually with SetToken(), or when a ReauthFunc was used that does not
|
||||
// record the AuthResult.
|
||||
func (client *ProviderClient) GetAuthResult() AuthResult {
|
||||
if client.mut != nil {
|
||||
client.mut.RLock()
|
||||
defer client.mut.RUnlock()
|
||||
}
|
||||
return client.authResult
|
||||
}
|
||||
|
||||
// Token safely reads the value of the auth token from the ProviderClient. Applications should
|
||||
// call this method to access the token instead of the TokenID field
|
||||
func (client *ProviderClient) Token() string {
|
||||
|
@ -117,33 +151,102 @@ func (client *ProviderClient) Token() string {
|
|||
}
|
||||
|
||||
// SetToken safely sets the value of the auth token in the ProviderClient. Applications may
|
||||
// use this method in a custom ReauthFunc
|
||||
// use this method in a custom ReauthFunc.
|
||||
//
|
||||
// WARNING: This function is deprecated. Use SetTokenAndAuthResult() instead.
|
||||
func (client *ProviderClient) SetToken(t string) {
|
||||
if client.mut != nil {
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
}
|
||||
client.TokenID = t
|
||||
client.authResult = nil
|
||||
}
|
||||
|
||||
//Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is
|
||||
//called because of a 401 response, the caller may pass the previous token. In
|
||||
//this case, the reauthentication can be skipped if another thread has already
|
||||
//reauthenticated in the meantime. If no previous token is known, an empty
|
||||
//string should be passed instead to force unconditional reauthentication.
|
||||
// SetTokenAndAuthResult safely sets the value of the auth token in the
|
||||
// ProviderClient and also records the AuthResult that was returned from the
|
||||
// token creation request. Applications may call this in a custom ReauthFunc.
|
||||
func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error {
|
||||
tokenID := ""
|
||||
var err error
|
||||
if r != nil {
|
||||
tokenID, err = r.ExtractTokenID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if client.mut != nil {
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
}
|
||||
client.TokenID = tokenID
|
||||
client.authResult = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyTokenFrom safely copies the token from another ProviderClient into the
|
||||
// this one.
|
||||
func (client *ProviderClient) CopyTokenFrom(other *ProviderClient) {
|
||||
if client.mut != nil {
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
}
|
||||
if other.mut != nil && other.mut != client.mut {
|
||||
other.mut.RLock()
|
||||
defer other.mut.RUnlock()
|
||||
}
|
||||
client.TokenID = other.TokenID
|
||||
client.authResult = other.authResult
|
||||
}
|
||||
|
||||
// IsThrowaway safely reads the value of the client Throwaway field.
|
||||
func (client *ProviderClient) IsThrowaway() bool {
|
||||
if client.reauthmut != nil {
|
||||
client.reauthmut.RLock()
|
||||
defer client.reauthmut.RUnlock()
|
||||
}
|
||||
return client.Throwaway
|
||||
}
|
||||
|
||||
// SetThrowaway safely sets the value of the client Throwaway field.
|
||||
func (client *ProviderClient) SetThrowaway(v bool) {
|
||||
if client.reauthmut != nil {
|
||||
client.reauthmut.Lock()
|
||||
defer client.reauthmut.Unlock()
|
||||
}
|
||||
client.Throwaway = v
|
||||
}
|
||||
|
||||
// Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is
|
||||
// called because of a 401 response, the caller may pass the previous token. In
|
||||
// this case, the reauthentication can be skipped if another thread has already
|
||||
// reauthenticated in the meantime. If no previous token is known, an empty
|
||||
// string should be passed instead to force unconditional reauthentication.
|
||||
func (client *ProviderClient) Reauthenticate(previousToken string) (err error) {
|
||||
if client.ReauthFunc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if client.mut == nil {
|
||||
if client.reauthmut == nil {
|
||||
return client.ReauthFunc()
|
||||
}
|
||||
client.mut.Lock()
|
||||
defer client.mut.Unlock()
|
||||
|
||||
client.reauthmut.Lock()
|
||||
if client.reauthmut.reauthing {
|
||||
for !client.reauthmut.reauthing {
|
||||
client.reauthmut.done.Wait()
|
||||
}
|
||||
err = client.reauthmut.reauthingErr
|
||||
client.reauthmut.Unlock()
|
||||
return err
|
||||
}
|
||||
client.reauthmut.Unlock()
|
||||
|
||||
client.reauthmut.Lock()
|
||||
client.reauthmut.reauthing = true
|
||||
client.reauthmut.done = sync.NewCond(client.reauthmut)
|
||||
client.reauthmut.reauthingErr = nil
|
||||
client.reauthmut.Unlock()
|
||||
|
||||
if previousToken == "" || client.TokenID == previousToken {
|
||||
|
@ -152,6 +255,8 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) {
|
|||
|
||||
client.reauthmut.Lock()
|
||||
client.reauthmut.reauthing = false
|
||||
client.reauthmut.reauthingErr = err
|
||||
client.reauthmut.done.Broadcast()
|
||||
client.reauthmut.Unlock()
|
||||
return
|
||||
}
|
||||
|
@ -192,7 +297,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
|||
// io.ReadSeeker as-is. Default the content-type to application/json.
|
||||
if options.JSONBody != nil {
|
||||
if options.RawBody != nil {
|
||||
panic("Please provide only one of JSONBody or RawBody to gophercloud.Request().")
|
||||
return nil, errors.New("please provide only one of JSONBody or RawBody to gophercloud.Request()")
|
||||
}
|
||||
|
||||
rendered, err := json.Marshal(options.JSONBody)
|
||||
|
@ -213,6 +318,9 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if client.Context != nil {
|
||||
req = req.WithContext(client.Context)
|
||||
}
|
||||
|
||||
// Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to
|
||||
// modify or omit any header.
|
||||
|
@ -251,13 +359,14 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
|||
}
|
||||
|
||||
// Allow default OkCodes if none explicitly set
|
||||
if options.OkCodes == nil {
|
||||
options.OkCodes = defaultOkCodes(method)
|
||||
okc := options.OkCodes
|
||||
if okc == nil {
|
||||
okc = defaultOkCodes(method)
|
||||
}
|
||||
|
||||
// Validate the HTTP response status.
|
||||
var ok bool
|
||||
for _, code := range options.OkCodes {
|
||||
for _, code := range okc {
|
||||
if resp.StatusCode == code {
|
||||
ok = true
|
||||
break
|
||||
|
@ -295,11 +404,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
|||
seeker.Seek(0, 0)
|
||||
}
|
||||
}
|
||||
// make a new call to request with a nil reauth func in order to avoid infinite loop
|
||||
reauthFunc := client.ReauthFunc
|
||||
client.ReauthFunc = nil
|
||||
resp, err = client.Request(method, url, options)
|
||||
client.ReauthFunc = reauthFunc
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *ErrUnexpectedResponseCode:
|
||||
|
@ -338,6 +443,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
|||
if error408er, ok := errType.(Err408er); ok {
|
||||
err = error408er.Error408(respErr)
|
||||
}
|
||||
case http.StatusConflict:
|
||||
err = ErrDefault409{respErr}
|
||||
if error409er, ok := errType.(Err409er); ok {
|
||||
err = error409er.Error409(respErr)
|
||||
}
|
||||
case 429:
|
||||
err = ErrDefault429{respErr}
|
||||
if error429er, ok := errType.(Err429er); ok {
|
||||
|
|
|
@ -90,38 +90,40 @@ func (r Result) extractIntoPtr(to interface{}, label string) error {
|
|||
if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
|
||||
newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0)
|
||||
|
||||
for _, v := range m[label].([]interface{}) {
|
||||
// For each iteration of the slice, we create a new struct.
|
||||
// This is to work around a bug where elements of a slice
|
||||
// are reused and not overwritten when the same copy of the
|
||||
// struct is used:
|
||||
//
|
||||
// https://github.com/golang/go/issues/21092
|
||||
// https://github.com/golang/go/issues/24155
|
||||
// https://play.golang.org/p/NHo3ywlPZli
|
||||
newType := reflect.New(typeOfV).Elem()
|
||||
if mSlice, ok := m[label].([]interface{}); ok {
|
||||
for _, v := range mSlice {
|
||||
// For each iteration of the slice, we create a new struct.
|
||||
// This is to work around a bug where elements of a slice
|
||||
// are reused and not overwritten when the same copy of the
|
||||
// struct is used:
|
||||
//
|
||||
// https://github.com/golang/go/issues/21092
|
||||
// https://github.com/golang/go/issues/24155
|
||||
// https://play.golang.org/p/NHo3ywlPZli
|
||||
newType := reflect.New(typeOfV).Elem()
|
||||
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is needed for structs with an UnmarshalJSON method.
|
||||
// Technically this is just unmarshalling the response into
|
||||
// a struct that is never used, but it's good enough to
|
||||
// trigger the UnmarshalJSON method.
|
||||
for i := 0; i < newType.NumField(); i++ {
|
||||
s := newType.Field(i).Addr().Interface()
|
||||
|
||||
// Unmarshal is used rather than NewDecoder to also work
|
||||
// around the above-mentioned bug.
|
||||
err = json.Unmarshal(b, s)
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newSlice = reflect.Append(newSlice, newType)
|
||||
// This is needed for structs with an UnmarshalJSON method.
|
||||
// Technically this is just unmarshalling the response into
|
||||
// a struct that is never used, but it's good enough to
|
||||
// trigger the UnmarshalJSON method.
|
||||
for i := 0; i < newType.NumField(); i++ {
|
||||
s := newType.Field(i).Addr().Interface()
|
||||
|
||||
// Unmarshal is used rather than NewDecoder to also work
|
||||
// around the above-mentioned bug.
|
||||
err = json.Unmarshal(b, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newSlice = reflect.Append(newSlice, newType)
|
||||
}
|
||||
}
|
||||
|
||||
// "to" should now be properly modeled to receive the
|
||||
|
|
|
@ -129,6 +129,10 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) {
|
|||
opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion
|
||||
case "volume":
|
||||
opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion
|
||||
case "baremetal":
|
||||
opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion
|
||||
case "baremetal-introspection":
|
||||
opts.MoreHeaders["X-OpenStack-Ironic-Inspector-API-Version"] = client.Microversion
|
||||
}
|
||||
|
||||
if client.Type != "" {
|
||||
|
|
|
@ -196,7 +196,7 @@ github.com/google/shlex
|
|||
github.com/google/uuid
|
||||
# github.com/googleapis/gax-go/v2 v2.0.3
|
||||
github.com/googleapis/gax-go/v2
|
||||
# github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06
|
||||
# github.com/gophercloud/gophercloud v0.2.0
|
||||
github.com/gophercloud/gophercloud
|
||||
github.com/gophercloud/gophercloud/openstack
|
||||
github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions
|
||||
|
|
Loading…
Reference in New Issue