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())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
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
|
imageId = image.ImageID
|
||||||
} else {
|
} else {
|
||||||
imageId, err = servers.CreateImage(computeClient, server.ID, servers.CreateImageOpts{
|
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/go-cmp v0.2.0
|
||||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
|
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
|
||||||
github.com/google/uuid v1.0.0
|
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/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||||
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // 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/h2non/gock.v1 v1.0.12 // indirect
|
||||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
gopkg.in/ini.v1 v1.42.0 // indirect
|
||||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 // 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/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 h1:m7Rt/8En7PLrM7PQpykdZBPKUdgZWN6MwiA/ChVIoxs=
|
||||||
github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
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 h1:Cw/B8Bu7Rryomxf7bjc8zNfIyLgjxsDd91n0eGRWpuo=
|
||||||
github.com/gophercloud/utils v0.0.0-20190124192022-a5c25e7a53a6/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=
|
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=
|
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 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
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-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 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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=
|
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-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-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-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 h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
language: go
|
language: go
|
||||||
sudo: false
|
sudo: false
|
||||||
install:
|
install:
|
||||||
- go get golang.org/x/crypto/ssh
|
- GO111MODULE=off go get golang.org/x/crypto/ssh
|
||||||
- go get -v -tags 'fixtures acceptance' ./...
|
- GO111MODULE=off go get -v -tags 'fixtures acceptance' ./...
|
||||||
- go get github.com/wadey/gocovmerge
|
- GO111MODULE=off go get github.com/wadey/gocovmerge
|
||||||
- go get github.com/mattn/goveralls
|
- GO111MODULE=off go get github.com/mattn/goveralls
|
||||||
- go get golang.org/x/tools/cmd/goimports
|
- GO111MODULE=off go get golang.org/x/tools/cmd/goimports
|
||||||
go:
|
go:
|
||||||
- "1.10"
|
- "1.10"
|
||||||
|
- "1.11"
|
||||||
|
- "1.12"
|
||||||
- "tip"
|
- "tip"
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ="
|
- secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ="
|
||||||
|
- GO111MODULE=on
|
||||||
before_script:
|
before_script:
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
script:
|
script:
|
||||||
- ./script/coverage
|
- ./script/coverage
|
||||||
|
- ./script/unittest
|
||||||
- ./script/format
|
- ./script/format
|
||||||
after_success:
|
after_success:
|
||||||
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
|
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
|
||||||
|
|
|
@ -13,6 +13,31 @@
|
||||||
Run gophercloud acceptance test on master branch
|
Run gophercloud acceptance test on master branch
|
||||||
run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml
|
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:
|
- job:
|
||||||
name: gophercloud-acceptance-test-queens
|
name: gophercloud-acceptance-test-queens
|
||||||
parent: gophercloud-acceptance-test
|
parent: gophercloud-acceptance-test
|
||||||
|
@ -65,6 +90,7 @@
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-unittest
|
- gophercloud-unittest
|
||||||
- gophercloud-acceptance-test
|
- gophercloud-acceptance-test
|
||||||
|
- gophercloud-acceptance-test-ironic
|
||||||
recheck-mitaka:
|
recheck-mitaka:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-acceptance-test-mitaka
|
- gophercloud-acceptance-test-mitaka
|
||||||
|
@ -80,7 +106,9 @@
|
||||||
recheck-queens:
|
recheck-queens:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-acceptance-test-queens
|
- gophercloud-acceptance-test-queens
|
||||||
periodic:
|
recheck-rocky:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-unittest
|
- gophercloud-acceptance-test-rocky
|
||||||
- gophercloud-acceptance-test
|
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
|
// Populate the request structure based on the provided arguments. Create and return an error
|
||||||
// if insufficient or incompatible information is present.
|
// if insufficient or incompatible information is present.
|
||||||
var req request
|
var req request
|
||||||
var userRequest userReq
|
|
||||||
|
|
||||||
if opts.Password == "" {
|
if opts.Password == "" {
|
||||||
if opts.TokenID != "" {
|
if opts.TokenID != "" {
|
||||||
|
@ -229,25 +228,44 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
if opts.ApplicationCredentialSecret == "" {
|
if opts.ApplicationCredentialSecret == "" {
|
||||||
return nil, ErrAppCredMissingSecret{}
|
return nil, ErrAppCredMissingSecret{}
|
||||||
}
|
}
|
||||||
// make sure that only one of DomainName or DomainID were provided
|
|
||||||
if opts.DomainID == "" && opts.DomainName == "" {
|
var userRequest *userReq
|
||||||
return nil, ErrDomainIDOrDomainName{}
|
|
||||||
|
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 != "" {
|
if userRequest == nil && opts.Username == "" {
|
||||||
userRequest = userReq{
|
// Make sure that Username or UserID are provided
|
||||||
|
return nil, ErrUsernameOrUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if userRequest == nil && opts.DomainID != "" {
|
||||||
|
userRequest = &userReq{
|
||||||
Name: &opts.Username,
|
Name: &opts.Username,
|
||||||
Domain: &domainReq{ID: &opts.DomainID},
|
Domain: &domainReq{ID: &opts.DomainID},
|
||||||
}
|
}
|
||||||
} else if opts.DomainName != "" {
|
}
|
||||||
userRequest = userReq{
|
|
||||||
|
if userRequest == nil && opts.DomainName != "" {
|
||||||
|
userRequest = &userReq{
|
||||||
Name: &opts.Username,
|
Name: &opts.Username,
|
||||||
Domain: &domainReq{Name: &opts.DomainName},
|
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{
|
req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{
|
||||||
Name: &opts.ApplicationCredentialName,
|
Name: &opts.ApplicationCredentialName,
|
||||||
User: &userRequest,
|
User: userRequest,
|
||||||
Secret: &opts.ApplicationCredentialSecret,
|
Secret: &opts.ApplicationCredentialSecret,
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
collection of services. You will generally want to create one Provider
|
||||||
client per OpenStack cloud.
|
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
|
Use your OpenStack credentials to create a Provider client. The
|
||||||
IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in
|
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
|
information provided by the cloud operator. Additionally, the cloud may refer to
|
||||||
TenantID or TenantName as project_id and project_name. Credentials are
|
TenantID or TenantName as project_id and project_name. Credentials are
|
||||||
specified like so:
|
specified like so:
|
||||||
|
|
||||||
opts := gophercloud.AuthOptions{
|
opts := gophercloud.AuthOptions{
|
||||||
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||||
Username: "{username}",
|
Username: "{username}",
|
||||||
Password: "{password}",
|
Password: "{password}",
|
||||||
TenantID: "{tenant_id}",
|
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
|
You may also use the openstack.AuthOptionsFromEnv() helper function. This
|
||||||
function reads in standard environment variables frequently found in an
|
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
|
Compute, Object Storage, Block Storage. In order to define one, you need to
|
||||||
pass in the parent provider, like so:
|
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
|
Resources
|
||||||
|
|
||||||
Resource structs are the domain models that services make use of in order
|
Resource structs are the domain models that services make use of in order
|
||||||
to work with and represent the state of API resources:
|
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
|
Intermediate Result structs are returned for API operations, which allow
|
||||||
generic access to the HTTP headers, response body, and any errors associated
|
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
|
you must call the Extract method which is chained to the response, or an
|
||||||
Extract function from an applicable extension:
|
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
|
// Attempt to extract the disk configuration from the OS-DCF disk config
|
||||||
// extension:
|
// extension:
|
||||||
config, err := diskconfig.ExtractGet(result)
|
config, err := diskconfig.ExtractGet(result)
|
||||||
|
|
||||||
All requests that enumerate a collection return a Pager struct that is used to
|
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
|
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
|
extraction method from that request's package to interpret that Page as a slice
|
||||||
of results:
|
of results:
|
||||||
|
|
||||||
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
|
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
|
||||||
s, err := servers.ExtractServers(page)
|
s, err := servers.ExtractServers(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
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 "false" or an error to prematurely stop fetching new pages.
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
If you want to obtain the entire collection of pages without doing any
|
If you want to obtain the entire collection of pages without doing any
|
||||||
intermediary processing on each page, you can use the AllPages method:
|
intermediary processing on each page, you can use the AllPages method:
|
||||||
|
|
|
@ -122,6 +122,11 @@ type ErrDefault408 struct {
|
||||||
ErrUnexpectedResponseCode
|
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.
|
// ErrDefault429 is the default error type returned on a 429 HTTP response code.
|
||||||
type ErrDefault429 struct {
|
type ErrDefault429 struct {
|
||||||
ErrUnexpectedResponseCode
|
ErrUnexpectedResponseCode
|
||||||
|
@ -211,6 +216,12 @@ type Err408er interface {
|
||||||
Error408(ErrUnexpectedResponseCode) error
|
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
|
// Err429er is the interface resource error types implement to override the error message
|
||||||
// from a 429 error.
|
// from a 429 error.
|
||||||
type Err429er interface {
|
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.
|
settings found on the various OpenStack OS_* environment variables.
|
||||||
|
|
||||||
The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME,
|
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,
|
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
|
or an error will result. OS_PROJECT_ID, is optional.
|
||||||
OS_PROJECT_NAME are optional.
|
|
||||||
|
|
||||||
OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and
|
OS_TENANT_ID and OS_TENANT_NAME are deprecated forms of OS_PROJECT_ID and
|
||||||
OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will
|
OS_PROJECT_NAME and the latter are expected against a v3 auth api.
|
||||||
still be referred as "tenant" in Gophercloud.
|
|
||||||
|
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,
|
To use this function, first set the OS_* environment variables (for example,
|
||||||
by sourcing an `openrc` file), then:
|
by sourcing an `openrc` file), then:
|
||||||
|
@ -59,11 +63,14 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
return nilOptions, err
|
return nilOptions, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if username == "" && userID == "" {
|
if userID == "" && username == "" {
|
||||||
err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
// Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set
|
||||||
EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"},
|
if applicationCredentialID == "" && applicationCredentialSecret == "" {
|
||||||
|
err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||||
|
EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"},
|
||||||
|
}
|
||||||
|
return nilOptions, err
|
||||||
}
|
}
|
||||||
return nilOptions, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if password == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
if password == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
||||||
|
@ -80,6 +87,26 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
return nilOptions, err
|
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{
|
ao := gophercloud.AuthOptions{
|
||||||
IdentityEndpoint: authURL,
|
IdentityEndpoint: authURL,
|
||||||
UserID: userID,
|
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)
|
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil)
|
||||||
return
|
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
|
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.
|
// ReserveResult contains the response body and error from a Reserve request.
|
||||||
type ReserveResult struct {
|
type ReserveResult struct {
|
||||||
gophercloud.ErrResult
|
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"`
|
ImageID string `json:"imageRef,omitempty"`
|
||||||
// The associated volume type
|
// The associated volume type
|
||||||
VolumeType string `json:"volume_type,omitempty"`
|
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
|
// 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
|
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.
|
// Delete will delete the existing Volume with the provided ID.
|
||||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) {
|
||||||
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +175,8 @@ type UpdateOptsBuilder interface {
|
||||||
// to the volumes.Update function. For more information about the parameters, see
|
// to the volumes.Update function. For more information about the parameters, see
|
||||||
// the Volume object.
|
// the Volume object.
|
||||||
type UpdateOpts struct {
|
type UpdateOpts struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,8 @@ type Volume struct {
|
||||||
ConsistencyGroupID string `json:"consistencygroup_id"`
|
ConsistencyGroupID string `json:"consistencygroup_id"`
|
||||||
// Multiattach denotes if the volume is multi-attach capable.
|
// Multiattach denotes if the volume is multi-attach capable.
|
||||||
Multiattach bool `json:"multiattach"`
|
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
|
// UnmarshalJSON another unmarshalling function
|
||||||
|
|
|
@ -135,7 +135,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
||||||
|
|
||||||
result := tokens2.Create(v2Client, v2Opts)
|
result := tokens2.Create(v2Client, v2Opts)
|
||||||
|
|
||||||
token, err := result.ExtractToken()
|
err = client.SetTokenAndAuthResult(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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`,
|
// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
|
||||||
// this should retry authentication only once
|
// this should retry authentication only once
|
||||||
tac := *client
|
tac := *client
|
||||||
|
tac.SetThrowaway(true)
|
||||||
tac.ReauthFunc = nil
|
tac.ReauthFunc = nil
|
||||||
tac.TokenID = ""
|
tac.SetTokenAndAuthResult(nil)
|
||||||
tao := options
|
tao := options
|
||||||
tao.AllowReauth = false
|
tao.AllowReauth = false
|
||||||
client.ReauthFunc = func() error {
|
client.ReauthFunc = func() error {
|
||||||
|
@ -159,11 +160,10 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
client.TokenID = tac.TokenID
|
client.CopyTokenFrom(&tac)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.TokenID = token.ID
|
|
||||||
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||||
return V2EndpointURL(catalog, opts)
|
return V2EndpointURL(catalog, opts)
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
||||||
|
|
||||||
result := tokens3.Create(v3Client, opts)
|
result := tokens3.Create(v3Client, opts)
|
||||||
|
|
||||||
token, err := result.ExtractToken()
|
err = client.SetTokenAndAuthResult(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -199,15 +199,14 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
client.TokenID = token.ID
|
|
||||||
|
|
||||||
if opts.CanReauth() {
|
if opts.CanReauth() {
|
||||||
// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
|
// 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`,
|
// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
|
||||||
// this should retry authentication only once
|
// this should retry authentication only once
|
||||||
tac := *client
|
tac := *client
|
||||||
|
tac.SetThrowaway(true)
|
||||||
tac.ReauthFunc = nil
|
tac.ReauthFunc = nil
|
||||||
tac.TokenID = ""
|
tac.SetTokenAndAuthResult(nil)
|
||||||
var tao tokens3.AuthOptionsBuilder
|
var tao tokens3.AuthOptionsBuilder
|
||||||
switch ot := opts.(type) {
|
switch ot := opts.(type) {
|
||||||
case *gophercloud.AuthOptions:
|
case *gophercloud.AuthOptions:
|
||||||
|
@ -226,7 +225,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
client.TokenID = tac.TokenID
|
client.CopyTokenFrom(&tac)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,6 +304,18 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO
|
||||||
return sc, nil
|
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
|
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1
|
||||||
// object storage package.
|
// object storage package.
|
||||||
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
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
|
return
|
||||||
}
|
}
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
OkCodes: []int{200},
|
OkCodes: []int{200, 201},
|
||||||
})
|
})
|
||||||
return
|
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
|
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.
|
// result into a Flavor.
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
_, 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 specifies an IPv4 address for the instance.
|
||||||
AccessIPv4 string `json:"accessIPv4,omitempty"`
|
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"`
|
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
|
// ServiceClient will allow calls to be made to retrieve an image or
|
||||||
// flavor ID by name.
|
// flavor ID by name.
|
||||||
ServiceClient *gophercloud.ServiceClient `json:"-"`
|
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
|
// 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
|
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
|
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"`
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
// Description is the description of the tenant.
|
// 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 sets the tenant status to enabled or disabled.
|
||||||
Enabled *bool `json:"enabled,omitempty"`
|
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
|
}, 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
|
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||||
// with the user's Token.
|
// with the user's Token.
|
||||||
func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
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},
|
OkCodes: []int{200, 203},
|
||||||
})
|
})
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
r.Err = err
|
|
||||||
r.Header = resp.Header
|
r.Header = resp.Header
|
||||||
}
|
}
|
||||||
|
r.Err = err
|
||||||
return
|
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
|
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
|
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
|
||||||
// with the user's Token.
|
// with the user's Token.
|
||||||
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
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.
|
// UpdateOp represents a valid update operation.
|
||||||
type UpdateOp string
|
type UpdateOp string
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ Example to Disassociate a Floating IP with a Port
|
||||||
fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
|
||||||
|
|
||||||
updateOpts := floatingips.UpdateOpts{
|
updateOpts := floatingips.UpdateOpts{
|
||||||
PortID: nil,
|
PortID: new(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract()
|
fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract()
|
||||||
|
|
|
@ -5,6 +5,12 @@ import (
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
"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
|
// 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 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
|
// 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.
|
// either `asc' or `desc'. Marker and Limit are used for pagination.
|
||||||
type ListOpts struct {
|
type ListOpts struct {
|
||||||
ID string `q:"id"`
|
ID string `q:"id"`
|
||||||
|
Description string `q:"description"`
|
||||||
FloatingNetworkID string `q:"floating_network_id"`
|
FloatingNetworkID string `q:"floating_network_id"`
|
||||||
PortID string `q:"port_id"`
|
PortID string `q:"port_id"`
|
||||||
FixedIP string `q:"fixed_ip_address"`
|
FixedIP string `q:"fixed_ip_address"`
|
||||||
|
@ -24,18 +31,31 @@ type ListOpts struct {
|
||||||
SortDir string `q:"sort_dir"`
|
SortDir string `q:"sort_dir"`
|
||||||
RouterID string `q:"router_id"`
|
RouterID string `q:"router_id"`
|
||||||
Status string `q:"status"`
|
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
|
// 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
|
// floating IP resources. It accepts a ListOpts struct, which allows you to
|
||||||
// filter and sort the returned collection for greater efficiency.
|
// filter and sort the returned collection for greater efficiency.
|
||||||
func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
|
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
q, err := gophercloud.BuildQueryString(&opts)
|
url := rootURL(c)
|
||||||
if err != nil {
|
if opts != nil {
|
||||||
return pagination.Pager{Err: err}
|
query, err := opts.ToFloatingIPListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
}
|
}
|
||||||
u := rootURL(c) + q.String()
|
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
|
||||||
return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}}
|
return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -50,6 +70,7 @@ type CreateOptsBuilder interface {
|
||||||
// resource. The only required fields are FloatingNetworkID and PortID which
|
// resource. The only required fields are FloatingNetworkID and PortID which
|
||||||
// refer to the external network and internal port respectively.
|
// refer to the external network and internal port respectively.
|
||||||
type CreateOpts struct {
|
type CreateOpts struct {
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
FloatingNetworkID string `json:"floating_network_id" required:"true"`
|
FloatingNetworkID string `json:"floating_network_id" required:"true"`
|
||||||
FloatingIP string `json:"floating_ip_address,omitempty"`
|
FloatingIP string `json:"floating_ip_address,omitempty"`
|
||||||
PortID string `json:"port_id,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
|
// 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.
|
// ID. To disassociate the floating IP from all ports, provide an empty string.
|
||||||
type UpdateOpts struct {
|
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
|
// ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
|
||||||
// interface
|
// interface
|
||||||
func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) {
|
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
|
// 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 is the unique identifier for the floating IP instance.
|
||||||
ID string `json:"id"`
|
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
|
// FloatingNetworkID is the UUID of the external network where the floating
|
||||||
// IP is to be created.
|
// IP is to be created.
|
||||||
FloatingNetworkID string `json:"floating_network_id"`
|
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 is the ID of the router used for this floating IP.
|
||||||
RouterID string `json:"router_id"`
|
RouterID string `json:"router_id"`
|
||||||
|
|
||||||
|
// Tags optionally set via extensions/attributestags
|
||||||
|
Tags []string `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type commonResult struct {
|
type commonResult struct {
|
||||||
|
@ -50,11 +56,13 @@ type commonResult struct {
|
||||||
|
|
||||||
// Extract will extract a FloatingIP resource from a result.
|
// Extract will extract a FloatingIP resource from a result.
|
||||||
func (r commonResult) Extract() (*FloatingIP, error) {
|
func (r commonResult) Extract() (*FloatingIP, error) {
|
||||||
var s struct {
|
var s FloatingIP
|
||||||
FloatingIP *FloatingIP `json:"floatingip"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
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
|
// 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)
|
err := (r.(FloatingIPPage)).ExtractInto(&s)
|
||||||
return s.FloatingIPs, err
|
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"
|
networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78"
|
||||||
|
|
||||||
|
name := "new_name"
|
||||||
updateOpts := networks.UpdateOpts{
|
updateOpts := networks.UpdateOpts{
|
||||||
Name: "new_name",
|
Name: &name,
|
||||||
}
|
}
|
||||||
|
|
||||||
network, err := networks.Update(networkClient, networkID, updateOpts).Extract()
|
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 {
|
type ListOpts struct {
|
||||||
Status string `q:"status"`
|
Status string `q:"status"`
|
||||||
Name string `q:"name"`
|
Name string `q:"name"`
|
||||||
|
Description string `q:"description"`
|
||||||
AdminStateUp *bool `q:"admin_state_up"`
|
AdminStateUp *bool `q:"admin_state_up"`
|
||||||
TenantID string `q:"tenant_id"`
|
TenantID string `q:"tenant_id"`
|
||||||
ProjectID string `q:"project_id"`
|
ProjectID string `q:"project_id"`
|
||||||
|
@ -28,6 +29,10 @@ type ListOpts struct {
|
||||||
Limit int `q:"limit"`
|
Limit int `q:"limit"`
|
||||||
SortKey string `q:"sort_key"`
|
SortKey string `q:"sort_key"`
|
||||||
SortDir string `q:"sort_dir"`
|
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.
|
// ToNetworkListQuery formats a ListOpts into a query string.
|
||||||
|
@ -69,6 +74,7 @@ type CreateOptsBuilder interface {
|
||||||
type CreateOpts struct {
|
type CreateOpts struct {
|
||||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
Shared *bool `json:"shared,omitempty"`
|
Shared *bool `json:"shared,omitempty"`
|
||||||
TenantID string `json:"tenant_id,omitempty"`
|
TenantID string `json:"tenant_id,omitempty"`
|
||||||
ProjectID string `json:"project_id,omitempty"`
|
ProjectID string `json:"project_id,omitempty"`
|
||||||
|
@ -105,9 +111,10 @@ type UpdateOptsBuilder interface {
|
||||||
|
|
||||||
// UpdateOpts represents options used to update a network.
|
// UpdateOpts represents options used to update a network.
|
||||||
type UpdateOpts struct {
|
type UpdateOpts struct {
|
||||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
Shared *bool `json:"shared,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
Shared *bool `json:"shared,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToNetworkUpdateMap builds a request body from UpdateOpts.
|
// ToNetworkUpdateMap builds a request body from UpdateOpts.
|
||||||
|
|
|
@ -52,6 +52,9 @@ type Network struct {
|
||||||
// Human-readable name for the network. Might not be unique.
|
// Human-readable name for the network. Might not be unique.
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Description for the network
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
// The administrative state of network. If false (down), the network does not
|
// The administrative state of network. If false (down), the network does not
|
||||||
// forward packets.
|
// forward packets.
|
||||||
AdminStateUp bool `json:"admin_state_up"`
|
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.
|
// Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others.
|
||||||
// Used to make network resources highly available.
|
// Used to make network resources highly available.
|
||||||
AvailabilityZoneHints []string `json:"availability_zone_hints"`
|
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
|
// 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
|
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 v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
|
||||||
if zero {
|
if zero {
|
||||||
//fmt.Printf("value before change: %+v\n", optsValue.Field(i))
|
//fmt.Printf("value before change: %+v\n", optsValue.Field(i))
|
||||||
|
|
|
@ -2,7 +2,9 @@ package gophercloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -71,26 +73,44 @@ type ProviderClient struct {
|
||||||
// authentication functions for different Identity service versions.
|
// authentication functions for different Identity service versions.
|
||||||
ReauthFunc func() error
|
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
|
mut *sync.RWMutex
|
||||||
|
|
||||||
|
// reauthmut is a mutex for reauthentication it attempts to ensure that only one reauthentication
|
||||||
|
// attempt happens at one time.
|
||||||
reauthmut *reauthlock
|
reauthmut *reauthlock
|
||||||
|
|
||||||
|
authResult AuthResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reauthlock represents a set of attributes used to help in the reauthentication process.
|
||||||
type reauthlock struct {
|
type reauthlock struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
reauthing bool
|
reauthing bool
|
||||||
|
reauthingErr error
|
||||||
|
done *sync.Cond
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthenticatedHeaders returns a map of HTTP headers that are common for all
|
// 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) {
|
func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) {
|
||||||
|
if client.IsThrowaway() {
|
||||||
|
return
|
||||||
|
}
|
||||||
if client.reauthmut != nil {
|
if client.reauthmut != nil {
|
||||||
client.reauthmut.RLock()
|
client.reauthmut.Lock()
|
||||||
if client.reauthmut.reauthing {
|
for client.reauthmut.reauthing {
|
||||||
client.reauthmut.RUnlock()
|
client.reauthmut.done.Wait()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
client.reauthmut.RUnlock()
|
client.reauthmut.Unlock()
|
||||||
}
|
}
|
||||||
t := client.Token()
|
t := client.Token()
|
||||||
if t == "" {
|
if t == "" {
|
||||||
|
@ -106,6 +126,20 @@ func (client *ProviderClient) UseTokenLock() {
|
||||||
client.reauthmut = new(reauthlock)
|
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
|
// 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
|
// call this method to access the token instead of the TokenID field
|
||||||
func (client *ProviderClient) Token() string {
|
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
|
// 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) {
|
func (client *ProviderClient) SetToken(t string) {
|
||||||
if client.mut != nil {
|
if client.mut != nil {
|
||||||
client.mut.Lock()
|
client.mut.Lock()
|
||||||
defer client.mut.Unlock()
|
defer client.mut.Unlock()
|
||||||
}
|
}
|
||||||
client.TokenID = t
|
client.TokenID = t
|
||||||
|
client.authResult = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is
|
// SetTokenAndAuthResult safely sets the value of the auth token in the
|
||||||
//called because of a 401 response, the caller may pass the previous token. In
|
// ProviderClient and also records the AuthResult that was returned from the
|
||||||
//this case, the reauthentication can be skipped if another thread has already
|
// token creation request. Applications may call this in a custom ReauthFunc.
|
||||||
//reauthenticated in the meantime. If no previous token is known, an empty
|
func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error {
|
||||||
//string should be passed instead to force unconditional reauthentication.
|
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) {
|
func (client *ProviderClient) Reauthenticate(previousToken string) (err error) {
|
||||||
if client.ReauthFunc == nil {
|
if client.ReauthFunc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.mut == nil {
|
if client.reauthmut == nil {
|
||||||
return client.ReauthFunc()
|
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.Lock()
|
||||||
client.reauthmut.reauthing = true
|
client.reauthmut.reauthing = true
|
||||||
|
client.reauthmut.done = sync.NewCond(client.reauthmut)
|
||||||
|
client.reauthmut.reauthingErr = nil
|
||||||
client.reauthmut.Unlock()
|
client.reauthmut.Unlock()
|
||||||
|
|
||||||
if previousToken == "" || client.TokenID == previousToken {
|
if previousToken == "" || client.TokenID == previousToken {
|
||||||
|
@ -152,6 +255,8 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) {
|
||||||
|
|
||||||
client.reauthmut.Lock()
|
client.reauthmut.Lock()
|
||||||
client.reauthmut.reauthing = false
|
client.reauthmut.reauthing = false
|
||||||
|
client.reauthmut.reauthingErr = err
|
||||||
|
client.reauthmut.done.Broadcast()
|
||||||
client.reauthmut.Unlock()
|
client.reauthmut.Unlock()
|
||||||
return
|
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.
|
// io.ReadSeeker as-is. Default the content-type to application/json.
|
||||||
if options.JSONBody != nil {
|
if options.JSONBody != nil {
|
||||||
if options.RawBody != 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)
|
rendered, err := json.Marshal(options.JSONBody)
|
||||||
|
@ -213,6 +318,9 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to
|
||||||
// modify or omit any header.
|
// 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
|
// Allow default OkCodes if none explicitly set
|
||||||
if options.OkCodes == nil {
|
okc := options.OkCodes
|
||||||
options.OkCodes = defaultOkCodes(method)
|
if okc == nil {
|
||||||
|
okc = defaultOkCodes(method)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the HTTP response status.
|
// Validate the HTTP response status.
|
||||||
var ok bool
|
var ok bool
|
||||||
for _, code := range options.OkCodes {
|
for _, code := range okc {
|
||||||
if resp.StatusCode == code {
|
if resp.StatusCode == code {
|
||||||
ok = true
|
ok = true
|
||||||
break
|
break
|
||||||
|
@ -295,11 +404,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
||||||
seeker.Seek(0, 0)
|
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)
|
resp, err = client.Request(method, url, options)
|
||||||
client.ReauthFunc = reauthFunc
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case *ErrUnexpectedResponseCode:
|
case *ErrUnexpectedResponseCode:
|
||||||
|
@ -338,6 +443,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts)
|
||||||
if error408er, ok := errType.(Err408er); ok {
|
if error408er, ok := errType.(Err408er); ok {
|
||||||
err = error408er.Error408(respErr)
|
err = error408er.Error408(respErr)
|
||||||
}
|
}
|
||||||
|
case http.StatusConflict:
|
||||||
|
err = ErrDefault409{respErr}
|
||||||
|
if error409er, ok := errType.(Err409er); ok {
|
||||||
|
err = error409er.Error409(respErr)
|
||||||
|
}
|
||||||
case 429:
|
case 429:
|
||||||
err = ErrDefault429{respErr}
|
err = ErrDefault429{respErr}
|
||||||
if error429er, ok := errType.(Err429er); ok {
|
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 {
|
if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
|
||||||
newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0)
|
newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0)
|
||||||
|
|
||||||
for _, v := range m[label].([]interface{}) {
|
if mSlice, ok := m[label].([]interface{}); ok {
|
||||||
// For each iteration of the slice, we create a new struct.
|
for _, v := range mSlice {
|
||||||
// This is to work around a bug where elements of a slice
|
// For each iteration of the slice, we create a new struct.
|
||||||
// are reused and not overwritten when the same copy of the
|
// This is to work around a bug where elements of a slice
|
||||||
// struct is used:
|
// 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://github.com/golang/go/issues/21092
|
||||||
// https://play.golang.org/p/NHo3ywlPZli
|
// https://github.com/golang/go/issues/24155
|
||||||
newType := reflect.New(typeOfV).Elem()
|
// https://play.golang.org/p/NHo3ywlPZli
|
||||||
|
newType := reflect.New(typeOfV).Elem()
|
||||||
|
|
||||||
b, err := json.Marshal(v)
|
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// "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
|
opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion
|
||||||
case "volume":
|
case "volume":
|
||||||
opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion
|
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 != "" {
|
if client.Type != "" {
|
||||||
|
|
|
@ -196,7 +196,7 @@ github.com/google/shlex
|
||||||
github.com/google/uuid
|
github.com/google/uuid
|
||||||
# github.com/googleapis/gax-go/v2 v2.0.3
|
# github.com/googleapis/gax-go/v2 v2.0.3
|
||||||
github.com/googleapis/gax-go/v2
|
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
|
||||||
github.com/gophercloud/gophercloud/openstack
|
github.com/gophercloud/gophercloud/openstack
|
||||||
github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions
|
github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions
|
||||||
|
|
Loading…
Reference in New Issue