Update dependencies
This commit is contained in:
parent
bd344f9d73
commit
533c059378
14
go.mod
14
go.mod
|
@ -4,7 +4,6 @@ require (
|
||||||
cloud.google.com/go v0.45.1
|
cloud.google.com/go v0.45.1
|
||||||
github.com/Azure/azure-sdk-for-go v36.2.0+incompatible
|
github.com/Azure/azure-sdk-for-go v36.2.0+incompatible
|
||||||
github.com/Azure/go-autorest/autorest v0.9.2
|
github.com/Azure/go-autorest/autorest v0.9.2
|
||||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292 // indirect
|
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
||||||
github.com/agext/levenshtein v1.2.2
|
github.com/agext/levenshtein v1.2.2
|
||||||
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 // indirect
|
github.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6 // indirect
|
||||||
|
@ -43,8 +42,8 @@ require (
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||||
github.com/google/go-cmp v0.3.1
|
github.com/google/go-cmp v0.3.1
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968
|
github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5
|
||||||
github.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01 // indirect
|
github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
|
||||||
github.com/gorilla/websocket v1.4.0 // indirect
|
github.com/gorilla/websocket v1.4.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
|
||||||
|
@ -116,7 +115,6 @@ require (
|
||||||
github.com/spf13/afero v1.2.1
|
github.com/spf13/afero v1.2.1
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible
|
github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c
|
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c
|
||||||
github.com/terraform-providers/terraform-provider-openstack v1.15.0
|
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.1+incompatible // indirect
|
||||||
|
@ -128,16 +126,16 @@ require (
|
||||||
go.uber.org/atomic v1.3.2 // indirect
|
go.uber.org/atomic v1.3.2 // indirect
|
||||||
go.uber.org/multierr v1.1.0 // indirect
|
go.uber.org/multierr v1.1.0 // indirect
|
||||||
go.uber.org/zap v1.9.1 // indirect
|
go.uber.org/zap v1.9.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e
|
||||||
golang.org/x/mod v0.2.0
|
golang.org/x/mod v0.2.0
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
|
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371
|
||||||
google.golang.org/api v0.9.0
|
google.golang.org/api v0.9.0
|
||||||
google.golang.org/grpc v1.21.1
|
google.golang.org/grpc v1.21.1
|
||||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
gopkg.in/ini.v1 v1.42.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
gopkg.in/yaml.v2 v2.2.7
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
28
go.sum
28
go.sum
|
@ -42,8 +42,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
|
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
|
||||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292 h1:tuQ7w+my8a8mkwN7x2TSd7OzTjkZ7rAeSyH4xncuAMI=
|
|
||||||
github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
|
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||||
|
@ -181,10 +179,11 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968 h1:Pu+HW4kcQozw0QyrTTgLE+3RXNqFhQNNzhbnoLFL83c=
|
github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM=
|
||||||
github.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5 h1:Ciwp7ro4LyptUOkili/TX/ecuYr7vGtEIFnOOOKUjD8=
|
||||||
github.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01 h1:OgCNGSnEalfkRpn//WGJHhpo7fkP+LhTpvEITZ7CkK4=
|
github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
||||||
github.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=
|
github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d h1:fduaPzWwIfvOMLuHk2Al3GZH0XbUqG8MbElPop+Igzs=
|
||||||
|
github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
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=
|
||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
|
@ -422,8 +421,6 @@ github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible h1:5Td2b0yfaOvw
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c h1:iRD1CqtWUjgEVEmjwTMbP1DMzz1HRytOsgx/rlw/vNs=
|
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c h1:iRD1CqtWUjgEVEmjwTMbP1DMzz1HRytOsgx/rlw/vNs=
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
|
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
|
||||||
github.com/terraform-providers/terraform-provider-openstack v1.15.0 h1:adpjqej+F8BAX9dHmuPF47sUIkgifeqBu6p7iCsyj0Y=
|
|
||||||
github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=
|
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ=
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ=
|
||||||
|
@ -465,6 +462,8 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU=
|
||||||
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
@ -493,6 +492,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb h1:TR699M2v0qoKTOHxeLgp6zPqaQNs74f01a/ob9W0qko=
|
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb h1:TR699M2v0qoKTOHxeLgp6zPqaQNs74f01a/ob9W0qko=
|
||||||
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -505,6 +505,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -522,8 +523,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
@ -544,6 +545,8 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1j
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 h1:Cjq6sG3gnKDchzWy7ouGQklhxMtWvh4AhSNJ0qGIeo4=
|
||||||
|
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
@ -577,6 +580,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
@ -585,6 +590,9 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.iml
|
|
|
@ -1,13 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- tip
|
|
||||||
|
|
||||||
install: go get -v -t
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
- u@gogs.io
|
|
|
@ -1,191 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and
|
|
||||||
distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
|
||||||
owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
|
||||||
that control, are controlled by, or are under common control with that entity.
|
|
||||||
For the purposes of this definition, "control" means (i) the power, direct or
|
|
||||||
indirect, to cause the direction or management of such entity, whether by
|
|
||||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
|
||||||
permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications, including
|
|
||||||
but not limited to software source code, documentation source, and configuration
|
|
||||||
files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or
|
|
||||||
translation of a Source form, including but not limited to compiled object code,
|
|
||||||
generated documentation, and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
|
||||||
available under the License, as indicated by a copyright notice that is included
|
|
||||||
in or attached to the work (an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
|
||||||
is based on (or derived from) the Work and for which the editorial revisions,
|
|
||||||
annotations, elaborations, or other modifications represent, as a whole, an
|
|
||||||
original work of authorship. For the purposes of this License, Derivative Works
|
|
||||||
shall not include works that remain separable from, or merely link (or bind by
|
|
||||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including the original version
|
|
||||||
of the Work and any modifications or additions to that Work or Derivative Works
|
|
||||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
|
||||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
|
||||||
on behalf of the copyright owner. For the purposes of this definition,
|
|
||||||
"submitted" means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems, and
|
|
||||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
|
||||||
the purpose of discussing and improving the Work, but excluding communication
|
|
||||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
|
||||||
owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
|
||||||
of whom a Contribution has been received by Licensor and subsequently
|
|
||||||
incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby
|
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
|
||||||
Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License.
|
|
||||||
|
|
||||||
Subject to the terms and conditions of this License, each Contributor hereby
|
|
||||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
|
||||||
irrevocable (except as stated in this section) patent license to make, have
|
|
||||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
|
||||||
such license applies only to those patent claims licensable by such Contributor
|
|
||||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
|
||||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
|
||||||
submitted. If You institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
|
||||||
Contribution incorporated within the Work constitutes direct or contributory
|
|
||||||
patent infringement, then any patent licenses granted to You under this License
|
|
||||||
for that Work shall terminate as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution.
|
|
||||||
|
|
||||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
|
||||||
in any medium, with or without modifications, and in Source or Object form,
|
|
||||||
provided that You meet the following conditions:
|
|
||||||
|
|
||||||
You must give any other recipients of the Work or Derivative Works a copy of
|
|
||||||
this License; and
|
|
||||||
You must cause any modified files to carry prominent notices stating that You
|
|
||||||
changed the files; and
|
|
||||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
|
||||||
all copyright, patent, trademark, and attribution notices from the Source form
|
|
||||||
of the Work, excluding those notices that do not pertain to any part of the
|
|
||||||
Derivative Works; and
|
|
||||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
|
||||||
Derivative Works that You distribute must include a readable copy of the
|
|
||||||
attribution notices contained within such NOTICE file, excluding those notices
|
|
||||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
|
||||||
following places: within a NOTICE text file distributed as part of the
|
|
||||||
Derivative Works; within the Source form or documentation, if provided along
|
|
||||||
with the Derivative Works; or, within a display generated by the Derivative
|
|
||||||
Works, if and wherever such third-party notices normally appear. The contents of
|
|
||||||
the NOTICE file are for informational purposes only and do not modify the
|
|
||||||
License. You may add Your own attribution notices within Derivative Works that
|
|
||||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
|
||||||
provided that such additional attribution notices cannot be construed as
|
|
||||||
modifying the License.
|
|
||||||
You may add Your own copyright statement to Your modifications and may provide
|
|
||||||
additional or different license terms and conditions for use, reproduction, or
|
|
||||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
|
||||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
|
||||||
with the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions.
|
|
||||||
|
|
||||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
|
||||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
|
||||||
conditions of this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
|
||||||
any separate license agreement you may have executed with Licensor regarding
|
|
||||||
such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks.
|
|
||||||
|
|
||||||
This License does not grant permission to use the trade names, trademarks,
|
|
||||||
service marks, or product names of the Licensor, except as required for
|
|
||||||
reasonable and customary use in describing the origin of the Work and
|
|
||||||
reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty.
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
|
||||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
|
||||||
including, without limitation, any warranties or conditions of TITLE,
|
|
||||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
|
||||||
solely responsible for determining the appropriateness of using or
|
|
||||||
redistributing the Work and assume any risks associated with Your exercise of
|
|
||||||
permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability.
|
|
||||||
|
|
||||||
In no event and under no legal theory, whether in tort (including negligence),
|
|
||||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
|
||||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special, incidental,
|
|
||||||
or consequential damages of any character arising as a result of this License or
|
|
||||||
out of the use or inability to use the Work (including but not limited to
|
|
||||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
|
||||||
any and all other commercial damages or losses), even if such Contributor has
|
|
||||||
been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability.
|
|
||||||
|
|
||||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
|
||||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
|
||||||
other liability obligations and/or rights consistent with this License. However,
|
|
||||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
|
||||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
|
||||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason of your
|
|
||||||
accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate
|
|
||||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
|
||||||
identifying information. (Don't include the brackets!) The text should be
|
|
||||||
enclosed in the appropriate comment syntax for the file format. We also
|
|
||||||
recommend that a file or class name and description of purpose be included on
|
|
||||||
the same "printed page" as the copyright notice for easier identification within
|
|
||||||
third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
|
@ -1,20 +0,0 @@
|
||||||
Common Functions
|
|
||||||
================
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/Unknwon/com.svg)](https://travis-ci.org/Unknwon/com) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/Unknwon/com)
|
|
||||||
|
|
||||||
This is an open source project for commonly used functions for the Go programming language.
|
|
||||||
|
|
||||||
This package need >= **go 1.2**
|
|
||||||
|
|
||||||
Code Convention: based on [Go Code Convention](https://github.com/Unknwon/go-code-convention).
|
|
||||||
|
|
||||||
## Contribute
|
|
||||||
|
|
||||||
Your contribute is welcome, but you have to check following steps after you added some functions and commit them:
|
|
||||||
|
|
||||||
1. Make sure you wrote user-friendly comments for **all functions** .
|
|
||||||
2. Make sure you wrote test cases with any possible condition for **all functions** in file `*_test.go`.
|
|
||||||
3. Make sure you wrote benchmarks for **all functions** in file `*_test.go`.
|
|
||||||
4. Make sure you wrote useful examples for **all functions** in file `example_test.go`.
|
|
||||||
5. Make sure you ran `go test` and got **PASS** .
|
|
|
@ -1,161 +0,0 @@
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
// Package com is an open source project for commonly used functions for the Go programming language.
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExecCmdDirBytes executes system command in given directory
|
|
||||||
// and return stdout, stderr in bytes type, along with possible error.
|
|
||||||
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
|
|
||||||
bufOut := new(bytes.Buffer)
|
|
||||||
bufErr := new(bytes.Buffer)
|
|
||||||
|
|
||||||
cmd := exec.Command(cmdName, args...)
|
|
||||||
cmd.Dir = dir
|
|
||||||
cmd.Stdout = bufOut
|
|
||||||
cmd.Stderr = bufErr
|
|
||||||
|
|
||||||
err := cmd.Run()
|
|
||||||
return bufOut.Bytes(), bufErr.Bytes(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecCmdBytes executes system command
|
|
||||||
// and return stdout, stderr in bytes type, along with possible error.
|
|
||||||
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
|
|
||||||
return ExecCmdDirBytes("", cmdName, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecCmdDir executes system command in given directory
|
|
||||||
// and return stdout, stderr in string type, along with possible error.
|
|
||||||
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
|
|
||||||
bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
|
|
||||||
return string(bufOut), string(bufErr), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecCmd executes system command
|
|
||||||
// and return stdout, stderr in string type, along with possible error.
|
|
||||||
func ExecCmd(cmdName string, args ...string) (string, string, error) {
|
|
||||||
return ExecCmdDir("", cmdName, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// _________ .__ .____
|
|
||||||
// \_ ___ \ ____ | | ___________ | | ____ ____
|
|
||||||
// / \ \/ / _ \| | / _ \_ __ \ | | / _ \ / ___\
|
|
||||||
// \ \___( <_> ) |_( <_> ) | \/ | |__( <_> ) /_/ >
|
|
||||||
// \______ /\____/|____/\____/|__| |_______ \____/\___ /
|
|
||||||
// \/ \/ /_____/
|
|
||||||
|
|
||||||
// Color number constants.
|
|
||||||
const (
|
|
||||||
Gray = uint8(iota + 90)
|
|
||||||
Red
|
|
||||||
Green
|
|
||||||
Yellow
|
|
||||||
Blue
|
|
||||||
Magenta
|
|
||||||
//NRed = uint8(31) // Normal
|
|
||||||
EndColor = "\033[0m"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getColorLevel returns colored level string by given level.
|
|
||||||
func getColorLevel(level string) string {
|
|
||||||
level = strings.ToUpper(level)
|
|
||||||
switch level {
|
|
||||||
case "TRAC":
|
|
||||||
return fmt.Sprintf("\033[%dm%s\033[0m", Blue, level)
|
|
||||||
case "ERRO":
|
|
||||||
return fmt.Sprintf("\033[%dm%s\033[0m", Red, level)
|
|
||||||
case "WARN":
|
|
||||||
return fmt.Sprintf("\033[%dm%s\033[0m", Magenta, level)
|
|
||||||
case "SUCC":
|
|
||||||
return fmt.Sprintf("\033[%dm%s\033[0m", Green, level)
|
|
||||||
default:
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColorLogS colors log and return colored content.
|
|
||||||
// Log format: <level> <content [highlight][path]> [ error ].
|
|
||||||
// Level: TRAC -> blue; ERRO -> red; WARN -> Magenta; SUCC -> green; others -> default.
|
|
||||||
// Content: default; path: yellow; error -> red.
|
|
||||||
// Level has to be surrounded by "[" and "]".
|
|
||||||
// Highlights have to be surrounded by "# " and " #"(space), "#" will be deleted.
|
|
||||||
// Paths have to be surrounded by "( " and " )"(space).
|
|
||||||
// Errors have to be surrounded by "[ " and " ]"(space).
|
|
||||||
// Note: it hasn't support windows yet, contribute is welcome.
|
|
||||||
func ColorLogS(format string, a ...interface{}) string {
|
|
||||||
log := fmt.Sprintf(format, a...)
|
|
||||||
|
|
||||||
var clog string
|
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
// Level.
|
|
||||||
i := strings.Index(log, "]")
|
|
||||||
if log[0] == '[' && i > -1 {
|
|
||||||
clog += "[" + getColorLevel(log[1:i]) + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
log = log[i+1:]
|
|
||||||
|
|
||||||
// Error.
|
|
||||||
log = strings.Replace(log, "[ ", fmt.Sprintf("[\033[%dm", Red), -1)
|
|
||||||
log = strings.Replace(log, " ]", EndColor+"]", -1)
|
|
||||||
|
|
||||||
// Path.
|
|
||||||
log = strings.Replace(log, "( ", fmt.Sprintf("(\033[%dm", Yellow), -1)
|
|
||||||
log = strings.Replace(log, " )", EndColor+")", -1)
|
|
||||||
|
|
||||||
// Highlights.
|
|
||||||
log = strings.Replace(log, "# ", fmt.Sprintf("\033[%dm", Gray), -1)
|
|
||||||
log = strings.Replace(log, " #", EndColor, -1)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Level.
|
|
||||||
i := strings.Index(log, "]")
|
|
||||||
if log[0] == '[' && i > -1 {
|
|
||||||
clog += "[" + log[1:i] + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
log = log[i+1:]
|
|
||||||
|
|
||||||
// Error.
|
|
||||||
log = strings.Replace(log, "[ ", "[", -1)
|
|
||||||
log = strings.Replace(log, " ]", "]", -1)
|
|
||||||
|
|
||||||
// Path.
|
|
||||||
log = strings.Replace(log, "( ", "(", -1)
|
|
||||||
log = strings.Replace(log, " )", ")", -1)
|
|
||||||
|
|
||||||
// Highlights.
|
|
||||||
log = strings.Replace(log, "# ", "", -1)
|
|
||||||
log = strings.Replace(log, " #", "", -1)
|
|
||||||
}
|
|
||||||
return clog + log
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColorLog prints colored log to stdout.
|
|
||||||
// See color rules in function 'ColorLogS'.
|
|
||||||
func ColorLog(format string, a ...interface{}) {
|
|
||||||
fmt.Print(ColorLogS(format, a...))
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
// Copyright 2014 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Convert string to specify type.
|
|
||||||
type StrTo string
|
|
||||||
|
|
||||||
func (f StrTo) Exist() bool {
|
|
||||||
return string(f) != string(0x1E)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) Uint8() (uint8, error) {
|
|
||||||
v, err := strconv.ParseUint(f.String(), 10, 8)
|
|
||||||
return uint8(v), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) Int() (int, error) {
|
|
||||||
v, err := strconv.ParseInt(f.String(), 10, 0)
|
|
||||||
return int(v), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) Int64() (int64, error) {
|
|
||||||
v, err := strconv.ParseInt(f.String(), 10, 64)
|
|
||||||
return int64(v), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) MustUint8() uint8 {
|
|
||||||
v, _ := f.Uint8()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) MustInt() int {
|
|
||||||
v, _ := f.Int()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) MustInt64() int64 {
|
|
||||||
v, _ := f.Int64()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StrTo) String() string {
|
|
||||||
if f.Exist() {
|
|
||||||
return string(f)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert any type to string.
|
|
||||||
func ToStr(value interface{}, args ...int) (s string) {
|
|
||||||
switch v := value.(type) {
|
|
||||||
case bool:
|
|
||||||
s = strconv.FormatBool(v)
|
|
||||||
case float32:
|
|
||||||
s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
|
|
||||||
case float64:
|
|
||||||
s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
|
|
||||||
case int:
|
|
||||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
|
||||||
case int8:
|
|
||||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
|
||||||
case int16:
|
|
||||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
|
||||||
case int32:
|
|
||||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
|
||||||
case int64:
|
|
||||||
s = strconv.FormatInt(v, argInt(args).Get(0, 10))
|
|
||||||
case uint:
|
|
||||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
|
||||||
case uint8:
|
|
||||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
|
||||||
case uint16:
|
|
||||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
|
||||||
case uint32:
|
|
||||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
|
||||||
case uint64:
|
|
||||||
s = strconv.FormatUint(v, argInt(args).Get(0, 10))
|
|
||||||
case string:
|
|
||||||
s = v
|
|
||||||
case []byte:
|
|
||||||
s = string(v)
|
|
||||||
default:
|
|
||||||
s = fmt.Sprintf("%v", v)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type argInt []int
|
|
||||||
|
|
||||||
func (a argInt) Get(i int, args ...int) (r int) {
|
|
||||||
if i >= 0 && i < len(a) {
|
|
||||||
r = a[i]
|
|
||||||
} else if len(args) > 0 {
|
|
||||||
r = args[0]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HexStr2int converts hex format string to decimal number.
|
|
||||||
func HexStr2int(hexStr string) (int, error) {
|
|
||||||
num := 0
|
|
||||||
length := len(hexStr)
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
char := hexStr[length-i-1]
|
|
||||||
factor := -1
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case char >= '0' && char <= '9':
|
|
||||||
factor = int(char) - '0'
|
|
||||||
case char >= 'a' && char <= 'f':
|
|
||||||
factor = int(char) - 'a' + 10
|
|
||||||
default:
|
|
||||||
return -1, fmt.Errorf("invalid hex: %s", string(char))
|
|
||||||
}
|
|
||||||
|
|
||||||
num += factor * PowInt(16, i)
|
|
||||||
}
|
|
||||||
return num, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int2HexStr converts decimal number to hex format string.
|
|
||||||
func Int2HexStr(num int) (hex string) {
|
|
||||||
if num == 0 {
|
|
||||||
return "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
for num > 0 {
|
|
||||||
r := num % 16
|
|
||||||
|
|
||||||
c := "?"
|
|
||||||
if r >= 0 && r <= 9 {
|
|
||||||
c = string(r + '0')
|
|
||||||
} else {
|
|
||||||
c = string(r + 'a' - 10)
|
|
||||||
}
|
|
||||||
hex = c + hex
|
|
||||||
num = num / 16
|
|
||||||
}
|
|
||||||
return hex
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsDir returns true if given path is a directory,
|
|
||||||
// or returns false when it's a file or does not exist.
|
|
||||||
func IsDir(dir string) bool {
|
|
||||||
f, e := os.Stat(dir)
|
|
||||||
if e != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return f.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func statDir(dirPath, recPath string, includeDir, isDirOnly bool) ([]string, error) {
|
|
||||||
dir, err := os.Open(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer dir.Close()
|
|
||||||
|
|
||||||
fis, err := dir.Readdir(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
statList := make([]string, 0)
|
|
||||||
for _, fi := range fis {
|
|
||||||
if strings.Contains(fi.Name(), ".DS_Store") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
relPath := path.Join(recPath, fi.Name())
|
|
||||||
curPath := path.Join(dirPath, fi.Name())
|
|
||||||
if fi.IsDir() {
|
|
||||||
if includeDir {
|
|
||||||
statList = append(statList, relPath+"/")
|
|
||||||
}
|
|
||||||
s, err := statDir(curPath, relPath, includeDir, isDirOnly)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
statList = append(statList, s...)
|
|
||||||
} else if !isDirOnly {
|
|
||||||
statList = append(statList, relPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return statList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatDir gathers information of given directory by depth-first.
|
|
||||||
// It returns slice of file list and includes subdirectories if enabled;
|
|
||||||
// it returns error and nil slice when error occurs in underlying functions,
|
|
||||||
// or given path is not a directory or does not exist.
|
|
||||||
//
|
|
||||||
// Slice does not include given path itself.
|
|
||||||
// If subdirectories is enabled, they will have suffix '/'.
|
|
||||||
func StatDir(rootPath string, includeDir ...bool) ([]string, error) {
|
|
||||||
if !IsDir(rootPath) {
|
|
||||||
return nil, errors.New("not a directory or does not exist: " + rootPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
isIncludeDir := false
|
|
||||||
if len(includeDir) >= 1 {
|
|
||||||
isIncludeDir = includeDir[0]
|
|
||||||
}
|
|
||||||
return statDir(rootPath, "", isIncludeDir, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllSubDirs returns all subdirectories of given root path.
|
|
||||||
// Slice does not include given path itself.
|
|
||||||
func GetAllSubDirs(rootPath string) ([]string, error) {
|
|
||||||
if !IsDir(rootPath) {
|
|
||||||
return nil, errors.New("not a directory or does not exist: " + rootPath)
|
|
||||||
}
|
|
||||||
return statDir(rootPath, "", true, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFileListBySuffix returns an ordered list of file paths.
|
|
||||||
// It recognize if given path is a file, and don't do recursive find.
|
|
||||||
func GetFileListBySuffix(dirPath, suffix string) ([]string, error) {
|
|
||||||
if !IsExist(dirPath) {
|
|
||||||
return nil, fmt.Errorf("given path does not exist: %s", dirPath)
|
|
||||||
} else if IsFile(dirPath) {
|
|
||||||
return []string{dirPath}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given path is a directory.
|
|
||||||
dir, err := os.Open(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fis, err := dir.Readdir(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
files := make([]string, 0, len(fis))
|
|
||||||
for _, fi := range fis {
|
|
||||||
if strings.HasSuffix(fi.Name(), suffix) {
|
|
||||||
files = append(files, path.Join(dirPath, fi.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyDir copy files recursively from source to target directory.
|
|
||||||
//
|
|
||||||
// The filter accepts a function that process the path info.
|
|
||||||
// and should return true for need to filter.
|
|
||||||
//
|
|
||||||
// It returns error when error occurs in underlying functions.
|
|
||||||
func CopyDir(srcPath, destPath string, filters ...func(filePath string) bool) error {
|
|
||||||
// Check if target directory exists.
|
|
||||||
if IsExist(destPath) {
|
|
||||||
return errors.New("file or directory alreay exists: " + destPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := os.MkdirAll(destPath, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gather directory info.
|
|
||||||
infos, err := StatDir(srcPath, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var filter func(filePath string) bool
|
|
||||||
if len(filters) > 0 {
|
|
||||||
filter = filters[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, info := range infos {
|
|
||||||
if filter != nil && filter(info) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
curPath := path.Join(destPath, info)
|
|
||||||
if strings.HasSuffix(info, "/") {
|
|
||||||
err = os.MkdirAll(curPath, os.ModePerm)
|
|
||||||
} else {
|
|
||||||
err = Copy(path.Join(srcPath, info), curPath)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Storage unit constants.
|
|
||||||
const (
|
|
||||||
Byte = 1
|
|
||||||
KByte = Byte * 1024
|
|
||||||
MByte = KByte * 1024
|
|
||||||
GByte = MByte * 1024
|
|
||||||
TByte = GByte * 1024
|
|
||||||
PByte = TByte * 1024
|
|
||||||
EByte = PByte * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
func logn(n, b float64) float64 {
|
|
||||||
return math.Log(n) / math.Log(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func humanateBytes(s uint64, base float64, sizes []string) string {
|
|
||||||
if s < 10 {
|
|
||||||
return fmt.Sprintf("%dB", s)
|
|
||||||
}
|
|
||||||
e := math.Floor(logn(float64(s), base))
|
|
||||||
suffix := sizes[int(e)]
|
|
||||||
val := float64(s) / math.Pow(base, math.Floor(e))
|
|
||||||
f := "%.0f"
|
|
||||||
if val < 10 {
|
|
||||||
f = "%.1f"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(f+"%s", val, suffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HumaneFileSize calculates the file size and generate user-friendly string.
|
|
||||||
func HumaneFileSize(s uint64) string {
|
|
||||||
sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
|
|
||||||
return humanateBytes(s, 1024, sizes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileMTime returns file modified time and possible error.
|
|
||||||
func FileMTime(file string) (int64, error) {
|
|
||||||
f, err := os.Stat(file)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return f.ModTime().Unix(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileSize returns file size in bytes and possible error.
|
|
||||||
func FileSize(file string) (int64, error) {
|
|
||||||
f, err := os.Stat(file)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return f.Size(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy copies file from source to target path.
|
|
||||||
func Copy(src, dest string) error {
|
|
||||||
// Gather file information to set back later.
|
|
||||||
si, err := os.Lstat(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle symbolic link.
|
|
||||||
if si.Mode()&os.ModeSymlink != 0 {
|
|
||||||
target, err := os.Readlink(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
|
|
||||||
// which will lead "no such file or directory" error.
|
|
||||||
return os.Symlink(target, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
sr, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer sr.Close()
|
|
||||||
|
|
||||||
dw, err := os.Create(dest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer dw.Close()
|
|
||||||
|
|
||||||
if _, err = io.Copy(dw, sr); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set back file information.
|
|
||||||
if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Chmod(dest, si.Mode())
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFile writes data to a file named by filename.
|
|
||||||
// If the file does not exist, WriteFile creates it
|
|
||||||
// and its upper level paths.
|
|
||||||
func WriteFile(filename string, data []byte) error {
|
|
||||||
os.MkdirAll(path.Dir(filename), os.ModePerm)
|
|
||||||
return ioutil.WriteFile(filename, data, 0655)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFile returns true if given path is a file,
|
|
||||||
// or returns false when it's a directory or does not exist.
|
|
||||||
func IsFile(filePath string) bool {
|
|
||||||
f, e := os.Stat(filePath)
|
|
||||||
if e != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return !f.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExist checks whether a file or directory exists.
|
|
||||||
// It returns false when the file or directory does not exist.
|
|
||||||
func IsExist(path string) bool {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
return err == nil || os.IsExist(err)
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Html2JS converts []byte type of HTML content into JS format.
|
|
||||||
func Html2JS(data []byte) []byte {
|
|
||||||
s := string(data)
|
|
||||||
s = strings.Replace(s, `\`, `\\`, -1)
|
|
||||||
s = strings.Replace(s, "\n", `\n`, -1)
|
|
||||||
s = strings.Replace(s, "\r", "", -1)
|
|
||||||
s = strings.Replace(s, "\"", `\"`, -1)
|
|
||||||
s = strings.Replace(s, "<table>", "<table>", -1)
|
|
||||||
return []byte(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode html chars to string
|
|
||||||
func HtmlEncode(str string) string {
|
|
||||||
return html.EscapeString(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode string to html chars
|
|
||||||
func HtmlDecode(str string) string {
|
|
||||||
return html.UnescapeString(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip tags in html string
|
|
||||||
func StripTags(src string) string {
|
|
||||||
//去除style,script,html tag
|
|
||||||
re := regexp.MustCompile(`(?s)<(?:style|script)[^<>]*>.*?</(?:style|script)>|</?[a-z][a-z0-9]*[^<>]*>|<!--.*?-->`)
|
|
||||||
src = re.ReplaceAllString(src, "")
|
|
||||||
|
|
||||||
//trim all spaces(2+) into \n
|
|
||||||
re = regexp.MustCompile(`\s{2,}`)
|
|
||||||
src = re.ReplaceAllString(src, "\n")
|
|
||||||
|
|
||||||
return strings.TrimSpace(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// change \n to <br/>
|
|
||||||
func Nl2br(str string) string {
|
|
||||||
return strings.Replace(str, "\n", "<br/>", -1)
|
|
||||||
}
|
|
|
@ -1,201 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NotFoundError struct {
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e NotFoundError) Error() string {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
type RemoteError struct {
|
|
||||||
Host string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *RemoteError) Error() string {
|
|
||||||
return e.Err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36"
|
|
||||||
|
|
||||||
// HttpCall makes HTTP method call.
|
|
||||||
func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) {
|
|
||||||
req, err := http.NewRequest(method, url, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", UserAgent)
|
|
||||||
for k, vs := range header {
|
|
||||||
req.Header[k] = vs
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode == 200 {
|
|
||||||
return resp.Body, nil
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 {
|
|
||||||
err = fmt.Errorf("resource not found: %s", url)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpGet gets the specified resource.
|
|
||||||
// ErrNotFound is returned if the server responds with status 404.
|
|
||||||
func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) {
|
|
||||||
return HttpCall(client, "GET", url, header, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpPost posts the specified resource.
|
|
||||||
// ErrNotFound is returned if the server responds with status 404.
|
|
||||||
func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) {
|
|
||||||
return HttpCall(client, "POST", url, header, bytes.NewBuffer(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpGetToFile gets the specified resource and writes to file.
|
|
||||||
// ErrNotFound is returned if the server responds with status 404.
|
|
||||||
func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error {
|
|
||||||
rc, err := HttpGet(client, url, header)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
|
|
||||||
os.MkdirAll(path.Dir(fileName), os.ModePerm)
|
|
||||||
f, err := os.Create(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
_, err = io.Copy(f, rc)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpGetBytes gets the specified resource. ErrNotFound is returned if the server
|
|
||||||
// responds with status 404.
|
|
||||||
func HttpGetBytes(client *http.Client, url string, header http.Header) ([]byte, error) {
|
|
||||||
rc, err := HttpGet(client, url, header)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
return ioutil.ReadAll(rc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpGetJSON gets the specified resource and mapping to struct.
|
|
||||||
// ErrNotFound is returned if the server responds with status 404.
|
|
||||||
func HttpGetJSON(client *http.Client, url string, v interface{}) error {
|
|
||||||
rc, err := HttpGet(client, url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
err = json.NewDecoder(rc).Decode(v)
|
|
||||||
if _, ok := err.(*json.SyntaxError); ok {
|
|
||||||
return fmt.Errorf("JSON syntax error at %s", url)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpPostJSON posts the specified resource with struct values,
|
|
||||||
// and maps results to struct.
|
|
||||||
// ErrNotFound is returned if the server responds with status 404.
|
|
||||||
func HttpPostJSON(client *http.Client, url string, body, v interface{}) error {
|
|
||||||
data, err := json.Marshal(body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rc, err := HttpPost(client, url, http.Header{"content-type": []string{"application/json"}}, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
err = json.NewDecoder(rc).Decode(v)
|
|
||||||
if _, ok := err.(*json.SyntaxError); ok {
|
|
||||||
return fmt.Errorf("JSON syntax error at %s", url)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A RawFile describes a file that can be downloaded.
|
|
||||||
type RawFile interface {
|
|
||||||
Name() string
|
|
||||||
RawUrl() string
|
|
||||||
Data() []byte
|
|
||||||
SetData([]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FetchFiles fetches files specified by the rawURL field in parallel.
|
|
||||||
func FetchFiles(client *http.Client, files []RawFile, header http.Header) error {
|
|
||||||
ch := make(chan error, len(files))
|
|
||||||
for i := range files {
|
|
||||||
go func(i int) {
|
|
||||||
p, err := HttpGetBytes(client, files[i].RawUrl(), nil)
|
|
||||||
if err != nil {
|
|
||||||
ch <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
files[i].SetData(p)
|
|
||||||
ch <- nil
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
for _ = range files {
|
|
||||||
if err := <-ch; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FetchFiles uses command `curl` to fetch files specified by the rawURL field in parallel.
|
|
||||||
func FetchFilesCurl(files []RawFile, curlOptions ...string) error {
|
|
||||||
ch := make(chan error, len(files))
|
|
||||||
for i := range files {
|
|
||||||
go func(i int) {
|
|
||||||
stdout, _, err := ExecCmd("curl", append(curlOptions, files[i].RawUrl())...)
|
|
||||||
if err != nil {
|
|
||||||
ch <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
files[i].SetData([]byte(stdout))
|
|
||||||
ch <- nil
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
for _ = range files {
|
|
||||||
if err := <-ch; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2014 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
// PowInt is int type of math.Pow function.
|
|
||||||
func PowInt(x int, y int) int {
|
|
||||||
if y <= 0 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
if y % 2 == 0 {
|
|
||||||
sqrt := PowInt(x, y/2)
|
|
||||||
return sqrt * sqrt
|
|
||||||
} else {
|
|
||||||
return PowInt(x, y-1) * x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetGOPATHs returns all paths in GOPATH variable.
|
|
||||||
func GetGOPATHs() []string {
|
|
||||||
gopath := os.Getenv("GOPATH")
|
|
||||||
var paths []string
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
gopath = strings.Replace(gopath, "\\", "/", -1)
|
|
||||||
paths = strings.Split(gopath, ";")
|
|
||||||
} else {
|
|
||||||
paths = strings.Split(gopath, ":")
|
|
||||||
}
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSrcPath returns app. source code path.
|
|
||||||
// It only works when you have src. folder in GOPATH,
|
|
||||||
// it returns error not able to locate source folder path.
|
|
||||||
func GetSrcPath(importPath string) (appPath string, err error) {
|
|
||||||
paths := GetGOPATHs()
|
|
||||||
for _, p := range paths {
|
|
||||||
if IsExist(p + "/src/" + importPath + "/") {
|
|
||||||
appPath = p + "/src/" + importPath + "/"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(appPath) == 0 {
|
|
||||||
return "", errors.New("Unable to locate source folder path")
|
|
||||||
}
|
|
||||||
|
|
||||||
appPath = filepath.Dir(appPath) + "/"
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// Replace all '\' to '/'.
|
|
||||||
appPath = strings.Replace(appPath, "\\", "/", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return appPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HomeDir returns path of '~'(in Linux) on Windows,
|
|
||||||
// it returns error when the variable does not exist.
|
|
||||||
func HomeDir() (home string, err error) {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
home = os.Getenv("USERPROFILE")
|
|
||||||
if len(home) == 0 {
|
|
||||||
home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
home = os.Getenv("HOME")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(home) == 0 {
|
|
||||||
return "", errors.New("Cannot specify home directory because it's empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
return home, nil
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import "regexp"
|
|
||||||
|
|
||||||
const (
|
|
||||||
regex_email_pattern = `(?i)[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}`
|
|
||||||
regex_strict_email_pattern = `(?i)[A-Z0-9!#$%&'*+/=?^_{|}~-]+` +
|
|
||||||
`(?:\.[A-Z0-9!#$%&'*+/=?^_{|}~-]+)*` +
|
|
||||||
`@(?:[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?\.)+` +
|
|
||||||
`[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?`
|
|
||||||
regex_url_pattern = `(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
regex_email *regexp.Regexp
|
|
||||||
regex_strict_email *regexp.Regexp
|
|
||||||
regex_url *regexp.Regexp
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
regex_email = regexp.MustCompile(regex_email_pattern)
|
|
||||||
regex_strict_email = regexp.MustCompile(regex_strict_email_pattern)
|
|
||||||
regex_url = regexp.MustCompile(regex_url_pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate string is an email address, if not return false
|
|
||||||
// basically validation can match 99% cases
|
|
||||||
func IsEmail(email string) bool {
|
|
||||||
return regex_email.MatchString(email)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate string is an email address, if not return false
|
|
||||||
// this validation omits RFC 2822
|
|
||||||
func IsEmailRFC(email string) bool {
|
|
||||||
return regex_strict_email.MatchString(email)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate string is a url link, if not return false
|
|
||||||
// simple validation can match 99% cases
|
|
||||||
func IsUrl(url string) bool {
|
|
||||||
return regex_url.MatchString(url)
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AppendStr appends string to slice with no duplicates.
|
|
||||||
func AppendStr(strs []string, str string) []string {
|
|
||||||
for _, s := range strs {
|
|
||||||
if s == str {
|
|
||||||
return strs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return append(strs, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompareSliceStr compares two 'string' type slices.
|
|
||||||
// It returns true if elements and order are both the same.
|
|
||||||
func CompareSliceStr(s1, s2 []string) bool {
|
|
||||||
if len(s1) != len(s2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range s1 {
|
|
||||||
if s1[i] != s2[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompareSliceStr compares two 'string' type slices.
|
|
||||||
// It returns true if elements are the same, and ignores the order.
|
|
||||||
func CompareSliceStrU(s1, s2 []string) bool {
|
|
||||||
if len(s1) != len(s2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range s1 {
|
|
||||||
for j := len(s2) - 1; j >= 0; j-- {
|
|
||||||
if s1[i] == s2[j] {
|
|
||||||
s2 = append(s2[:j], s2[j+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(s2) > 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSliceContainsStr returns true if the string exists in given slice, ignore case.
|
|
||||||
func IsSliceContainsStr(sl []string, str string) bool {
|
|
||||||
str = strings.ToLower(str)
|
|
||||||
for _, s := range sl {
|
|
||||||
if strings.ToLower(s) == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSliceContainsInt64 returns true if the int64 exists in given slice.
|
|
||||||
func IsSliceContainsInt64(sl []int64, i int64) bool {
|
|
||||||
for _, s := range sl {
|
|
||||||
if s == i {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,243 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
r "math/rand"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AESEncrypt encrypts text and given key with AES.
|
|
||||||
func AESEncrypt(key, text []byte) ([]byte, error) {
|
|
||||||
block, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b := base64.StdEncoding.EncodeToString(text)
|
|
||||||
ciphertext := make([]byte, aes.BlockSize+len(b))
|
|
||||||
iv := ciphertext[:aes.BlockSize]
|
|
||||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cfb := cipher.NewCFBEncrypter(block, iv)
|
|
||||||
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
|
|
||||||
return ciphertext, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AESDecrypt decrypts text and given key with AES.
|
|
||||||
func AESDecrypt(key, text []byte) ([]byte, error) {
|
|
||||||
block, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(text) < aes.BlockSize {
|
|
||||||
return nil, errors.New("ciphertext too short")
|
|
||||||
}
|
|
||||||
iv := text[:aes.BlockSize]
|
|
||||||
text = text[aes.BlockSize:]
|
|
||||||
cfb := cipher.NewCFBDecrypter(block, iv)
|
|
||||||
cfb.XORKeyStream(text, text)
|
|
||||||
data, err := base64.StdEncoding.DecodeString(string(text))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLetter returns true if the 'l' is an English letter.
|
|
||||||
func IsLetter(l uint8) bool {
|
|
||||||
n := (l | 0x20) - 'a'
|
|
||||||
if n >= 0 && n < 26 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
|
|
||||||
func Expand(template string, match map[string]string, subs ...string) string {
|
|
||||||
var p []byte
|
|
||||||
var i int
|
|
||||||
for {
|
|
||||||
i = strings.Index(template, "{")
|
|
||||||
if i < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p = append(p, template[:i]...)
|
|
||||||
template = template[i+1:]
|
|
||||||
i = strings.Index(template, "}")
|
|
||||||
if s, ok := match[template[:i]]; ok {
|
|
||||||
p = append(p, s...)
|
|
||||||
} else {
|
|
||||||
j, _ := strconv.Atoi(template[:i])
|
|
||||||
if j >= len(subs) {
|
|
||||||
p = append(p, []byte("Missing")...)
|
|
||||||
} else {
|
|
||||||
p = append(p, subs[j]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template = template[i+1:]
|
|
||||||
}
|
|
||||||
p = append(p, template...)
|
|
||||||
return string(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse s string, support unicode
|
|
||||||
func Reverse(s string) string {
|
|
||||||
n := len(s)
|
|
||||||
runes := make([]rune, n)
|
|
||||||
for _, rune := range s {
|
|
||||||
n--
|
|
||||||
runes[n] = rune
|
|
||||||
}
|
|
||||||
return string(runes[n:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandomCreateBytes generate random []byte by specify chars.
|
|
||||||
func RandomCreateBytes(n int, alphabets ...byte) []byte {
|
|
||||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
||||||
var bytes = make([]byte, n)
|
|
||||||
var randby bool
|
|
||||||
if num, err := rand.Read(bytes); num != n || err != nil {
|
|
||||||
r.Seed(time.Now().UnixNano())
|
|
||||||
randby = true
|
|
||||||
}
|
|
||||||
for i, b := range bytes {
|
|
||||||
if len(alphabets) == 0 {
|
|
||||||
if randby {
|
|
||||||
bytes[i] = alphanum[r.Intn(len(alphanum))]
|
|
||||||
} else {
|
|
||||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if randby {
|
|
||||||
bytes[i] = alphabets[r.Intn(len(alphabets))]
|
|
||||||
} else {
|
|
||||||
bytes[i] = alphabets[b%byte(len(alphabets))]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnakeCase can convert all upper case characters in a string to
|
|
||||||
// underscore format.
|
|
||||||
//
|
|
||||||
// Some samples.
|
|
||||||
// "FirstName" => "first_name"
|
|
||||||
// "HTTPServer" => "http_server"
|
|
||||||
// "NoHTTPS" => "no_https"
|
|
||||||
// "GO_PATH" => "go_path"
|
|
||||||
// "GO PATH" => "go_path" // space is converted to underscore.
|
|
||||||
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
|
|
||||||
//
|
|
||||||
// From https://github.com/huandu/xstrings
|
|
||||||
func ToSnakeCase(str string) string {
|
|
||||||
if len(str) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
var prev, r0, r1 rune
|
|
||||||
var size int
|
|
||||||
|
|
||||||
r0 = '_'
|
|
||||||
|
|
||||||
for len(str) > 0 {
|
|
||||||
prev = r0
|
|
||||||
r0, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:]
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case r0 == utf8.RuneError:
|
|
||||||
buf.WriteByte(byte(str[0]))
|
|
||||||
|
|
||||||
case unicode.IsUpper(r0):
|
|
||||||
if prev != '_' {
|
|
||||||
buf.WriteRune('_')
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(unicode.ToLower(r0))
|
|
||||||
|
|
||||||
if len(str) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
r0, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:]
|
|
||||||
|
|
||||||
if !unicode.IsUpper(r0) {
|
|
||||||
buf.WriteRune(r0)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// find next non-upper-case character and insert `_` properly.
|
|
||||||
// it's designed to convert `HTTPServer` to `http_server`.
|
|
||||||
// if there are more than 2 adjacent upper case characters in a word,
|
|
||||||
// treat them as an abbreviation plus a normal word.
|
|
||||||
for len(str) > 0 {
|
|
||||||
r1 = r0
|
|
||||||
r0, size = utf8.DecodeRuneInString(str)
|
|
||||||
str = str[size:]
|
|
||||||
|
|
||||||
if r0 == utf8.RuneError {
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
buf.WriteByte(byte(str[0]))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unicode.IsUpper(r0) {
|
|
||||||
if r0 == '_' || r0 == ' ' || r0 == '-' {
|
|
||||||
r0 = '_'
|
|
||||||
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
} else {
|
|
||||||
buf.WriteRune('_')
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
buf.WriteRune(r0)
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(unicode.ToLower(r1))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(str) == 0 || r0 == '_' {
|
|
||||||
buf.WriteRune(unicode.ToLower(r0))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
if r0 == ' ' || r0 == '-' {
|
|
||||||
r0 = '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(r0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Format unix time int64 to string
|
|
||||||
func Date(ti int64, format string) string {
|
|
||||||
t := time.Unix(int64(ti), 0)
|
|
||||||
return DateT(t, format)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format unix time string to string
|
|
||||||
func DateS(ts string, format string) string {
|
|
||||||
i, _ := strconv.ParseInt(ts, 10, 64)
|
|
||||||
return Date(i, format)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format time.Time struct to string
|
|
||||||
// MM - month - 01
|
|
||||||
// M - month - 1, single bit
|
|
||||||
// DD - day - 02
|
|
||||||
// D - day 2
|
|
||||||
// YYYY - year - 2006
|
|
||||||
// YY - year - 06
|
|
||||||
// HH - 24 hours - 03
|
|
||||||
// H - 24 hours - 3
|
|
||||||
// hh - 12 hours - 03
|
|
||||||
// h - 12 hours - 3
|
|
||||||
// mm - minute - 04
|
|
||||||
// m - minute - 4
|
|
||||||
// ss - second - 05
|
|
||||||
// s - second = 5
|
|
||||||
func DateT(t time.Time, format string) string {
|
|
||||||
res := strings.Replace(format, "MM", t.Format("01"), -1)
|
|
||||||
res = strings.Replace(res, "M", t.Format("1"), -1)
|
|
||||||
res = strings.Replace(res, "DD", t.Format("02"), -1)
|
|
||||||
res = strings.Replace(res, "D", t.Format("2"), -1)
|
|
||||||
res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
|
|
||||||
res = strings.Replace(res, "YY", t.Format("06"), -1)
|
|
||||||
res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
|
|
||||||
res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
|
|
||||||
res = strings.Replace(res, "hh", t.Format("03"), -1)
|
|
||||||
res = strings.Replace(res, "h", t.Format("3"), -1)
|
|
||||||
res = strings.Replace(res, "mm", t.Format("04"), -1)
|
|
||||||
res = strings.Replace(res, "m", t.Format("4"), -1)
|
|
||||||
res = strings.Replace(res, "ss", t.Format("05"), -1)
|
|
||||||
res = strings.Replace(res, "s", t.Format("5"), -1)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// DateFormat pattern rules.
|
|
||||||
var datePatterns = []string{
|
|
||||||
// year
|
|
||||||
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
|
||||||
"y", "06", //A two digit representation of a year Examples: 99 or 03
|
|
||||||
|
|
||||||
// month
|
|
||||||
"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
|
|
||||||
"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
|
|
||||||
"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
|
|
||||||
"F", "January", // A full textual representation of a month, such as January or March January through December
|
|
||||||
|
|
||||||
// day
|
|
||||||
"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
|
|
||||||
"j", "2", // Day of the month without leading zeros 1 to 31
|
|
||||||
|
|
||||||
// week
|
|
||||||
"D", "Mon", // A textual representation of a day, three letters Mon through Sun
|
|
||||||
"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
|
|
||||||
|
|
||||||
// time
|
|
||||||
"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
|
|
||||||
"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
|
|
||||||
"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
|
|
||||||
"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
|
|
||||||
|
|
||||||
"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
|
|
||||||
"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
|
|
||||||
|
|
||||||
"i", "04", // Minutes with leading zeros 00 to 59
|
|
||||||
"s", "05", // Seconds, with leading zeros 00 through 59
|
|
||||||
|
|
||||||
// time zone
|
|
||||||
"T", "MST",
|
|
||||||
"P", "-07:00",
|
|
||||||
"O", "-0700",
|
|
||||||
|
|
||||||
// RFC 2822
|
|
||||||
"r", time.RFC1123Z,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse Date use PHP time format.
|
|
||||||
func DateParse(dateString, format string) (time.Time, error) {
|
|
||||||
replacer := strings.NewReplacer(datePatterns...)
|
|
||||||
format = replacer.Replace(format)
|
|
||||||
return time.ParseInLocation(format, dateString, time.Local)
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
// Copyright 2013 com authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package com
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
// url encode string, is + not %20
|
|
||||||
func UrlEncode(str string) string {
|
|
||||||
return url.QueryEscape(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// url decode string
|
|
||||||
func UrlDecode(str string) (string, error) {
|
|
||||||
return url.QueryUnescape(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// base64 encode
|
|
||||||
func Base64Encode(str string) string {
|
|
||||||
return base64.StdEncoding.EncodeToString([]byte(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
// base64 decode
|
|
||||||
func Base64Decode(str string) (string, error) {
|
|
||||||
s, e := base64.StdEncoding.DecodeString(str)
|
|
||||||
return string(s), e
|
|
||||||
}
|
|
|
@ -1,17 +1,20 @@
|
||||||
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.11"
|
||||||
|
- "1.12"
|
||||||
|
- "1.13"
|
||||||
- "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:
|
||||||
|
|
|
@ -12,15 +12,24 @@
|
||||||
description: |
|
description: |
|
||||||
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
|
||||||
|
nodeset: ubuntu-bionic
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: gophercloud-acceptance-test-queens
|
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
|
||||||
|
nodeset: ubuntu-bionic
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-stein
|
||||||
parent: gophercloud-acceptance-test
|
parent: gophercloud-acceptance-test
|
||||||
description: |
|
description: |
|
||||||
Run gophercloud acceptance test on queens branch
|
Run gophercloud acceptance test on stein branch
|
||||||
vars:
|
vars:
|
||||||
global_env:
|
global_env:
|
||||||
OS_BRANCH: stable/queens
|
OS_BRANCH: stable/stein
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: gophercloud-acceptance-test-rocky
|
name: gophercloud-acceptance-test-rocky
|
||||||
|
@ -31,6 +40,15 @@
|
||||||
global_env:
|
global_env:
|
||||||
OS_BRANCH: stable/rocky
|
OS_BRANCH: stable/rocky
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: gophercloud-acceptance-test-queens
|
||||||
|
parent: gophercloud-acceptance-test
|
||||||
|
description: |
|
||||||
|
Run gophercloud acceptance test on queens branch
|
||||||
|
vars:
|
||||||
|
global_env:
|
||||||
|
OS_BRANCH: stable/queens
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: gophercloud-acceptance-test-pike
|
name: gophercloud-acceptance-test-pike
|
||||||
parent: gophercloud-acceptance-test
|
parent: gophercloud-acceptance-test
|
||||||
|
@ -58,25 +76,13 @@
|
||||||
global_env:
|
global_env:
|
||||||
OS_BRANCH: stable/newton
|
OS_BRANCH: stable/newton
|
||||||
|
|
||||||
- job:
|
|
||||||
name: gophercloud-acceptance-test-mitaka
|
|
||||||
parent: gophercloud-acceptance-test
|
|
||||||
description: |
|
|
||||||
Run gophercloud acceptance test on mitaka branch
|
|
||||||
vars:
|
|
||||||
global_env:
|
|
||||||
OS_BRANCH: stable/mitaka
|
|
||||||
nodeset: ubuntu-trusty
|
|
||||||
|
|
||||||
- project:
|
- project:
|
||||||
name: gophercloud/gophercloud
|
name: gophercloud/gophercloud
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-unittest
|
- gophercloud-unittest
|
||||||
- gophercloud-acceptance-test
|
- gophercloud-acceptance-test
|
||||||
recheck-mitaka:
|
- gophercloud-acceptance-test-ironic
|
||||||
jobs:
|
|
||||||
- gophercloud-acceptance-test-mitaka
|
|
||||||
recheck-newton:
|
recheck-newton:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-acceptance-test-newton
|
- gophercloud-acceptance-test-newton
|
||||||
|
@ -92,7 +98,6 @@
|
||||||
recheck-rocky:
|
recheck-rocky:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-acceptance-test-rocky
|
- gophercloud-acceptance-test-rocky
|
||||||
periodic:
|
recheck-stein:
|
||||||
jobs:
|
jobs:
|
||||||
- gophercloud-unittest
|
- gophercloud-acceptance-test-stein
|
||||||
- gophercloud-acceptance-test
|
|
||||||
|
|
|
@ -0,0 +1,344 @@
|
||||||
|
## 0.11.0 (Unreleased)
|
||||||
|
|
||||||
|
UPGRADE NOTES
|
||||||
|
|
||||||
|
* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
|
||||||
|
* All responses now have access to the returned headers. Please report any issues this has caused [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942)
|
||||||
|
* Changes have been made to the internal HTTP client to ensure response bodies are handled in a way that enables connections to be re-used more efficiently [GH-1952](https://github.com/gophercloud/gophercloud/pull/1952)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `objectstorage/v1/containers.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
|
||||||
|
* Added `objectstorage/v1/objects.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
|
||||||
|
* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
|
||||||
|
* All responses now have access to the returned headers [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942)
|
||||||
|
* Added `compute/v2/extensions/injectnetworkinfo.InjectNetworkInfo` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941)
|
||||||
|
* Added `compute/v2/extensions/resetnetwork.ResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941)
|
||||||
|
* Added `identity/v3/extensions/trusts.ListRoles` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
|
||||||
|
* Added `identity/v3/extensions/trusts.GetRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
|
||||||
|
* Added `identity/v3/extensions/trusts.CheckRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
|
||||||
|
* Added `identity/v3/extensions/oauth1.Create` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.CreateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.DeleteConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.ListConsumers` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.GetConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.UpdateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.RequestToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.AuthorizeToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.CreateAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.GetAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.RevokeAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.ListAccessTokens` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.ListAccessTokenRoles` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
* Added `identity/v3/extensions/oauth1.GetAccessTokenRole` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
|
||||||
|
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
|
||||||
|
* Changed `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from `string` to `*time.Time` [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937)
|
||||||
|
|
||||||
|
## 0.10.0 (April 12, 2020)
|
||||||
|
|
||||||
|
UPGRADE NOTES
|
||||||
|
|
||||||
|
* The various `IDFromName` convenience functions have been moved to https://github.com/gophercloud/utils [GH-1897](https://github.com/gophercloud/gophercloud/pull/1897)
|
||||||
|
* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891)
|
||||||
|
* Added `blockstorage/extensions/backups.Export` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894)
|
||||||
|
* Added `blockstorage/extensions/backups.Import` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894)
|
||||||
|
* Added `placement/v1/resourceproviders.GetTraits` [GH-1899](https://github.com/gophercloud/gophercloud/pull/1899)
|
||||||
|
* Added the ability to authenticate with Amazon EC2 Credentials [GH-1900](https://github.com/gophercloud/gophercloud/pull/1900)
|
||||||
|
* Added ability to list Nova services by binary and host [GH-1904](https://github.com/gophercloud/gophercloud/pull/1904)
|
||||||
|
* Added `compute/v2/extensions/services.Update` [GH-1902](https://github.com/gophercloud/gophercloud/pull/1902)
|
||||||
|
* Added system scope to v3 authentication [GH-1908](https://github.com/gophercloud/gophercloud/pull/1908)
|
||||||
|
* Added `identity/v3/extensions/ec2tokens.ValidateS3Token` [GH-1906](https://github.com/gophercloud/gophercloud/pull/1906)
|
||||||
|
* Added `containerinfra/v1/clusters.Cluster.HealthStatus` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910)
|
||||||
|
* Added `containerinfra/v1/clusters.Cluster.HealthStatusReason` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910)
|
||||||
|
* Added `loadbalancer/v2/amphorae.Failover` [GH-1912](https://github.com/gophercloud/gophercloud/pull/1912)
|
||||||
|
* Added `identity/v3/extensions/ec2credentials.List` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
|
||||||
|
* Added `identity/v3/extensions/ec2credentials.Get` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
|
||||||
|
* Added `identity/v3/extensions/ec2credentials.Create` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
|
||||||
|
* Added `identity/v3/extensions/ec2credentials.Delete` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
|
||||||
|
* Added `ErrUnexpectedResponseCode.ResponseHeader` [GH-1919](https://github.com/gophercloud/gophercloud/pull/1919)
|
||||||
|
* Added support for TOTP authentication [GH-1922](https://github.com/gophercloud/gophercloud/pull/1922)
|
||||||
|
* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932)
|
||||||
|
* Added `sharedfilesystems/v2/shares.GetExportLocation` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932)
|
||||||
|
* Added `sharedfilesystems/v2/shares.Revert` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
|
||||||
|
* Added `sharedfilesystems/v2/shares.ResetStatus` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
|
||||||
|
* Added `sharedfilesystems/v2/shares.ForceDelete` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
|
||||||
|
* Added `sharedfilesystems/v2/shares.Unmanage` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
|
||||||
|
* Added `blockstorage/v3/attachments.Create` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
|
||||||
|
* Added `blockstorage/v3/attachments.List` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
|
||||||
|
* Added `blockstorage/v3/attachments.Get` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
|
||||||
|
* Added `blockstorage/v3/attachments.Update` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
|
||||||
|
* Added `blockstorage/v3/attachments.Delete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
|
||||||
|
* Added `blockstorage/v3/attachments.Complete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Fixed issue with Orchestration `get_file` only being able to read JSON and YAML files [GH-1915](https://github.com/gophercloud/gophercloud/pull/1915)
|
||||||
|
|
||||||
|
## 0.9.0 (March 10, 2020)
|
||||||
|
|
||||||
|
UPGRADE NOTES
|
||||||
|
|
||||||
|
* The way we implement new API result fields added by microversions has changed. Previously, we would declare a dedicated `ExtractFoo` function in a file called `microversions.go`. Now, we are declaring those fields inline of the original result struct as a pointer. [GH-1854](https://github.com/gophercloud/gophercloud/pull/1854)
|
||||||
|
|
||||||
|
* `compute/v2/servers.CreateOpts.Networks` has changed from `[]Network` to `interface{}` in order to support creating servers that have no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `compute/v2/extensions/instanceactions.List` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848)
|
||||||
|
* Added `compute/v2/extensions/instanceactions.Get` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848)
|
||||||
|
* Added `networking/v2/ports.List.FixedIPs` [GH-1849](https://github.com/gophercloud/gophercloud/pull/1849)
|
||||||
|
* Added `identity/v3/extensions/trusts.List` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855)
|
||||||
|
* Added `identity/v3/extensions/trusts.Get` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855)
|
||||||
|
* Added `identity/v3/extensions/trusts.Trust.ExpiresAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857)
|
||||||
|
* Added `identity/v3/extensions/trusts.Trust.DeletedAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857)
|
||||||
|
* Added `compute/v2/extensions/instanceactions.InstanceActionDetail` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851)
|
||||||
|
* Added `compute/v2/extensions/instanceactions.Event` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851)
|
||||||
|
* Added `compute/v2/extensions/instanceactions.ListOpts` [GH-1858](https://github.com/gophercloud/gophercloud/pull/1858)
|
||||||
|
* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864)
|
||||||
|
* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey2` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864)
|
||||||
|
* Added `placement/v1/resourceproviders.GetUsages` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862)
|
||||||
|
* Added `placement/v1/resourceproviders.GetInventories` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862)
|
||||||
|
* Added `imageservice/v2/images.ReplaceImageMinRam` [GH-1867](https://github.com/gophercloud/gophercloud/pull/1867)
|
||||||
|
* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865)
|
||||||
|
* Added `objectstorage/v1/containers.CreateOpts.TempURLKey2` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865)
|
||||||
|
* Added `blockstorage/extensions/volumetransfers.List` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
|
||||||
|
* Added `blockstorage/extensions/volumetransfers.Create` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
|
||||||
|
* Added `blockstorage/extensions/volumetransfers.Accept` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
|
||||||
|
* Added `blockstorage/extensions/volumetransfers.Get` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
|
||||||
|
* Added `blockstorage/extensions/volumetransfers.Delete` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
|
||||||
|
* Added `blockstorage/extensions/backups.RestoreFromBackup` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871)
|
||||||
|
* Added `blockstorage/v3/volumes.CreateOpts.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871)
|
||||||
|
* Added `blockstorage/v3/volumes.Volume.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871)
|
||||||
|
* Added `identity/v3/projects.ListOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Added `identity/v3/projects.ListOpts.TagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Added `identity/v3/projects.ListOpts.NotTags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Added `identity/v3/projects.ListOpts.NotTagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Added `identity/v3/projects.CreateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Added `identity/v3/projects.UpdateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Added `identity/v3/projects.Project.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
|
||||||
|
* Changed `compute/v2/servers.CreateOpts.Networks` from `[]Network` to `interface{}` to support creating servers with no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884)
|
||||||
|
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Added support for `int64` headers, which were previously being silently dropped [GH-1860](https://github.com/gophercloud/gophercloud/pull/1860)
|
||||||
|
* Allow image properties with empty values [GH-1875](https://github.com/gophercloud/gophercloud/pull/1875)
|
||||||
|
* Fixed `compute/v2/extensions/extendedserverattributes.ServerAttributesExt.Userdata` JSON tag [GH-1881](https://github.com/gophercloud/gophercloud/pull/1881)
|
||||||
|
|
||||||
|
## 0.8.0 (February 8, 2020)
|
||||||
|
|
||||||
|
UPGRADE NOTES
|
||||||
|
|
||||||
|
* The behavior of `keymanager/v1/acls.SetOpts` has changed. Instead of a struct, it is now `[]SetOpt`. See [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816) for implementation details.
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* The result of `containerinfra/v1/clusters.Resize` now returns only the UUID when calling `Extract`. This is a backwards-breaking change from the previous struct that was returned [GH-1649](https://github.com/gophercloud/gophercloud/pull/1649)
|
||||||
|
* Added `compute/v2/extensions/shelveunshelve.Shelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799)
|
||||||
|
* Added `compute/v2/extensions/shelveunshelve.ShelveOffload` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799)
|
||||||
|
* Added `compute/v2/extensions/shelveunshelve.Unshelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799)
|
||||||
|
* Added `containerinfra/v1/nodegroups.Get` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774)
|
||||||
|
* Added `containerinfra/v1/nodegroups.List` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774)
|
||||||
|
* Added `orchestration/v1/resourcetypes.List` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806)
|
||||||
|
* Added `orchestration/v1/resourcetypes.GetSchema` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806)
|
||||||
|
* Added `orchestration/v1/resourcetypes.GenerateTemplate` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806)
|
||||||
|
* Added `keymanager/v1/acls.SetOpt` and changed `keymanager/v1/acls.SetOpts` to `[]SetOpt` [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816)
|
||||||
|
* Added `blockstorage/apiversions.List` [GH-458](https://github.com/gophercloud/gophercloud/pull/458)
|
||||||
|
* Added `blockstorage/apiversions.Get` [GH-458](https://github.com/gophercloud/gophercloud/pull/458)
|
||||||
|
* Added `StatusCodeError` interface and `GetStatusCode` convenience method [GH-1820](https://github.com/gophercloud/gophercloud/pull/1820)
|
||||||
|
* Added pagination support to `compute/v2/extensions/usage.SingleTenant` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819)
|
||||||
|
* Added pagination support to `compute/v2/extensions/usage.AllTenants` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819)
|
||||||
|
* Added `placement/v1/resourceproviders.List` [GH-1815](https://github.com/gophercloud/gophercloud/pull/1815)
|
||||||
|
* Allow `CreateMemberOptsBuilder` to be passed in `loadbalancer/v2/pools.Create` [GH-1822](https://github.com/gophercloud/gophercloud/pull/1822)
|
||||||
|
* Added `Backup` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824)
|
||||||
|
* Added `MonitorAddress` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824)
|
||||||
|
* Added `MonitorPort` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824)
|
||||||
|
* Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818)
|
||||||
|
* Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835]
|
||||||
|
* Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842)
|
||||||
|
* Added `placement/v1/resourceproviders.Create` [GH-1841](https://github.com/gophercloud/gophercloud/pull/1841)
|
||||||
|
* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
|
||||||
|
* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
|
||||||
|
* Added `blockstorage/extensions/volumeactions.VolumeImage.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
|
||||||
|
* Added `blockstorage/extensions/volumeactions.VolumeImage.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Changed `sort_key` to `sort_keys` in ` workflow/v2/crontriggers.ListOpts` [GH-1809](https://github.com/gophercloud/gophercloud/pull/1809)
|
||||||
|
* Allow `blockstorage/extensions/schedulerstats.Capabilities.MaxOverSubscriptionRatio` to accept both string and int/float responses [GH-1817](https://github.com/gophercloud/gophercloud/pull/1817)
|
||||||
|
* Fixed bug in `NewLoadBalancerV2` for situations when the LBaaS service was advertised without a `/v2.0` endpoint [GH-1829](https://github.com/gophercloud/gophercloud/pull/1829)
|
||||||
|
* Fixed JSON tags in `baremetal/v1/ports.UpdateOperation` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840)
|
||||||
|
* Fixed JSON tags in `networking/v2/extensions/lbaas/vips.commonResult.Extract()` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840)
|
||||||
|
|
||||||
|
## 0.7.0 (December 3, 2019)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Allow a token to be used directly for authentication instead of generating a new token based on a given token [GH-1752](https://github.com/gophercloud/gophercloud/pull/1752)
|
||||||
|
* Moved `tags.ServerTagsExt` to servers.TagsExt` [GH-1760](https://github.com/gophercloud/gophercloud/pull/1760)
|
||||||
|
* Added `tags`, `tags-any`, `not-tags`, and `not-tags-any` to `compute/v2/servers.ListOpts` [GH-1759](https://github.com/gophercloud/gophercloud/pull/1759)
|
||||||
|
* Added `AccessRule` to `identity/v3/applicationcredentials` [GH-1758](https://github.com/gophercloud/gophercloud/pull/1758)
|
||||||
|
* Gophercloud no longer returns an error when multiple endpoints are found. Instead, it will choose the first endpoint and discard the others [GH-1766](https://github.com/gophercloud/gophercloud/pull/1766)
|
||||||
|
* Added `networking/v2/extensions/fwaas_v2/rules.Create` [GH-1768](https://github.com/gophercloud/gophercloud/pull/1768)
|
||||||
|
* Added `networking/v2/extensions/fwaas_v2/rules.Delete` [GH-1771](https://github.com/gophercloud/gophercloud/pull/1771)
|
||||||
|
* Added `loadbalancer/v2/providers.List` [GH-1765](https://github.com/gophercloud/gophercloud/pull/1765)
|
||||||
|
* Added `networking/v2/extensions/fwaas_v2/rules.Get` [GH-1772](https://github.com/gophercloud/gophercloud/pull/1772)
|
||||||
|
* Added `networking/v2/extensions/fwaas_v2/rules.Update` [GH-1776](https://github.com/gophercloud/gophercloud/pull/1776)
|
||||||
|
* Added `networking/v2/extensions/fwaas_v2/rules.List` [GH-1783](https://github.com/gophercloud/gophercloud/pull/1783)
|
||||||
|
* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.CreateOpts` [GH-1785](https://github.com/gophercloud/gophercloud/pull/1785)
|
||||||
|
* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.UpdateOpts` [GH-1786](https://github.com/gophercloud/gophercloud/pull/1786)
|
||||||
|
* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.Monitor` [GH-1787](https://github.com/gophercloud/gophercloud/pull/1787)
|
||||||
|
* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.ListOpts` [GH-1788](https://github.com/gophercloud/gophercloud/pull/1788)
|
||||||
|
* Updated `go.mod` dependencies, specifically to account for CVE-2019-11840 with `golang.org/x/crypto` [GH-1793](https://github.com/gophercloud/gophercloud/pull/1788)
|
||||||
|
|
||||||
|
## 0.6.0 (October 17, 2019)
|
||||||
|
|
||||||
|
UPGRADE NOTES
|
||||||
|
|
||||||
|
* The way reauthentication works has been refactored. This should not cause a problem, but please report bugs if it does. See [GH-1746](https://github.com/gophercloud/gophercloud/pull/1746) for more information.
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `networking/v2/extensions/quotas.Get` [GH-1742](https://github.com/gophercloud/gophercloud/pull/1742)
|
||||||
|
* Added `networking/v2/extensions/quotas.Update` [GH-1747](https://github.com/gophercloud/gophercloud/pull/1747)
|
||||||
|
* Refactored the reauthentication implementation to use goroutines and added a check to prevent an infinite loop in certain situations. [GH-1746](https://github.com/gophercloud/gophercloud/pull/1746)
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Changed `Flavor` to `FlavorID` in `loadbalancer/v2/loadbalancers` [GH-1744](https://github.com/gophercloud/gophercloud/pull/1744)
|
||||||
|
* Changed `Flavor` to `FlavorID` in `networking/v2/extensions/lbaas_v2/loadbalancers` [GH-1744](https://github.com/gophercloud/gophercloud/pull/1744)
|
||||||
|
* The `go-yaml` dependency was updated to `v2.2.4` to fix possible DDOS vulnerabilities [GH-1751](https://github.com/gophercloud/gophercloud/pull/1751)
|
||||||
|
|
||||||
|
## 0.5.0 (October 13, 2019)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `VolumeType` to `compute/v2/extensions/bootfromvolume.BlockDevice`[GH-1690](https://github.com/gophercloud/gophercloud/pull/1690)
|
||||||
|
* Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688](https://github.com/gophercloud/gophercloud/pull/1688)
|
||||||
|
* Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698](https://github.com/gophercloud/gophercloud/pull/1696)
|
||||||
|
* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696](https://github.com/gophercloud/gophercloud/pull/1696)
|
||||||
|
* Added `compute/v2/extensions/tags.Add` [GH-1696](https://github.com/gophercloud/gophercloud/pull/1696)
|
||||||
|
* Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703](https://github.com/gophercloud/gophercloud/pull/1703)
|
||||||
|
* Added `ExtractDomain` method to token results in `identity/v3/tokens` [GH-1712](https://github.com/gophercloud/gophercloud/pull/1712)
|
||||||
|
* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.CreateOpts` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710)
|
||||||
|
* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710)
|
||||||
|
* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.Listener` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710)
|
||||||
|
* Added `compute/v2/extensions/tags.Add` [GH-1695](https://github.com/gophercloud/gophercloud/pull/1695)
|
||||||
|
* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1694](https://github.com/gophercloud/gophercloud/pull/1694)
|
||||||
|
* Added `compute/v2/extensions/tags.Delete` [GH-1699](https://github.com/gophercloud/gophercloud/pull/1699)
|
||||||
|
* Added `compute/v2/extensions/tags.DeleteAll` [GH-1700](https://github.com/gophercloud/gophercloud/pull/1700)
|
||||||
|
* Added `ImageStatusImporting` as an image status [GH-1725](https://github.com/gophercloud/gophercloud/pull/1725)
|
||||||
|
* Added `ByPath` to `baremetalintrospection/v1/introspection.RootDiskType` [GH-1730](https://github.com/gophercloud/gophercloud/pull/1730)
|
||||||
|
* Added `AttachedVolumes` to `compute/v2/servers.Server` [GH-1732](https://github.com/gophercloud/gophercloud/pull/1732)
|
||||||
|
* Enable unmarshaling server tags to a `compute/v2/servers.Server` struct [GH-1734]
|
||||||
|
* Allow setting an empty members list in `loadbalancer/v2/pools.BatchUpdateMembers` [GH-1736](https://github.com/gophercloud/gophercloud/pull/1736)
|
||||||
|
* Allow unsetting members' subnet ID and name in `loadbalancer/v2/pools.BatchUpdateMemberOpts` [GH-1738](https://github.com/gophercloud/gophercloud/pull/1738)
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705](https://github.com/gophercloud/gophercloud/pull/1705)
|
||||||
|
* Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706](https://github.com/gophercloud/gophercloud/pull/1706)
|
||||||
|
* Fixed issue with `blockstorage/v1/volumes.Create` where the response was expected to be 202 [GH-1720](https://github.com/gophercloud/gophercloud/pull/1720)
|
||||||
|
* Changed `DefaultTlsContainerRef` from `string` to `*string` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723)
|
||||||
|
* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723)
|
||||||
|
* Changed `DefaultTlsContainerRef` from `string` to `*string` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723)
|
||||||
|
* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723)
|
||||||
|
|
||||||
|
|
||||||
|
## 0.4.0 (September 3, 2019)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `blockstorage/extensions/quotasets.results.QuotaSet.Groups` [GH-1668](https://github.com/gophercloud/gophercloud/pull/1668)
|
||||||
|
* Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668](https://github.com/gophercloud/gophercloud/pull/1668)
|
||||||
|
* Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674](https://github.com/gophercloud/gophercloud/pull/1674)
|
||||||
|
* Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676](https://github.com/gophercloud/gophercloud/pull/1676)
|
||||||
|
* Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677](https://github.com/gophercloud/gophercloud/pull/1677)
|
||||||
|
* Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681](https://github.com/gophercloud/gophercloud/pull/1681)
|
||||||
|
* Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651](https://github.com/gophercloud/gophercloud/pull/1651)
|
||||||
|
* Added `networking/v2/extensions/agents.ListDHCPNetworks` [GH-1686](https://github.com/gophercloud/gophercloud/pull/1686)
|
||||||
|
* Added `networking/v2/extensions/layer3/portforwarding.Delete` [GH-1652](https://github.com/gophercloud/gophercloud/pull/1652)
|
||||||
|
* Added `compute/v2/extensions/tags.List` [GH-1679](https://github.com/gophercloud/gophercloud/pull/1679)
|
||||||
|
* Added `compute/v2/extensions/tags.Check` [GH-1679](https://github.com/gophercloud/gophercloud/pull/1679)
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Changed `identity/v3/endpoints.ListOpts.RegionID` from `int` to `string` [GH-1664](https://github.com/gophercloud/gophercloud/pull/1664)
|
||||||
|
* Fixed issue where older time formats in some networking APIs/resources were unable to be parsed [GH-1671](https://github.com/gophercloud/gophercloud/pull/1664)
|
||||||
|
* Changed `SATA`, `SCSI`, and `SAS` types to `InterfaceType` in `baremetal/v1/nodes` [GH-1683]
|
||||||
|
|
||||||
|
## 0.3.0 (July 31, 2019)
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
* Added `baremetal/apiversions.List` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577)
|
||||||
|
* Added `baremetal/apiversions.Get` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577)
|
||||||
|
* Added `compute/v2/extensions/servergroups.CreateOpts.Policy` [GH-1636](https://github.com/gophercloud/gophercloud/pull/1636)
|
||||||
|
* Added `identity/v3/extensions/trusts.Create` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644)
|
||||||
|
* Added `identity/v3/extensions/trusts.Delete` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644)
|
||||||
|
* Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/layer3/floatingips.FloatingIP` [GH-1647](https://github.com/gophercloud/gophercloud/issues/1646)
|
||||||
|
* Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/security/groups.SecGroup` [GH-1654](https://github.com/gophercloud/gophercloud/issues/1654)
|
||||||
|
* Added `CreatedAt` and `UpdatedAt` to `networking/v2/networks.Network` [GH-1657](https://github.com/gophercloud/gophercloud/issues/1657)
|
||||||
|
* Added `keymanager/v1/containers.CreateSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659)
|
||||||
|
* Added `keymanager/v1/containers.DeleteSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659)
|
||||||
|
* Added `sharedfilesystems/v2/shares.GetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656)
|
||||||
|
* Added `sharedfilesystems/v2/shares.GetMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656)
|
||||||
|
* Added `sharedfilesystems/v2/shares.SetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656)
|
||||||
|
* Added `sharedfilesystems/v2/shares.UpdateMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656)
|
||||||
|
* Added `sharedfilesystems/v2/shares.DeleteMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656)
|
||||||
|
* Added `sharedfilesystems/v2/sharetypes.IDFromName` [GH-1662](https://github.com/gophercloud/gophercloud/issues/1662)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
* Changed `baremetal/v1/nodes.CleanStep.Args` from `map[string]string` to `map[string]interface{}` [GH-1638](https://github.com/gophercloud/gophercloud/pull/1638)
|
||||||
|
* Removed `URLPath` and `ExpectedCodes` from `loadbalancer/v2/monitors.ToMonitorCreateMap` since Octavia now provides default values when these fields are not specified [GH-1640](https://github.com/gophercloud/gophercloud/pull/1540)
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
|
@ -45,6 +45,9 @@ type AuthOptions struct {
|
||||||
|
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
|
||||||
|
// Passcode is used in TOTP authentication method
|
||||||
|
Passcode string `json:"passcode,omitempty"`
|
||||||
|
|
||||||
// At most one of DomainID and DomainName must be provided if using Username
|
// At most one of DomainID and DomainName must be provided if using Username
|
||||||
// with Identity V3. Otherwise, either are optional.
|
// with Identity V3. Otherwise, either are optional.
|
||||||
DomainID string `json:"-"`
|
DomainID string `json:"-"`
|
||||||
|
@ -98,6 +101,7 @@ type AuthScope struct {
|
||||||
ProjectName string
|
ProjectName string
|
||||||
DomainID string
|
DomainID string
|
||||||
DomainName string
|
DomainName string
|
||||||
|
System bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||||
|
@ -133,6 +137,8 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
|
||||||
return map[string]interface{}{"auth": authMap}, nil
|
return map[string]interface{}{"auth": authMap}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToTokenV3CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||||
|
// interface in the v3 tokens package
|
||||||
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
|
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
|
||||||
type domainReq struct {
|
type domainReq struct {
|
||||||
ID *string `json:"id,omitempty"`
|
ID *string `json:"id,omitempty"`
|
||||||
|
@ -148,7 +154,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
type userReq struct {
|
type userReq struct {
|
||||||
ID *string `json:"id,omitempty"`
|
ID *string `json:"id,omitempty"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password *string `json:"password,omitempty"`
|
||||||
|
Passcode *string `json:"passcode,omitempty"`
|
||||||
Domain *domainReq `json:"domain,omitempty"`
|
Domain *domainReq `json:"domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,11 +174,16 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
Secret *string `json:"secret,omitempty"`
|
Secret *string `json:"secret,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type totpReq struct {
|
||||||
|
User *userReq `json:"user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type identityReq struct {
|
type identityReq struct {
|
||||||
Methods []string `json:"methods"`
|
Methods []string `json:"methods"`
|
||||||
Password *passwordReq `json:"password,omitempty"`
|
Password *passwordReq `json:"password,omitempty"`
|
||||||
Token *tokenReq `json:"token,omitempty"`
|
Token *tokenReq `json:"token,omitempty"`
|
||||||
ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"`
|
ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"`
|
||||||
|
TOTP *totpReq `json:"totp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type authReq struct {
|
type authReq struct {
|
||||||
|
@ -186,7 +198,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
// if insufficient or incompatible information is present.
|
// if insufficient or incompatible information is present.
|
||||||
var req request
|
var req request
|
||||||
|
|
||||||
if opts.Password == "" {
|
if opts.Password == "" && opts.Passcode == "" {
|
||||||
if opts.TokenID != "" {
|
if opts.TokenID != "" {
|
||||||
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
|
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
|
||||||
// parameters.
|
// parameters.
|
||||||
|
@ -274,7 +286,14 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Password authentication.
|
// Password authentication.
|
||||||
req.Auth.Identity.Methods = []string{"password"}
|
if opts.Password != "" {
|
||||||
|
req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "password")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOTP authentication.
|
||||||
|
if opts.Passcode != "" {
|
||||||
|
req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "totp")
|
||||||
|
}
|
||||||
|
|
||||||
// At least one of Username and UserID must be specified.
|
// At least one of Username and UserID must be specified.
|
||||||
if opts.Username == "" && opts.UserID == "" {
|
if opts.Username == "" && opts.UserID == "" {
|
||||||
|
@ -298,23 +317,46 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the request for Username and Password authentication with a DomainID.
|
// Configure the request for Username and Password authentication with a DomainID.
|
||||||
req.Auth.Identity.Password = &passwordReq{
|
if opts.Password != "" {
|
||||||
User: userReq{
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
Name: &opts.Username,
|
User: userReq{
|
||||||
Password: opts.Password,
|
Name: &opts.Username,
|
||||||
Domain: &domainReq{ID: &opts.DomainID},
|
Password: &opts.Password,
|
||||||
},
|
Domain: &domainReq{ID: &opts.DomainID},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.Passcode != "" {
|
||||||
|
req.Auth.Identity.TOTP = &totpReq{
|
||||||
|
User: &userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Passcode: &opts.Passcode,
|
||||||
|
Domain: &domainReq{ID: &opts.DomainID},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.DomainName != "" {
|
if opts.DomainName != "" {
|
||||||
// Configure the request for Username and Password authentication with a DomainName.
|
// Configure the request for Username and Password authentication with a DomainName.
|
||||||
req.Auth.Identity.Password = &passwordReq{
|
if opts.Password != "" {
|
||||||
User: userReq{
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
Name: &opts.Username,
|
User: userReq{
|
||||||
Password: opts.Password,
|
Name: &opts.Username,
|
||||||
Domain: &domainReq{Name: &opts.DomainName},
|
Password: &opts.Password,
|
||||||
},
|
Domain: &domainReq{Name: &opts.DomainName},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Passcode != "" {
|
||||||
|
req.Auth.Identity.TOTP = &totpReq{
|
||||||
|
User: &userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Passcode: &opts.Passcode,
|
||||||
|
Domain: &domainReq{Name: &opts.DomainName},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,8 +371,22 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the request for UserID and Password authentication.
|
// Configure the request for UserID and Password authentication.
|
||||||
req.Auth.Identity.Password = &passwordReq{
|
if opts.Password != "" {
|
||||||
User: userReq{ID: &opts.UserID, Password: opts.Password},
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{
|
||||||
|
ID: &opts.UserID,
|
||||||
|
Password: &opts.Password,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Passcode != "" {
|
||||||
|
req.Auth.Identity.TOTP = &totpReq{
|
||||||
|
User: &userReq{
|
||||||
|
ID: &opts.UserID,
|
||||||
|
Passcode: &opts.Passcode,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,6 +403,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in
|
||||||
|
// the v3 tokens package.
|
||||||
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
// For backwards compatibility.
|
// For backwards compatibility.
|
||||||
// If AuthOptions.Scope was not set, try to determine it.
|
// If AuthOptions.Scope was not set, try to determine it.
|
||||||
|
@ -364,6 +422,14 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Scope.System {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"system": map[string]interface{}{
|
||||||
|
"all": true,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
if opts.Scope.ProjectName != "" {
|
if opts.Scope.ProjectName != "" {
|
||||||
// ProjectName provided: either DomainID or DomainName must also be supplied.
|
// ProjectName provided: either DomainID or DomainName must also be supplied.
|
||||||
// ProjectID may not be supplied.
|
// ProjectID may not be supplied.
|
||||||
|
@ -433,5 +499,16 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts AuthOptions) CanReauth() bool {
|
func (opts AuthOptions) CanReauth() bool {
|
||||||
|
if opts.Passcode != "" {
|
||||||
|
// cannot reauth using TOTP passcode
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return opts.AllowReauth
|
return opts.AllowReauth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||||
|
// interface in the v3 tokens package.
|
||||||
|
func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -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, err := 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:
|
||||||
|
|
|
@ -2,6 +2,7 @@ package gophercloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,11 +78,12 @@ func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string {
|
||||||
// those listed in OkCodes is encountered.
|
// those listed in OkCodes is encountered.
|
||||||
type ErrUnexpectedResponseCode struct {
|
type ErrUnexpectedResponseCode struct {
|
||||||
BaseError
|
BaseError
|
||||||
URL string
|
URL string
|
||||||
Method string
|
Method string
|
||||||
Expected []int
|
Expected []int
|
||||||
Actual int
|
Actual int
|
||||||
Body []byte
|
Body []byte
|
||||||
|
ResponseHeader http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrUnexpectedResponseCode) Error() string {
|
func (e ErrUnexpectedResponseCode) Error() string {
|
||||||
|
@ -92,6 +94,23 @@ func (e ErrUnexpectedResponseCode) Error() string {
|
||||||
return e.choseErrString()
|
return e.choseErrString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStatusCode returns the actual status code of the error.
|
||||||
|
func (e ErrUnexpectedResponseCode) GetStatusCode() int {
|
||||||
|
return e.Actual
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusCodeError is a convenience interface to easily allow access to the
|
||||||
|
// status code field of the various ErrDefault* types.
|
||||||
|
//
|
||||||
|
// By using this interface, you only have to make a single type cast of
|
||||||
|
// the returned error to err.(StatusCodeError) and then call GetStatusCode()
|
||||||
|
// instead of having a large switch statement checking for each of the
|
||||||
|
// ErrDefault* types.
|
||||||
|
type StatusCodeError interface {
|
||||||
|
Error() string
|
||||||
|
GetStatusCode() int
|
||||||
|
}
|
||||||
|
|
||||||
// ErrDefault400 is the default error type returned on a 400 HTTP response code.
|
// ErrDefault400 is the default error type returned on a 400 HTTP response code.
|
||||||
type ErrDefault400 struct {
|
type ErrDefault400 struct {
|
||||||
ErrUnexpectedResponseCode
|
ErrUnexpectedResponseCode
|
||||||
|
@ -122,6 +141,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 +235,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,13 @@
|
||||||
|
module github.com/gophercloud/gophercloud
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect
|
||||||
|
golang.org/x/text v0.3.2 // indirect
|
||||||
|
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.7
|
||||||
|
)
|
|
@ -0,0 +1,26 @@
|
||||||
|
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-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU=
|
||||||
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
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=
|
||||||
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -1 +0,0 @@
|
||||||
package internal
|
|
|
@ -1,34 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RemainingKeys will inspect a struct and compare it to a map. Any struct
|
|
||||||
// field that does not have a JSON tag that matches a key in the map or
|
|
||||||
// a matching lower-case field in the map will be returned as an extra.
|
|
||||||
//
|
|
||||||
// This is useful for determining the extra fields returned in response bodies
|
|
||||||
// for resources that can contain an arbitrary or dynamic number of fields.
|
|
||||||
func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) {
|
|
||||||
extras = make(map[string]interface{})
|
|
||||||
for k, v := range m {
|
|
||||||
extras[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
valueOf := reflect.ValueOf(s)
|
|
||||||
typeOf := reflect.TypeOf(s)
|
|
||||||
for i := 0; i < valueOf.NumField(); i++ {
|
|
||||||
field := typeOf.Field(i)
|
|
||||||
|
|
||||||
lowerField := strings.ToLower(field.Name)
|
|
||||||
delete(extras, lowerField)
|
|
||||||
|
|
||||||
if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" {
|
|
||||||
delete(extras, tagValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -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:
|
||||||
|
@ -34,6 +38,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
username := os.Getenv("OS_USERNAME")
|
username := os.Getenv("OS_USERNAME")
|
||||||
userID := os.Getenv("OS_USERID")
|
userID := os.Getenv("OS_USERID")
|
||||||
password := os.Getenv("OS_PASSWORD")
|
password := os.Getenv("OS_PASSWORD")
|
||||||
|
passcode := os.Getenv("OS_PASSCODE")
|
||||||
tenantID := os.Getenv("OS_TENANT_ID")
|
tenantID := os.Getenv("OS_TENANT_ID")
|
||||||
tenantName := os.Getenv("OS_TENANT_NAME")
|
tenantName := os.Getenv("OS_TENANT_NAME")
|
||||||
domainID := os.Getenv("OS_DOMAIN_ID")
|
domainID := os.Getenv("OS_DOMAIN_ID")
|
||||||
|
@ -69,8 +74,9 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if password == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" {
|
||||||
err := gophercloud.ErrMissingEnvironmentVariable{
|
err := gophercloud.ErrMissingEnvironmentVariable{
|
||||||
|
// silently ignore TOTP passcode warning, since it is not a common auth method
|
||||||
EnvironmentVariable: "OS_PASSWORD",
|
EnvironmentVariable: "OS_PASSWORD",
|
||||||
}
|
}
|
||||||
return nilOptions, err
|
return nilOptions, err
|
||||||
|
@ -83,6 +89,13 @@ 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 applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" {
|
||||||
if userID == "" && username == "" {
|
if userID == "" && username == "" {
|
||||||
return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{
|
||||||
|
@ -101,6 +114,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
|
Passcode: passcode,
|
||||||
TenantID: tenantID,
|
TenantID: tenantID,
|
||||||
TenantName: tenantName,
|
TenantName: tenantName,
|
||||||
DomainID: domainID,
|
DomainID: domainID,
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
Package volumeactions provides information and interaction with volumes in the
|
|
||||||
OpenStack Block Storage service. A volume is a detachable block storage
|
|
||||||
device, akin to a USB hard drive.
|
|
||||||
|
|
||||||
Example of Attaching a Volume to an Instance
|
|
||||||
|
|
||||||
attachOpts := volumeactions.AttachOpts{
|
|
||||||
MountPoint: "/mnt",
|
|
||||||
Mode: "rw",
|
|
||||||
InstanceUUID: server.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
detachOpts := volumeactions.DetachOpts{
|
|
||||||
AttachmentID: volume.Attachments[0].AttachmentID,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Example of Creating an Image from a Volume
|
|
||||||
|
|
||||||
uploadImageOpts := volumeactions.UploadImageOpts{
|
|
||||||
ImageName: "my_vol",
|
|
||||||
Force: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%+v\n", volumeImage)
|
|
||||||
|
|
||||||
Example of Extending a Volume's Size
|
|
||||||
|
|
||||||
extendOpts := volumeactions.ExtendSizeOpts{
|
|
||||||
NewSize: 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example of Initializing a Volume Connection
|
|
||||||
|
|
||||||
connectOpts := &volumeactions.InitializeConnectionOpts{
|
|
||||||
IP: "127.0.0.1",
|
|
||||||
Host: "stack",
|
|
||||||
Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
|
|
||||||
Multipath: gophercloud.Disabled,
|
|
||||||
Platform: "x86_64",
|
|
||||||
OSType: "linux2",
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%+v\n", connectionInfo["data"])
|
|
||||||
|
|
||||||
terminateOpts := &volumeactions.InitializeConnectionOpts{
|
|
||||||
IP: "127.0.0.1",
|
|
||||||
Host: "stack",
|
|
||||||
Initiator: "iqn.1994-05.com.redhat:17cf566367d2",
|
|
||||||
Multipath: gophercloud.Disabled,
|
|
||||||
Platform: "x86_64",
|
|
||||||
OSType: "linux2",
|
|
||||||
}
|
|
||||||
|
|
||||||
err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package volumeactions
|
|
|
@ -1,269 +0,0 @@
|
||||||
package volumeactions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AttachOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Attach request.
|
|
||||||
type AttachOptsBuilder interface {
|
|
||||||
ToVolumeAttachMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttachMode describes the attachment mode for volumes.
|
|
||||||
type AttachMode string
|
|
||||||
|
|
||||||
// These constants determine how a volume is attached.
|
|
||||||
const (
|
|
||||||
ReadOnly AttachMode = "ro"
|
|
||||||
ReadWrite AttachMode = "rw"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AttachOpts contains options for attaching a Volume.
|
|
||||||
type AttachOpts struct {
|
|
||||||
// The mountpoint of this volume.
|
|
||||||
MountPoint string `json:"mountpoint,omitempty"`
|
|
||||||
|
|
||||||
// The nova instance ID, can't set simultaneously with HostName.
|
|
||||||
InstanceUUID string `json:"instance_uuid,omitempty"`
|
|
||||||
|
|
||||||
// The hostname of baremetal host, can't set simultaneously with InstanceUUID.
|
|
||||||
HostName string `json:"host_name,omitempty"`
|
|
||||||
|
|
||||||
// Mount mode of this volume.
|
|
||||||
Mode AttachMode `json:"mode,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeAttachMap assembles a request body based on the contents of a
|
|
||||||
// AttachOpts.
|
|
||||||
func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "os-attach")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach will attach a volume based on the values in AttachOpts.
|
|
||||||
func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) {
|
|
||||||
b, err := opts.ToVolumeAttachMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginDetach will mark the volume as detaching.
|
|
||||||
func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) {
|
|
||||||
b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetachOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Detach request.
|
|
||||||
type DetachOptsBuilder interface {
|
|
||||||
ToVolumeDetachMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetachOpts contains options for detaching a Volume.
|
|
||||||
type DetachOpts struct {
|
|
||||||
// AttachmentID is the ID of the attachment between a volume and instance.
|
|
||||||
AttachmentID string `json:"attachment_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeDetachMap assembles a request body based on the contents of a
|
|
||||||
// DetachOpts.
|
|
||||||
func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "os-detach")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detach will detach a volume based on volume ID.
|
|
||||||
func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) {
|
|
||||||
b, err := opts.ToVolumeDetachMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve will reserve a volume based on volume ID.
|
|
||||||
func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) {
|
|
||||||
b := map[string]interface{}{"os-reserve": make(map[string]interface{})}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200, 201, 202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unreserve will unreserve a volume based on volume ID.
|
|
||||||
func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) {
|
|
||||||
b := map[string]interface{}{"os-unreserve": make(map[string]interface{})}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200, 201, 202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// InitializeConnection request.
|
|
||||||
type InitializeConnectionOptsBuilder interface {
|
|
||||||
ToVolumeInitializeConnectionMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitializeConnectionOpts hosts options for InitializeConnection.
|
|
||||||
// The fields are specific to the storage driver in use and the destination
|
|
||||||
// attachment.
|
|
||||||
type InitializeConnectionOpts struct {
|
|
||||||
IP string `json:"ip,omitempty"`
|
|
||||||
Host string `json:"host,omitempty"`
|
|
||||||
Initiator string `json:"initiator,omitempty"`
|
|
||||||
Wwpns []string `json:"wwpns,omitempty"`
|
|
||||||
Wwnns string `json:"wwnns,omitempty"`
|
|
||||||
Multipath *bool `json:"multipath,omitempty"`
|
|
||||||
Platform string `json:"platform,omitempty"`
|
|
||||||
OSType string `json:"os_type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a
|
|
||||||
// InitializeConnectionOpts.
|
|
||||||
func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) {
|
|
||||||
b, err := gophercloud.BuildRequestBody(opts, "connector")
|
|
||||||
return map[string]interface{}{"os-initialize_connection": b}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitializeConnection initializes an iSCSI connection by volume ID.
|
|
||||||
func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) {
|
|
||||||
b, err := opts.ToVolumeInitializeConnectionMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200, 201, 202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// TerminateConnection request.
|
|
||||||
type TerminateConnectionOptsBuilder interface {
|
|
||||||
ToVolumeTerminateConnectionMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerminateConnectionOpts hosts options for TerminateConnection.
|
|
||||||
type TerminateConnectionOpts struct {
|
|
||||||
IP string `json:"ip,omitempty"`
|
|
||||||
Host string `json:"host,omitempty"`
|
|
||||||
Initiator string `json:"initiator,omitempty"`
|
|
||||||
Wwpns []string `json:"wwpns,omitempty"`
|
|
||||||
Wwnns string `json:"wwnns,omitempty"`
|
|
||||||
Multipath *bool `json:"multipath,omitempty"`
|
|
||||||
Platform string `json:"platform,omitempty"`
|
|
||||||
OSType string `json:"os_type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a
|
|
||||||
// TerminateConnectionOpts.
|
|
||||||
func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) {
|
|
||||||
b, err := gophercloud.BuildRequestBody(opts, "connector")
|
|
||||||
return map[string]interface{}{"os-terminate_connection": b}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerminateConnection terminates an iSCSI connection by volume ID.
|
|
||||||
func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) {
|
|
||||||
b, err := opts.ToVolumeTerminateConnectionMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendSizeOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// ExtendSize request.
|
|
||||||
type ExtendSizeOptsBuilder interface {
|
|
||||||
ToVolumeExtendSizeMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendSizeOpts contains options for extending the size of an existing Volume.
|
|
||||||
// This object is passed to the volumes.ExtendSize function.
|
|
||||||
type ExtendSizeOpts struct {
|
|
||||||
// NewSize is the new size of the volume, in GB.
|
|
||||||
NewSize int `json:"new_size" required:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeExtendSizeMap assembles a request body based on the contents of an
|
|
||||||
// ExtendSizeOpts.
|
|
||||||
func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "os-extend")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendSize will extend the size of the volume based on the provided information.
|
|
||||||
// This operation does not return a response body.
|
|
||||||
func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) {
|
|
||||||
b, err := opts.ToVolumeExtendSizeMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadImageOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// UploadImage request.
|
|
||||||
type UploadImageOptsBuilder interface {
|
|
||||||
ToVolumeUploadImageMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadImageOpts contains options for uploading a Volume to image storage.
|
|
||||||
type UploadImageOpts struct {
|
|
||||||
// Container format, may be bare, ofv, ova, etc.
|
|
||||||
ContainerFormat string `json:"container_format,omitempty"`
|
|
||||||
|
|
||||||
// Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc.
|
|
||||||
DiskFormat string `json:"disk_format,omitempty"`
|
|
||||||
|
|
||||||
// The name of image that will be stored in glance.
|
|
||||||
ImageName string `json:"image_name,omitempty"`
|
|
||||||
|
|
||||||
// Force image creation, usable if volume attached to instance.
|
|
||||||
Force bool `json:"force,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeUploadImageMap assembles a request body based on the contents of a
|
|
||||||
// UploadImageOpts.
|
|
||||||
func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "os-volume_upload_image")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadImage will upload an image based on the values in UploadImageOptsBuilder.
|
|
||||||
func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) {
|
|
||||||
b, err := opts.ToVolumeUploadImageMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForceDelete will delete the volume regardless of state.
|
|
||||||
func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) {
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
package volumeactions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AttachResult contains the response body and error from an Attach request.
|
|
||||||
type AttachResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginDetachingResult contains the response body and error from a BeginDetach
|
|
||||||
// request.
|
|
||||||
type BeginDetachingResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetachResult contains the response body and error from a Detach request.
|
|
||||||
type DetachResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// UploadImageResult contains the response body and error from an UploadImage
|
|
||||||
// request.
|
|
||||||
type UploadImageResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReserveResult contains the response body and error from a Reserve request.
|
|
||||||
type ReserveResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnreserveResult contains the response body and error from an Unreserve
|
|
||||||
// request.
|
|
||||||
type UnreserveResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerminateConnectionResult contains the response body and error from a
|
|
||||||
// TerminateConnection request.
|
|
||||||
type TerminateConnectionResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitializeConnectionResult contains the response body and error from an
|
|
||||||
// InitializeConnection request.
|
|
||||||
type InitializeConnectionResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendSizeResult contains the response body and error from an ExtendSize request.
|
|
||||||
type ExtendSizeResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get the connection information out of the
|
|
||||||
// InitializeConnectionResult object.
|
|
||||||
//
|
|
||||||
// This will be a generic map[string]interface{} and the results will be
|
|
||||||
// dependent on the type of connection made.
|
|
||||||
func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) {
|
|
||||||
var s struct {
|
|
||||||
ConnectionInfo map[string]interface{} `json:"connection_info"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.ConnectionInfo, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageVolumeType contains volume type information obtained from UploadImage
|
|
||||||
// action.
|
|
||||||
type ImageVolumeType struct {
|
|
||||||
// The ID of a volume type.
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
// Human-readable display name for the volume type.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Human-readable description for the volume type.
|
|
||||||
Description string `json:"display_description"`
|
|
||||||
|
|
||||||
// Flag for public access.
|
|
||||||
IsPublic bool `json:"is_public"`
|
|
||||||
|
|
||||||
// Extra specifications for volume type.
|
|
||||||
ExtraSpecs map[string]interface{} `json:"extra_specs"`
|
|
||||||
|
|
||||||
// ID of quality of service specs.
|
|
||||||
QosSpecsID string `json:"qos_specs_id"`
|
|
||||||
|
|
||||||
// Flag for deletion status of volume type.
|
|
||||||
Deleted bool `json:"deleted"`
|
|
||||||
|
|
||||||
// The date when volume type was deleted.
|
|
||||||
DeletedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// The date when volume type was created.
|
|
||||||
CreatedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// The date when this volume was last updated.
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ImageVolumeType) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp ImageVolumeType
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = ImageVolumeType(s.tmp)
|
|
||||||
|
|
||||||
r.CreatedAt = time.Time(s.CreatedAt)
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
r.DeletedAt = time.Time(s.DeletedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeImage contains information about volume uploaded to an image service.
|
|
||||||
type VolumeImage struct {
|
|
||||||
// The ID of a volume an image is created from.
|
|
||||||
VolumeID string `json:"id"`
|
|
||||||
|
|
||||||
// Container format, may be bare, ofv, ova, etc.
|
|
||||||
ContainerFormat string `json:"container_format"`
|
|
||||||
|
|
||||||
// Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc.
|
|
||||||
DiskFormat string `json:"disk_format"`
|
|
||||||
|
|
||||||
// Human-readable description for the volume.
|
|
||||||
Description string `json:"display_description"`
|
|
||||||
|
|
||||||
// The ID of the created image.
|
|
||||||
ImageID string `json:"image_id"`
|
|
||||||
|
|
||||||
// Human-readable display name for the image.
|
|
||||||
ImageName string `json:"image_name"`
|
|
||||||
|
|
||||||
// Size of the volume in GB.
|
|
||||||
Size int `json:"size"`
|
|
||||||
|
|
||||||
// Current status of the volume.
|
|
||||||
Status string `json:"status"`
|
|
||||||
|
|
||||||
// The date when this volume was last updated.
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// Volume type object of used volume.
|
|
||||||
VolumeType ImageVolumeType `json:"volume_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *VolumeImage) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp VolumeImage
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = VolumeImage(s.tmp)
|
|
||||||
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get an object with info about the uploaded image out of the
|
|
||||||
// UploadImageResult object.
|
|
||||||
func (r UploadImageResult) Extract() (VolumeImage, error) {
|
|
||||||
var s struct {
|
|
||||||
VolumeImage VolumeImage `json:"os-volume_upload_image"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.VolumeImage, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForceDeleteResult contains the response body and error from a ForceDelete request.
|
|
||||||
type ForceDeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package volumeactions
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func actionURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("volumes", id, "action")
|
|
||||||
}
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/doc.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/doc.go
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
// Package volumes provides information and interaction with volumes in the
|
|
||||||
// OpenStack Block Storage service. A volume is a detachable block storage
|
|
||||||
// device, akin to a USB hard drive. It can only be attached to one instance at
|
|
||||||
// a time.
|
|
||||||
package volumes
|
|
172
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go
generated
vendored
172
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go
generated
vendored
|
@ -1,172 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToVolumeCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts contains options for creating a Volume. This object is passed to
|
|
||||||
// the volumes.Create function. For more information about these parameters,
|
|
||||||
// see the Volume object.
|
|
||||||
type CreateOpts struct {
|
|
||||||
Size int `json:"size" required:"true"`
|
|
||||||
AvailabilityZone string `json:"availability_zone,omitempty"`
|
|
||||||
Description string `json:"display_description,omitempty"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
Name string `json:"display_name,omitempty"`
|
|
||||||
SnapshotID string `json:"snapshot_id,omitempty"`
|
|
||||||
SourceVolID string `json:"source_volid,omitempty"`
|
|
||||||
ImageID string `json:"imageRef,omitempty"`
|
|
||||||
VolumeType string `json:"volume_type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeCreateMap assembles a request body based on the contents of a
|
|
||||||
// CreateOpts.
|
|
||||||
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create will create a new Volume based on the values in CreateOpts. To extract
|
|
||||||
// the Volume object from the response, call the Extract method on the
|
|
||||||
// CreateResult.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToVolumeCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200, 201},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the Volume with the provided ID. To extract the Volume object
|
|
||||||
// from the response, call the Extract method on the GetResult.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOptsBuilder allows extensions to add additional parameters to the List
|
|
||||||
// request.
|
|
||||||
type ListOptsBuilder interface {
|
|
||||||
ToVolumeListQuery() (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
|
|
||||||
// function.
|
|
||||||
type ListOpts struct {
|
|
||||||
// admin-only option. Set it to true to see all tenant volumes.
|
|
||||||
AllTenants bool `q:"all_tenants"`
|
|
||||||
// List only volumes that contain Metadata.
|
|
||||||
Metadata map[string]string `q:"metadata"`
|
|
||||||
// List only volumes that have Name as the display name.
|
|
||||||
Name string `q:"display_name"`
|
|
||||||
// List only volumes that have a status of Status.
|
|
||||||
Status string `q:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeListQuery formats a ListOpts into a query string.
|
|
||||||
func (opts ListOpts) ToVolumeListQuery() (string, error) {
|
|
||||||
q, err := gophercloud.BuildQueryString(opts)
|
|
||||||
return q.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns Volumes optionally limited by the conditions provided in ListOpts.
|
|
||||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
|
||||||
url := listURL(client)
|
|
||||||
if opts != nil {
|
|
||||||
query, err := opts.ToVolumeListQuery()
|
|
||||||
if err != nil {
|
|
||||||
return pagination.Pager{Err: err}
|
|
||||||
}
|
|
||||||
url += query
|
|
||||||
}
|
|
||||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return VolumePage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Update request.
|
|
||||||
type UpdateOptsBuilder interface {
|
|
||||||
ToVolumeUpdateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOpts contain options for updating an existing Volume. This object is passed
|
|
||||||
// to the volumes.Update function. For more information about the parameters, see
|
|
||||||
// the Volume object.
|
|
||||||
type UpdateOpts struct {
|
|
||||||
Name *string `json:"display_name,omitempty"`
|
|
||||||
Description *string `json:"display_description,omitempty"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeUpdateMap assembles a request body based on the contents of an
|
|
||||||
// UpdateOpts.
|
|
||||||
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update will update the Volume with provided information. To extract the updated
|
|
||||||
// Volume from the response, call the Extract method on the UpdateResult.
|
|
||||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
|
||||||
b, err := opts.ToVolumeUpdateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDFromName is a convienience function that returns a server's ID given its name.
|
|
||||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
|
||||||
count := 0
|
|
||||||
id := ""
|
|
||||||
|
|
||||||
listOpts := ListOpts{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
pages, err := List(client, listOpts).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err := ExtractVolumes(pages)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range all {
|
|
||||||
if s.Name == name {
|
|
||||||
count++
|
|
||||||
id = s.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch count {
|
|
||||||
case 0:
|
|
||||||
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
|
|
||||||
case 1:
|
|
||||||
return id, nil
|
|
||||||
default:
|
|
||||||
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
|
|
||||||
}
|
|
||||||
}
|
|
109
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/results.go
generated
vendored
109
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/results.go
generated
vendored
|
@ -1,109 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Volume contains all the information associated with an OpenStack Volume.
|
|
||||||
type Volume struct {
|
|
||||||
// Current status of the volume.
|
|
||||||
Status string `json:"status"`
|
|
||||||
// Human-readable display name for the volume.
|
|
||||||
Name string `json:"display_name"`
|
|
||||||
// Instances onto which the volume is attached.
|
|
||||||
Attachments []map[string]interface{} `json:"attachments"`
|
|
||||||
// This parameter is no longer used.
|
|
||||||
AvailabilityZone string `json:"availability_zone"`
|
|
||||||
// Indicates whether this is a bootable volume.
|
|
||||||
Bootable string `json:"bootable"`
|
|
||||||
// The date when this volume was created.
|
|
||||||
CreatedAt time.Time `json:"-"`
|
|
||||||
// Human-readable description for the volume.
|
|
||||||
Description string `json:"display_description"`
|
|
||||||
// The type of volume to create, either SATA or SSD.
|
|
||||||
VolumeType string `json:"volume_type"`
|
|
||||||
// The ID of the snapshot from which the volume was created
|
|
||||||
SnapshotID string `json:"snapshot_id"`
|
|
||||||
// The ID of another block storage volume from which the current volume was created
|
|
||||||
SourceVolID string `json:"source_volid"`
|
|
||||||
// Arbitrary key-value pairs defined by the user.
|
|
||||||
Metadata map[string]string `json:"metadata"`
|
|
||||||
// Unique identifier for the volume.
|
|
||||||
ID string `json:"id"`
|
|
||||||
// Size of the volume in GB.
|
|
||||||
Size int `json:"size"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Volume) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Volume
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Volume(s.tmp)
|
|
||||||
|
|
||||||
r.CreatedAt = time.Time(s.CreatedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult contains the response body and error from a Create request.
|
|
||||||
type CreateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult contains the response body and error from a Get request.
|
|
||||||
type GetResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult contains the response body and error from a Delete request.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumePage is a pagination.pager that is returned from a call to the List function.
|
|
||||||
type VolumePage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if a VolumePage contains no Volumes.
|
|
||||||
func (r VolumePage) IsEmpty() (bool, error) {
|
|
||||||
volumes, err := ExtractVolumes(r)
|
|
||||||
return len(volumes) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
|
|
||||||
func ExtractVolumes(r pagination.Page) ([]Volume, error) {
|
|
||||||
var s struct {
|
|
||||||
Volumes []Volume `json:"volumes"`
|
|
||||||
}
|
|
||||||
err := (r.(VolumePage)).ExtractInto(&s)
|
|
||||||
return s.Volumes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateResult contains the response body and error from an Update request.
|
|
||||||
type UpdateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get the Volume object out of the commonResult object.
|
|
||||||
func (r commonResult) Extract() (*Volume, error) {
|
|
||||||
var s struct {
|
|
||||||
Volume *Volume `json:"volume"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.Volume, err
|
|
||||||
}
|
|
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/urls.go
generated
vendored
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/urls.go
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("volumes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return createURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("volumes", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/util.go
generated
vendored
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/util.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WaitForStatus will continually poll the resource, checking for a particular
|
|
||||||
// status. It will do this for the amount of seconds defined.
|
|
||||||
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
|
||||||
return gophercloud.WaitFor(secs, func() (bool, error) {
|
|
||||||
current, err := Get(c, id).Extract()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.Status == status {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
}
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/doc.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/doc.go
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
// Package snapshots provides information and interaction with snapshots in the
|
|
||||||
// OpenStack Block Storage service. A snapshot is a point in time copy of the
|
|
||||||
// data contained in an external storage volume, and can be controlled
|
|
||||||
// programmatically.
|
|
||||||
package snapshots
|
|
175
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/requests.go
generated
vendored
175
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/requests.go
generated
vendored
|
@ -1,175 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToSnapshotCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts contains options for creating a Snapshot. This object is passed to
|
|
||||||
// the snapshots.Create function. For more information about these parameters,
|
|
||||||
// see the Snapshot object.
|
|
||||||
type CreateOpts struct {
|
|
||||||
VolumeID string `json:"volume_id" required:"true"`
|
|
||||||
Force bool `json:"force,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnapshotCreateMap assembles a request body based on the contents of a
|
|
||||||
// CreateOpts.
|
|
||||||
func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create will create a new Snapshot based on the values in CreateOpts. To
|
|
||||||
// extract the Snapshot object from the response, call the Extract method on the
|
|
||||||
// CreateResult.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToSnapshotCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete will delete the existing Snapshot with the provided ID.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the Snapshot with the provided ID. To extract the Snapshot
|
|
||||||
// object from the response, call the Extract method on the GetResult.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOptsBuilder allows extensions to add additional parameters to the List
|
|
||||||
// request.
|
|
||||||
type ListOptsBuilder interface {
|
|
||||||
ToSnapshotListQuery() (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOpts hold options for listing Snapshots. It is passed to the
|
|
||||||
// snapshots.List function.
|
|
||||||
type ListOpts struct {
|
|
||||||
// AllTenants will retrieve snapshots of all tenants/projects.
|
|
||||||
AllTenants bool `q:"all_tenants"`
|
|
||||||
|
|
||||||
// Name will filter by the specified snapshot name.
|
|
||||||
Name string `q:"name"`
|
|
||||||
|
|
||||||
// Status will filter by the specified status.
|
|
||||||
Status string `q:"status"`
|
|
||||||
|
|
||||||
// TenantID will filter by a specific tenant/project ID.
|
|
||||||
// Setting AllTenants is required to use this.
|
|
||||||
TenantID string `q:"project_id"`
|
|
||||||
|
|
||||||
// VolumeID will filter by a specified volume ID.
|
|
||||||
VolumeID string `q:"volume_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnapshotListQuery formats a ListOpts into a query string.
|
|
||||||
func (opts ListOpts) ToSnapshotListQuery() (string, error) {
|
|
||||||
q, err := gophercloud.BuildQueryString(opts)
|
|
||||||
return q.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns Snapshots optionally limited by the conditions provided in
|
|
||||||
// ListOpts.
|
|
||||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
|
||||||
url := listURL(client)
|
|
||||||
if opts != nil {
|
|
||||||
query, err := opts.ToSnapshotListQuery()
|
|
||||||
if err != nil {
|
|
||||||
return pagination.Pager{Err: err}
|
|
||||||
}
|
|
||||||
url += query
|
|
||||||
}
|
|
||||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return SnapshotPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadataOptsBuilder allows extensions to add additional parameters to
|
|
||||||
// the Update request.
|
|
||||||
type UpdateMetadataOptsBuilder interface {
|
|
||||||
ToSnapshotUpdateMetadataMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadataOpts contain options for updating an existing Snapshot. This
|
|
||||||
// object is passed to the snapshots.Update function. For more information
|
|
||||||
// about the parameters, see the Snapshot object.
|
|
||||||
type UpdateMetadataOpts struct {
|
|
||||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of
|
|
||||||
// an UpdateMetadataOpts.
|
|
||||||
func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadata will update the Snapshot with provided information. To
|
|
||||||
// extract the updated Snapshot from the response, call the ExtractMetadata
|
|
||||||
// method on the UpdateMetadataResult.
|
|
||||||
func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
|
|
||||||
b, err := opts.ToSnapshotUpdateMetadataMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDFromName is a convienience function that returns a snapshot's ID given its name.
|
|
||||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
|
||||||
count := 0
|
|
||||||
id := ""
|
|
||||||
|
|
||||||
listOpts := ListOpts{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
pages, err := List(client, listOpts).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err := ExtractSnapshots(pages)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range all {
|
|
||||||
if s.Name == name {
|
|
||||||
count++
|
|
||||||
id = s.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch count {
|
|
||||||
case 0:
|
|
||||||
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"}
|
|
||||||
case 1:
|
|
||||||
return id, nil
|
|
||||||
default:
|
|
||||||
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"}
|
|
||||||
}
|
|
||||||
}
|
|
120
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/results.go
generated
vendored
120
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/results.go
generated
vendored
|
@ -1,120 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot contains all the information associated with a Cinder Snapshot.
|
|
||||||
type Snapshot struct {
|
|
||||||
// Unique identifier.
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
// Date created.
|
|
||||||
CreatedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// Date updated.
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// Display name.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Display description.
|
|
||||||
Description string `json:"description"`
|
|
||||||
|
|
||||||
// ID of the Volume from which this Snapshot was created.
|
|
||||||
VolumeID string `json:"volume_id"`
|
|
||||||
|
|
||||||
// Currect status of the Snapshot.
|
|
||||||
Status string `json:"status"`
|
|
||||||
|
|
||||||
// Size of the Snapshot, in GB.
|
|
||||||
Size int `json:"size"`
|
|
||||||
|
|
||||||
// User-defined key-value pairs.
|
|
||||||
Metadata map[string]string `json:"metadata"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult contains the response body and error from a Create request.
|
|
||||||
type CreateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult contains the response body and error from a Get request.
|
|
||||||
type GetResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult contains the response body and error from a Delete request.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// SnapshotPage is a pagination.Pager that is returned from a call to the List function.
|
|
||||||
type SnapshotPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Snapshot) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Snapshot
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Snapshot(s.tmp)
|
|
||||||
|
|
||||||
r.CreatedAt = time.Time(s.CreatedAt)
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if a SnapshotPage contains no Snapshots.
|
|
||||||
func (r SnapshotPage) IsEmpty() (bool, error) {
|
|
||||||
volumes, err := ExtractSnapshots(r)
|
|
||||||
return len(volumes) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call.
|
|
||||||
func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) {
|
|
||||||
var s struct {
|
|
||||||
Snapshots []Snapshot `json:"snapshots"`
|
|
||||||
}
|
|
||||||
err := (r.(SnapshotPage)).ExtractInto(&s)
|
|
||||||
return s.Snapshots, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadataResult contains the response body and error from an UpdateMetadata request.
|
|
||||||
type UpdateMetadataResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata.
|
|
||||||
func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) {
|
|
||||||
if r.Err != nil {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
m := r.Body.(map[string]interface{})["metadata"]
|
|
||||||
return m.(map[string]interface{}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get the Snapshot object out of the commonResult object.
|
|
||||||
func (r commonResult) Extract() (*Snapshot, error) {
|
|
||||||
var s struct {
|
|
||||||
Snapshot *Snapshot `json:"snapshot"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.Snapshot, err
|
|
||||||
}
|
|
27
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/urls.go
generated
vendored
27
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/urls.go
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("snapshots")
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("snapshots", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return createURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func metadataURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("snapshots", id, "metadata")
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateMetadataURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return metadataURL(c, id)
|
|
||||||
}
|
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/util.go
generated
vendored
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots/util.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WaitForStatus will continually poll the resource, checking for a particular
|
|
||||||
// status. It will do this for the amount of seconds defined.
|
|
||||||
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
|
||||||
return gophercloud.WaitFor(secs, func() (bool, error) {
|
|
||||||
current, err := Get(c, id).Extract()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.Status == status {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
}
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
// Package volumes provides information and interaction with volumes in the
|
|
||||||
// OpenStack Block Storage service. A volume is a detachable block storage
|
|
||||||
// device, akin to a USB hard drive. It can only be attached to one instance at
|
|
||||||
// a time.
|
|
||||||
package volumes
|
|
235
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go
generated
vendored
235
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go
generated
vendored
|
@ -1,235 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToVolumeCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts contains options for creating a Volume. This object is passed to
|
|
||||||
// the volumes.Create function. For more information about these parameters,
|
|
||||||
// see the Volume object.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// The size of the volume, in GB
|
|
||||||
Size int `json:"size" required:"true"`
|
|
||||||
// The availability zone
|
|
||||||
AvailabilityZone string `json:"availability_zone,omitempty"`
|
|
||||||
// ConsistencyGroupID is the ID of a consistency group
|
|
||||||
ConsistencyGroupID string `json:"consistencygroup_id,omitempty"`
|
|
||||||
// The volume description
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
// One or more metadata key and value pairs to associate with the volume
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
// The volume name
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
// the ID of the existing volume snapshot
|
|
||||||
SnapshotID string `json:"snapshot_id,omitempty"`
|
|
||||||
// SourceReplica is a UUID of an existing volume to replicate with
|
|
||||||
SourceReplica string `json:"source_replica,omitempty"`
|
|
||||||
// the ID of the existing volume
|
|
||||||
SourceVolID string `json:"source_volid,omitempty"`
|
|
||||||
// The ID of the image from which you want to create the volume.
|
|
||||||
// Required to create a bootable volume.
|
|
||||||
ImageID string `json:"imageRef,omitempty"`
|
|
||||||
// The associated volume type
|
|
||||||
VolumeType string `json:"volume_type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeCreateMap assembles a request body based on the contents of a
|
|
||||||
// CreateOpts.
|
|
||||||
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create will create a new Volume based on the values in CreateOpts. To extract
|
|
||||||
// the Volume object from the response, call the Extract method on the
|
|
||||||
// CreateResult.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToVolumeCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
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, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the Volume with the provided ID. To extract the Volume object
|
|
||||||
// from the response, call the Extract method on the GetResult.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOptsBuilder allows extensions to add additional parameters to the List
|
|
||||||
// request.
|
|
||||||
type ListOptsBuilder interface {
|
|
||||||
ToVolumeListQuery() (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
|
|
||||||
// function.
|
|
||||||
type ListOpts struct {
|
|
||||||
// AllTenants will retrieve volumes of all tenants/projects.
|
|
||||||
AllTenants bool `q:"all_tenants"`
|
|
||||||
|
|
||||||
// Metadata will filter results based on specified metadata.
|
|
||||||
Metadata map[string]string `q:"metadata"`
|
|
||||||
|
|
||||||
// Name will filter by the specified volume name.
|
|
||||||
Name string `q:"name"`
|
|
||||||
|
|
||||||
// Status will filter by the specified status.
|
|
||||||
Status string `q:"status"`
|
|
||||||
|
|
||||||
// TenantID will filter by a specific tenant/project ID.
|
|
||||||
// Setting AllTenants is required for this.
|
|
||||||
TenantID string `q:"project_id"`
|
|
||||||
|
|
||||||
// Comma-separated list of sort keys and optional sort directions in the
|
|
||||||
// form of <key>[:<direction>].
|
|
||||||
Sort string `q:"sort"`
|
|
||||||
|
|
||||||
// Requests a page size of items.
|
|
||||||
Limit int `q:"limit"`
|
|
||||||
|
|
||||||
// Used in conjunction with limit to return a slice of items.
|
|
||||||
Offset int `q:"offset"`
|
|
||||||
|
|
||||||
// The ID of the last-seen item.
|
|
||||||
Marker string `q:"marker"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeListQuery formats a ListOpts into a query string.
|
|
||||||
func (opts ListOpts) ToVolumeListQuery() (string, error) {
|
|
||||||
q, err := gophercloud.BuildQueryString(opts)
|
|
||||||
return q.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns Volumes optionally limited by the conditions provided in ListOpts.
|
|
||||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
|
||||||
url := listURL(client)
|
|
||||||
if opts != nil {
|
|
||||||
query, err := opts.ToVolumeListQuery()
|
|
||||||
if err != nil {
|
|
||||||
return pagination.Pager{Err: err}
|
|
||||||
}
|
|
||||||
url += query
|
|
||||||
}
|
|
||||||
|
|
||||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return VolumePage{pagination.LinkedPageBase{PageResult: r}}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Update request.
|
|
||||||
type UpdateOptsBuilder interface {
|
|
||||||
ToVolumeUpdateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOpts contain options for updating an existing Volume. This object is passed
|
|
||||||
// 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"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeUpdateMap assembles a request body based on the contents of an
|
|
||||||
// UpdateOpts.
|
|
||||||
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update will update the Volume with provided information. To extract the updated
|
|
||||||
// Volume from the response, call the Extract method on the UpdateResult.
|
|
||||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
|
||||||
b, err := opts.ToVolumeUpdateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDFromName is a convienience function that returns a server's ID given its name.
|
|
||||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
|
||||||
count := 0
|
|
||||||
id := ""
|
|
||||||
|
|
||||||
listOpts := ListOpts{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
pages, err := List(client, listOpts).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err := ExtractVolumes(pages)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range all {
|
|
||||||
if s.Name == name {
|
|
||||||
count++
|
|
||||||
id = s.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch count {
|
|
||||||
case 0:
|
|
||||||
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
|
|
||||||
case 1:
|
|
||||||
return id, nil
|
|
||||||
default:
|
|
||||||
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
|
|
||||||
}
|
|
||||||
}
|
|
167
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go
generated
vendored
167
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go
generated
vendored
|
@ -1,167 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Attachment struct {
|
|
||||||
AttachedAt time.Time `json:"-"`
|
|
||||||
AttachmentID string `json:"attachment_id"`
|
|
||||||
Device string `json:"device"`
|
|
||||||
HostName string `json:"host_name"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
ServerID string `json:"server_id"`
|
|
||||||
VolumeID string `json:"volume_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Attachment) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Attachment
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Attachment(s.tmp)
|
|
||||||
|
|
||||||
r.AttachedAt = time.Time(s.AttachedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume contains all the information associated with an OpenStack Volume.
|
|
||||||
type Volume struct {
|
|
||||||
// Unique identifier for the volume.
|
|
||||||
ID string `json:"id"`
|
|
||||||
// Current status of the volume.
|
|
||||||
Status string `json:"status"`
|
|
||||||
// Size of the volume in GB.
|
|
||||||
Size int `json:"size"`
|
|
||||||
// AvailabilityZone is which availability zone the volume is in.
|
|
||||||
AvailabilityZone string `json:"availability_zone"`
|
|
||||||
// The date when this volume was created.
|
|
||||||
CreatedAt time.Time `json:"-"`
|
|
||||||
// The date when this volume was last updated
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
// Instances onto which the volume is attached.
|
|
||||||
Attachments []Attachment `json:"attachments"`
|
|
||||||
// Human-readable display name for the volume.
|
|
||||||
Name string `json:"name"`
|
|
||||||
// Human-readable description for the volume.
|
|
||||||
Description string `json:"description"`
|
|
||||||
// The type of volume to create, either SATA or SSD.
|
|
||||||
VolumeType string `json:"volume_type"`
|
|
||||||
// The ID of the snapshot from which the volume was created
|
|
||||||
SnapshotID string `json:"snapshot_id"`
|
|
||||||
// The ID of another block storage volume from which the current volume was created
|
|
||||||
SourceVolID string `json:"source_volid"`
|
|
||||||
// Arbitrary key-value pairs defined by the user.
|
|
||||||
Metadata map[string]string `json:"metadata"`
|
|
||||||
// UserID is the id of the user who created the volume.
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
// Indicates whether this is a bootable volume.
|
|
||||||
Bootable string `json:"bootable"`
|
|
||||||
// Encrypted denotes if the volume is encrypted.
|
|
||||||
Encrypted bool `json:"encrypted"`
|
|
||||||
// ReplicationStatus is the status of replication.
|
|
||||||
ReplicationStatus string `json:"replication_status"`
|
|
||||||
// ConsistencyGroupID is the consistency group ID.
|
|
||||||
ConsistencyGroupID string `json:"consistencygroup_id"`
|
|
||||||
// Multiattach denotes if the volume is multi-attach capable.
|
|
||||||
Multiattach bool `json:"multiattach"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Volume) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Volume
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Volume(s.tmp)
|
|
||||||
|
|
||||||
r.CreatedAt = time.Time(s.CreatedAt)
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumePage is a pagination.pager that is returned from a call to the List function.
|
|
||||||
type VolumePage struct {
|
|
||||||
pagination.LinkedPageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if a ListResult contains no Volumes.
|
|
||||||
func (r VolumePage) IsEmpty() (bool, error) {
|
|
||||||
volumes, err := ExtractVolumes(r)
|
|
||||||
return len(volumes) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextPageURL uses the response's embedded link reference to navigate to the
|
|
||||||
// next page of results.
|
|
||||||
func (r VolumePage) NextPageURL() (string, error) {
|
|
||||||
var s struct {
|
|
||||||
Links []gophercloud.Link `json:"volumes_links"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return gophercloud.ExtractNextURL(s.Links)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
|
|
||||||
func ExtractVolumes(r pagination.Page) ([]Volume, error) {
|
|
||||||
var s []Volume
|
|
||||||
err := ExtractVolumesInto(r, &s)
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get the Volume object out of the commonResult object.
|
|
||||||
func (r commonResult) Extract() (*Volume, error) {
|
|
||||||
var s Volume
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return &s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r commonResult) ExtractInto(v interface{}) error {
|
|
||||||
return r.Result.ExtractIntoStructPtr(v, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractVolumesInto(r pagination.Page, v interface{}) error {
|
|
||||||
return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult contains the response body and error from a Create request.
|
|
||||||
type CreateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult contains the response body and error from a Get request.
|
|
||||||
type GetResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateResult contains the response body and error from an Update request.
|
|
||||||
type UpdateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult contains the response body and error from a Delete request.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go
generated
vendored
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("volumes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("volumes", "detail")
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("volumes", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go
generated
vendored
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WaitForStatus will continually poll the resource, checking for a particular
|
|
||||||
// status. It will do this for the amount of seconds defined.
|
|
||||||
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
|
||||||
return gophercloud.WaitFor(secs, func() (bool, error) {
|
|
||||||
current, err := Get(c, id).Extract()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.Status == status {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
}
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/doc.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/doc.go
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
// Package snapshots provides information and interaction with snapshots in the
|
|
||||||
// OpenStack Block Storage service. A snapshot is a point in time copy of the
|
|
||||||
// data contained in an external storage volume, and can be controlled
|
|
||||||
// programmatically.
|
|
||||||
package snapshots
|
|
186
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/requests.go
generated
vendored
186
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/requests.go
generated
vendored
|
@ -1,186 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToSnapshotCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts contains options for creating a Snapshot. This object is passed to
|
|
||||||
// the snapshots.Create function. For more information about these parameters,
|
|
||||||
// see the Snapshot object.
|
|
||||||
type CreateOpts struct {
|
|
||||||
VolumeID string `json:"volume_id" required:"true"`
|
|
||||||
Force bool `json:"force,omitempty"`
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnapshotCreateMap assembles a request body based on the contents of a
|
|
||||||
// CreateOpts.
|
|
||||||
func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create will create a new Snapshot based on the values in CreateOpts. To
|
|
||||||
// extract the Snapshot object from the response, call the Extract method on the
|
|
||||||
// CreateResult.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToSnapshotCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete will delete the existing Snapshot with the provided ID.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the Snapshot with the provided ID. To extract the Snapshot
|
|
||||||
// object from the response, call the Extract method on the GetResult.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOptsBuilder allows extensions to add additional parameters to the List
|
|
||||||
// request.
|
|
||||||
type ListOptsBuilder interface {
|
|
||||||
ToSnapshotListQuery() (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListOpts struct {
|
|
||||||
// AllTenants will retrieve snapshots of all tenants/projects.
|
|
||||||
AllTenants bool `q:"all_tenants"`
|
|
||||||
|
|
||||||
// Name will filter by the specified snapshot name.
|
|
||||||
Name string `q:"name"`
|
|
||||||
|
|
||||||
// Status will filter by the specified status.
|
|
||||||
Status string `q:"status"`
|
|
||||||
|
|
||||||
// TenantID will filter by a specific tenant/project ID.
|
|
||||||
// Setting AllTenants is required to use this.
|
|
||||||
TenantID string `q:"project_id"`
|
|
||||||
|
|
||||||
// VolumeID will filter by a specified volume ID.
|
|
||||||
VolumeID string `q:"volume_id"`
|
|
||||||
|
|
||||||
// Comma-separated list of sort keys and optional sort directions in the
|
|
||||||
// form of <key>[:<direction>].
|
|
||||||
Sort string `q:"sort"`
|
|
||||||
|
|
||||||
// Requests a page size of items.
|
|
||||||
Limit int `q:"limit"`
|
|
||||||
|
|
||||||
// Used in conjunction with limit to return a slice of items.
|
|
||||||
Offset int `q:"offset"`
|
|
||||||
|
|
||||||
// The ID of the last-seen item.
|
|
||||||
Marker string `q:"marker"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnapshotListQuery formats a ListOpts into a query string.
|
|
||||||
func (opts ListOpts) ToSnapshotListQuery() (string, error) {
|
|
||||||
q, err := gophercloud.BuildQueryString(opts)
|
|
||||||
return q.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns Snapshots optionally limited by the conditions provided in
|
|
||||||
// ListOpts.
|
|
||||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
|
||||||
url := listURL(client)
|
|
||||||
if opts != nil {
|
|
||||||
query, err := opts.ToSnapshotListQuery()
|
|
||||||
if err != nil {
|
|
||||||
return pagination.Pager{Err: err}
|
|
||||||
}
|
|
||||||
url += query
|
|
||||||
}
|
|
||||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return SnapshotPage{pagination.LinkedPageBase{PageResult: r}}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadataOptsBuilder allows extensions to add additional parameters to
|
|
||||||
// the Update request.
|
|
||||||
type UpdateMetadataOptsBuilder interface {
|
|
||||||
ToSnapshotUpdateMetadataMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadataOpts contain options for updating an existing Snapshot. This
|
|
||||||
// object is passed to the snapshots.Update function. For more information
|
|
||||||
// about the parameters, see the Snapshot object.
|
|
||||||
type UpdateMetadataOpts struct {
|
|
||||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of
|
|
||||||
// an UpdateMetadataOpts.
|
|
||||||
func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadata will update the Snapshot with provided information. To
|
|
||||||
// extract the updated Snapshot from the response, call the ExtractMetadata
|
|
||||||
// method on the UpdateMetadataResult.
|
|
||||||
func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) {
|
|
||||||
b, err := opts.ToSnapshotUpdateMetadataMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDFromName is a convienience function that returns a snapshot's ID given its name.
|
|
||||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
|
||||||
count := 0
|
|
||||||
id := ""
|
|
||||||
|
|
||||||
listOpts := ListOpts{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
pages, err := List(client, listOpts).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err := ExtractSnapshots(pages)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range all {
|
|
||||||
if s.Name == name {
|
|
||||||
count++
|
|
||||||
id = s.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch count {
|
|
||||||
case 0:
|
|
||||||
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"}
|
|
||||||
case 1:
|
|
||||||
return id, nil
|
|
||||||
default:
|
|
||||||
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"}
|
|
||||||
}
|
|
||||||
}
|
|
132
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/results.go
generated
vendored
132
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/results.go
generated
vendored
|
@ -1,132 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot contains all the information associated with a Cinder Snapshot.
|
|
||||||
type Snapshot struct {
|
|
||||||
// Unique identifier.
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
// Date created.
|
|
||||||
CreatedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// Date updated.
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
|
|
||||||
// Display name.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Display description.
|
|
||||||
Description string `json:"description"`
|
|
||||||
|
|
||||||
// ID of the Volume from which this Snapshot was created.
|
|
||||||
VolumeID string `json:"volume_id"`
|
|
||||||
|
|
||||||
// Currect status of the Snapshot.
|
|
||||||
Status string `json:"status"`
|
|
||||||
|
|
||||||
// Size of the Snapshot, in GB.
|
|
||||||
Size int `json:"size"`
|
|
||||||
|
|
||||||
// User-defined key-value pairs.
|
|
||||||
Metadata map[string]string `json:"metadata"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult contains the response body and error from a Create request.
|
|
||||||
type CreateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult contains the response body and error from a Get request.
|
|
||||||
type GetResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult contains the response body and error from a Delete request.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// SnapshotPage is a pagination.Pager that is returned from a call to the List function.
|
|
||||||
type SnapshotPage struct {
|
|
||||||
pagination.LinkedPageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON converts our JSON API response into our snapshot struct
|
|
||||||
func (r *Snapshot) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Snapshot
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Snapshot(s.tmp)
|
|
||||||
|
|
||||||
r.CreatedAt = time.Time(s.CreatedAt)
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if a SnapshotPage contains no Snapshots.
|
|
||||||
func (r SnapshotPage) IsEmpty() (bool, error) {
|
|
||||||
volumes, err := ExtractSnapshots(r)
|
|
||||||
return len(volumes) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (page SnapshotPage) NextPageURL() (string, error) {
|
|
||||||
var s struct {
|
|
||||||
Links []gophercloud.Link `json:"snapshots_links"`
|
|
||||||
}
|
|
||||||
err := page.ExtractInto(&s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return gophercloud.ExtractNextURL(s.Links)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call.
|
|
||||||
func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) {
|
|
||||||
var s struct {
|
|
||||||
Snapshots []Snapshot `json:"snapshots"`
|
|
||||||
}
|
|
||||||
err := (r.(SnapshotPage)).ExtractInto(&s)
|
|
||||||
return s.Snapshots, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMetadataResult contains the response body and error from an UpdateMetadata request.
|
|
||||||
type UpdateMetadataResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata.
|
|
||||||
func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) {
|
|
||||||
if r.Err != nil {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
m := r.Body.(map[string]interface{})["metadata"]
|
|
||||||
return m.(map[string]interface{}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get the Snapshot object out of the commonResult object.
|
|
||||||
func (r commonResult) Extract() (*Snapshot, error) {
|
|
||||||
var s struct {
|
|
||||||
Snapshot *Snapshot `json:"snapshot"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.Snapshot, err
|
|
||||||
}
|
|
27
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/urls.go
generated
vendored
27
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/urls.go
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("snapshots")
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("snapshots", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return createURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func metadataURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("snapshots", id, "metadata")
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateMetadataURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return metadataURL(c, id)
|
|
||||||
}
|
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/util.go
generated
vendored
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots/util.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
package snapshots
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WaitForStatus will continually poll the resource, checking for a particular
|
|
||||||
// status. It will do this for the amount of seconds defined.
|
|
||||||
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
|
||||||
return gophercloud.WaitFor(secs, func() (bool, error) {
|
|
||||||
current, err := Get(c, id).Extract()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.Status == status {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
}
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/doc.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/doc.go
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
// Package volumes provides information and interaction with volumes in the
|
|
||||||
// OpenStack Block Storage service. A volume is a detachable block storage
|
|
||||||
// device, akin to a USB hard drive. It can only be attached to one instance at
|
|
||||||
// a time.
|
|
||||||
package volumes
|
|
237
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go
generated
vendored
237
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go
generated
vendored
|
@ -1,237 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToVolumeCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts contains options for creating a Volume. This object is passed to
|
|
||||||
// the volumes.Create function. For more information about these parameters,
|
|
||||||
// see the Volume object.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// The size of the volume, in GB
|
|
||||||
Size int `json:"size" required:"true"`
|
|
||||||
// The availability zone
|
|
||||||
AvailabilityZone string `json:"availability_zone,omitempty"`
|
|
||||||
// ConsistencyGroupID is the ID of a consistency group
|
|
||||||
ConsistencyGroupID string `json:"consistencygroup_id,omitempty"`
|
|
||||||
// The volume description
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
// One or more metadata key and value pairs to associate with the volume
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
// The volume name
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
// the ID of the existing volume snapshot
|
|
||||||
SnapshotID string `json:"snapshot_id,omitempty"`
|
|
||||||
// SourceReplica is a UUID of an existing volume to replicate with
|
|
||||||
SourceReplica string `json:"source_replica,omitempty"`
|
|
||||||
// the ID of the existing volume
|
|
||||||
SourceVolID string `json:"source_volid,omitempty"`
|
|
||||||
// The ID of the image from which you want to create the volume.
|
|
||||||
// Required to create a bootable volume.
|
|
||||||
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
|
|
||||||
// CreateOpts.
|
|
||||||
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create will create a new Volume based on the values in CreateOpts. To extract
|
|
||||||
// the Volume object from the response, call the Extract method on the
|
|
||||||
// CreateResult.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToVolumeCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{202},
|
|
||||||
})
|
|
||||||
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, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the Volume with the provided ID. To extract the Volume object
|
|
||||||
// from the response, call the Extract method on the GetResult.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOptsBuilder allows extensions to add additional parameters to the List
|
|
||||||
// request.
|
|
||||||
type ListOptsBuilder interface {
|
|
||||||
ToVolumeListQuery() (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
|
|
||||||
// function.
|
|
||||||
type ListOpts struct {
|
|
||||||
// AllTenants will retrieve volumes of all tenants/projects.
|
|
||||||
AllTenants bool `q:"all_tenants"`
|
|
||||||
|
|
||||||
// Metadata will filter results based on specified metadata.
|
|
||||||
Metadata map[string]string `q:"metadata"`
|
|
||||||
|
|
||||||
// Name will filter by the specified volume name.
|
|
||||||
Name string `q:"name"`
|
|
||||||
|
|
||||||
// Status will filter by the specified status.
|
|
||||||
Status string `q:"status"`
|
|
||||||
|
|
||||||
// TenantID will filter by a specific tenant/project ID.
|
|
||||||
// Setting AllTenants is required for this.
|
|
||||||
TenantID string `q:"project_id"`
|
|
||||||
|
|
||||||
// Comma-separated list of sort keys and optional sort directions in the
|
|
||||||
// form of <key>[:<direction>].
|
|
||||||
Sort string `q:"sort"`
|
|
||||||
|
|
||||||
// Requests a page size of items.
|
|
||||||
Limit int `q:"limit"`
|
|
||||||
|
|
||||||
// Used in conjunction with limit to return a slice of items.
|
|
||||||
Offset int `q:"offset"`
|
|
||||||
|
|
||||||
// The ID of the last-seen item.
|
|
||||||
Marker string `q:"marker"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeListQuery formats a ListOpts into a query string.
|
|
||||||
func (opts ListOpts) ToVolumeListQuery() (string, error) {
|
|
||||||
q, err := gophercloud.BuildQueryString(opts)
|
|
||||||
return q.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns Volumes optionally limited by the conditions provided in ListOpts.
|
|
||||||
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
|
||||||
url := listURL(client)
|
|
||||||
if opts != nil {
|
|
||||||
query, err := opts.ToVolumeListQuery()
|
|
||||||
if err != nil {
|
|
||||||
return pagination.Pager{Err: err}
|
|
||||||
}
|
|
||||||
url += query
|
|
||||||
}
|
|
||||||
|
|
||||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return VolumePage{pagination.LinkedPageBase{PageResult: r}}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Update request.
|
|
||||||
type UpdateOptsBuilder interface {
|
|
||||||
ToVolumeUpdateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOpts contain options for updating an existing Volume. This object is passed
|
|
||||||
// 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"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeUpdateMap assembles a request body based on the contents of an
|
|
||||||
// UpdateOpts.
|
|
||||||
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update will update the Volume with provided information. To extract the updated
|
|
||||||
// Volume from the response, call the Extract method on the UpdateResult.
|
|
||||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
|
||||||
b, err := opts.ToVolumeUpdateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDFromName is a convienience function that returns a server's ID given its name.
|
|
||||||
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
|
||||||
count := 0
|
|
||||||
id := ""
|
|
||||||
|
|
||||||
listOpts := ListOpts{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
pages, err := List(client, listOpts).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
all, err := ExtractVolumes(pages)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range all {
|
|
||||||
if s.Name == name {
|
|
||||||
count++
|
|
||||||
id = s.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch count {
|
|
||||||
case 0:
|
|
||||||
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
|
|
||||||
case 1:
|
|
||||||
return id, nil
|
|
||||||
default:
|
|
||||||
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
|
|
||||||
}
|
|
||||||
}
|
|
170
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/results.go
generated
vendored
170
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/results.go
generated
vendored
|
@ -1,170 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Attachment represents a Volume Attachment record
|
|
||||||
type Attachment struct {
|
|
||||||
AttachedAt time.Time `json:"-"`
|
|
||||||
AttachmentID string `json:"attachment_id"`
|
|
||||||
Device string `json:"device"`
|
|
||||||
HostName string `json:"host_name"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
ServerID string `json:"server_id"`
|
|
||||||
VolumeID string `json:"volume_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON is our unmarshalling helper
|
|
||||||
func (r *Attachment) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Attachment
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Attachment(s.tmp)
|
|
||||||
|
|
||||||
r.AttachedAt = time.Time(s.AttachedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume contains all the information associated with an OpenStack Volume.
|
|
||||||
type Volume struct {
|
|
||||||
// Unique identifier for the volume.
|
|
||||||
ID string `json:"id"`
|
|
||||||
// Current status of the volume.
|
|
||||||
Status string `json:"status"`
|
|
||||||
// Size of the volume in GB.
|
|
||||||
Size int `json:"size"`
|
|
||||||
// AvailabilityZone is which availability zone the volume is in.
|
|
||||||
AvailabilityZone string `json:"availability_zone"`
|
|
||||||
// The date when this volume was created.
|
|
||||||
CreatedAt time.Time `json:"-"`
|
|
||||||
// The date when this volume was last updated
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
// Instances onto which the volume is attached.
|
|
||||||
Attachments []Attachment `json:"attachments"`
|
|
||||||
// Human-readable display name for the volume.
|
|
||||||
Name string `json:"name"`
|
|
||||||
// Human-readable description for the volume.
|
|
||||||
Description string `json:"description"`
|
|
||||||
// The type of volume to create, either SATA or SSD.
|
|
||||||
VolumeType string `json:"volume_type"`
|
|
||||||
// The ID of the snapshot from which the volume was created
|
|
||||||
SnapshotID string `json:"snapshot_id"`
|
|
||||||
// The ID of another block storage volume from which the current volume was created
|
|
||||||
SourceVolID string `json:"source_volid"`
|
|
||||||
// Arbitrary key-value pairs defined by the user.
|
|
||||||
Metadata map[string]string `json:"metadata"`
|
|
||||||
// UserID is the id of the user who created the volume.
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
// Indicates whether this is a bootable volume.
|
|
||||||
Bootable string `json:"bootable"`
|
|
||||||
// Encrypted denotes if the volume is encrypted.
|
|
||||||
Encrypted bool `json:"encrypted"`
|
|
||||||
// ReplicationStatus is the status of replication.
|
|
||||||
ReplicationStatus string `json:"replication_status"`
|
|
||||||
// ConsistencyGroupID is the consistency group ID.
|
|
||||||
ConsistencyGroupID string `json:"consistencygroup_id"`
|
|
||||||
// Multiattach denotes if the volume is multi-attach capable.
|
|
||||||
Multiattach bool `json:"multiattach"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON another unmarshalling function
|
|
||||||
func (r *Volume) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Volume
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = Volume(s.tmp)
|
|
||||||
|
|
||||||
r.CreatedAt = time.Time(s.CreatedAt)
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumePage is a pagination.pager that is returned from a call to the List function.
|
|
||||||
type VolumePage struct {
|
|
||||||
pagination.LinkedPageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if a ListResult contains no Volumes.
|
|
||||||
func (r VolumePage) IsEmpty() (bool, error) {
|
|
||||||
volumes, err := ExtractVolumes(r)
|
|
||||||
return len(volumes) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (page VolumePage) NextPageURL() (string, error) {
|
|
||||||
var s struct {
|
|
||||||
Links []gophercloud.Link `json:"volumes_links"`
|
|
||||||
}
|
|
||||||
err := page.ExtractInto(&s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return gophercloud.ExtractNextURL(s.Links)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
|
|
||||||
func ExtractVolumes(r pagination.Page) ([]Volume, error) {
|
|
||||||
var s []Volume
|
|
||||||
err := ExtractVolumesInto(r, &s)
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will get the Volume object out of the commonResult object.
|
|
||||||
func (r commonResult) Extract() (*Volume, error) {
|
|
||||||
var s Volume
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return &s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractInto converts our response data into a volume struct
|
|
||||||
func (r commonResult) ExtractInto(v interface{}) error {
|
|
||||||
return r.Result.ExtractIntoStructPtr(v, "volume")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes
|
|
||||||
func ExtractVolumesInto(r pagination.Page, v interface{}) error {
|
|
||||||
return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult contains the response body and error from a Create request.
|
|
||||||
type CreateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult contains the response body and error from a Get request.
|
|
||||||
type GetResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateResult contains the response body and error from an Update request.
|
|
||||||
type UpdateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult contains the response body and error from a Delete request.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/urls.go
generated
vendored
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/urls.go
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("volumes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("volumes", "detail")
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("volumes", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return deleteURL(c, id)
|
|
||||||
}
|
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/util.go
generated
vendored
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/util.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
package volumes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WaitForStatus will continually poll the resource, checking for a particular
|
|
||||||
// status. It will do this for the amount of seconds defined.
|
|
||||||
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
|
||||||
return gophercloud.WaitFor(secs, func() (bool, error) {
|
|
||||||
current, err := Get(c, id).Extract()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.Status == status {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -3,9 +3,12 @@ package openstack
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1"
|
||||||
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||||
"github.com/gophercloud/gophercloud/openstack/utils"
|
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||||
)
|
)
|
||||||
|
@ -67,7 +70,7 @@ Example:
|
||||||
|
|
||||||
ao, err := openstack.AuthOptionsFromEnv()
|
ao, err := openstack.AuthOptionsFromEnv()
|
||||||
provider, err := openstack.AuthenticatedClient(ao)
|
provider, err := openstack.AuthenticatedClient(ao)
|
||||||
client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{
|
client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
|
||||||
Region: os.Getenv("OS_REGION_NAME"),
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
})
|
})
|
||||||
*/
|
*/
|
||||||
|
@ -187,16 +190,61 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
||||||
v3Client.Endpoint = endpoint
|
v3Client.Endpoint = endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
result := tokens3.Create(v3Client, opts)
|
var catalog *tokens3.ServiceCatalog
|
||||||
|
|
||||||
err = client.SetTokenAndAuthResult(result)
|
var tokenID string
|
||||||
if err != nil {
|
// passthroughToken allows to passthrough the token without a scope
|
||||||
return err
|
var passthroughToken bool
|
||||||
|
switch v := opts.(type) {
|
||||||
|
case *gophercloud.AuthOptions:
|
||||||
|
tokenID = v.TokenID
|
||||||
|
passthroughToken = (v.Scope == nil || *v.Scope == gophercloud.AuthScope{})
|
||||||
|
case *tokens3.AuthOptions:
|
||||||
|
tokenID = v.TokenID
|
||||||
|
passthroughToken = (v.Scope == tokens3.Scope{})
|
||||||
}
|
}
|
||||||
|
|
||||||
catalog, err := result.ExtractServiceCatalog()
|
if tokenID != "" && passthroughToken {
|
||||||
if err != nil {
|
// passing through the token ID without requesting a new scope
|
||||||
return err
|
if opts.CanReauth() {
|
||||||
|
return fmt.Errorf("cannot use AllowReauth, when the token ID is defined and auth scope is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
v3Client.SetToken(tokenID)
|
||||||
|
result := tokens3.Get(v3Client, tokenID)
|
||||||
|
if result.Err != nil {
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SetTokenAndAuthResult(result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err = result.ExtractServiceCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var result tokens3.CreateResult
|
||||||
|
switch opts.(type) {
|
||||||
|
case *ec2tokens.AuthOptions:
|
||||||
|
result = ec2tokens.Create(v3Client, opts)
|
||||||
|
case *oauth1.AuthOptions:
|
||||||
|
result = oauth1.Create(v3Client, opts)
|
||||||
|
default:
|
||||||
|
result = tokens3.Create(v3Client, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SetTokenAndAuthResult(result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err = result.ExtractServiceCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.CanReauth() {
|
if opts.CanReauth() {
|
||||||
|
@ -217,6 +265,14 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
||||||
o := *ot
|
o := *ot
|
||||||
o.AllowReauth = false
|
o.AllowReauth = false
|
||||||
tao = &o
|
tao = &o
|
||||||
|
case *ec2tokens.AuthOptions:
|
||||||
|
o := *ot
|
||||||
|
o.AllowReauth = false
|
||||||
|
tao = &o
|
||||||
|
case *oauth1.AuthOptions:
|
||||||
|
o := *ot
|
||||||
|
o.AllowReauth = false
|
||||||
|
tao = &o
|
||||||
default:
|
default:
|
||||||
tao = opts
|
tao = opts
|
||||||
}
|
}
|
||||||
|
@ -304,6 +360,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) {
|
||||||
|
@ -383,7 +451,11 @@ func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi
|
||||||
// load balancer service.
|
// load balancer service.
|
||||||
func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
sc, err := initClientOpts(client, eo, "load-balancer")
|
sc, err := initClientOpts(client, eo, "load-balancer")
|
||||||
sc.ResourceBase = sc.Endpoint + "v2.0/"
|
|
||||||
|
// Fixes edge case having an OpenStack lb endpoint with trailing version number.
|
||||||
|
endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1)
|
||||||
|
|
||||||
|
sc.ResourceBase = endpoint + "v2.0/"
|
||||||
return sc, err
|
return sc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,3 +496,8 @@ func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.Endp
|
||||||
func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
return initClientOpts(client, eo, "workflowv2")
|
return initClientOpts(client, eo, "workflowv2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPlacementV1 creates a ServiceClient that may be used with the placement package.
|
||||||
|
func NewPlacementV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
return initClientOpts(client, eo, "placement")
|
||||||
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
Package attachinterfaces provides the ability to retrieve and manage network
|
|
||||||
interfaces through Nova.
|
|
||||||
|
|
||||||
Example of Listing a Server's Interfaces
|
|
||||||
|
|
||||||
serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f"
|
|
||||||
allPages, err := attachinterfaces.List(computeClient, serverID).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allInterfaces, err := attachinterfaces.ExtractInterfaces(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, interface := range allInterfaces {
|
|
||||||
fmt.Printf("%+v\n", interface)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Get a Server's Interface
|
|
||||||
|
|
||||||
portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e"
|
|
||||||
serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f"
|
|
||||||
interface, err := attachinterfaces.Get(computeClient, serverID, portID).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a new Interface attachment on the Server
|
|
||||||
|
|
||||||
networkID := "8a5fe506-7e9f-4091-899b-96336909d93c"
|
|
||||||
serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f"
|
|
||||||
attachOpts := attachinterfaces.CreateOpts{
|
|
||||||
NetworkID: networkID,
|
|
||||||
}
|
|
||||||
interface, err := attachinterfaces.Create(computeClient, serverID, attachOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Delete an Interface attachment from the Server
|
|
||||||
|
|
||||||
portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e"
|
|
||||||
serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f"
|
|
||||||
err := attachinterfaces.Delete(computeClient, serverID, portID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package attachinterfaces
|
|
|
@ -1,72 +0,0 @@
|
||||||
package attachinterfaces
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List makes a request against the nova API to list the server's interfaces.
|
|
||||||
func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listInterfaceURL(client, serverID), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return InterfacePage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get requests details on a single interface attachment by the server and port IDs.
|
|
||||||
func Get(client *gophercloud.ServiceClient, serverID, portID string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToAttachInterfacesCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts specifies parameters of a new interface attachment.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// PortID is the ID of the port for which you want to create an interface.
|
|
||||||
// The NetworkID and PortID parameters are mutually exclusive.
|
|
||||||
// If you do not specify the PortID parameter, the OpenStack Networking API
|
|
||||||
// v2.0 allocates a port and creates an interface for it on the network.
|
|
||||||
PortID string `json:"port_id,omitempty"`
|
|
||||||
|
|
||||||
// NetworkID is the ID of the network for which you want to create an interface.
|
|
||||||
// The NetworkID and PortID parameters are mutually exclusive.
|
|
||||||
// If you do not specify the NetworkID parameter, the OpenStack Networking
|
|
||||||
// API v2.0 uses the network information cache that is associated with the instance.
|
|
||||||
NetworkID string `json:"net_id,omitempty"`
|
|
||||||
|
|
||||||
// Slice of FixedIPs. If you request a specific FixedIP address without a
|
|
||||||
// NetworkID, the request returns a Bad Request (400) response code.
|
|
||||||
// Note: this uses the FixedIP struct, but only the IPAddress field can be used.
|
|
||||||
FixedIPs []FixedIP `json:"fixed_ips,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToAttachInterfacesCreateMap constructs a request body from CreateOpts.
|
|
||||||
func (opts CreateOpts) ToAttachInterfacesCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "interfaceAttachment")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create requests the creation of a new interface attachment on the server.
|
|
||||||
func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToAttachInterfacesCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete makes a request against the nova API to detach a single interface from the server.
|
|
||||||
// It needs server and port IDs to make a such request.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, serverID, portID string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteInterfaceURL(client, serverID, portID), nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
package attachinterfaces
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
type attachInterfaceResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract interprets any attachInterfaceResult as an Interface, if possible.
|
|
||||||
func (r attachInterfaceResult) Extract() (*Interface, error) {
|
|
||||||
var s struct {
|
|
||||||
Interface *Interface `json:"interfaceAttachment"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.Interface, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the response from a Get operation. Call its Extract
|
|
||||||
// method to interpret it as an Interface.
|
|
||||||
type GetResult struct {
|
|
||||||
attachInterfaceResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult is the response from a Create operation. Call its Extract
|
|
||||||
// method to interpret it as an Interface.
|
|
||||||
type CreateResult struct {
|
|
||||||
attachInterfaceResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the call succeeded or failed.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixedIP represents a Fixed IP Address.
|
|
||||||
// This struct is also used when creating an attachment,
|
|
||||||
// but it is not possible to specify a SubnetID.
|
|
||||||
type FixedIP struct {
|
|
||||||
SubnetID string `json:"subnet_id,omitempty"`
|
|
||||||
IPAddress string `json:"ip_address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface represents a network interface on a server.
|
|
||||||
type Interface struct {
|
|
||||||
PortState string `json:"port_state"`
|
|
||||||
FixedIPs []FixedIP `json:"fixed_ips"`
|
|
||||||
PortID string `json:"port_id"`
|
|
||||||
NetID string `json:"net_id"`
|
|
||||||
MACAddr string `json:"mac_addr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InterfacePage abstracts the raw results of making a List() request against
|
|
||||||
// the API.
|
|
||||||
//
|
|
||||||
// As OpenStack extensions may freely alter the response bodies of structures
|
|
||||||
// returned to the client, you may only safely access the data provided through
|
|
||||||
// the ExtractInterfaces call.
|
|
||||||
type InterfacePage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns true if an InterfacePage contains no interfaces.
|
|
||||||
func (r InterfacePage) IsEmpty() (bool, error) {
|
|
||||||
interfaces, err := ExtractInterfaces(r)
|
|
||||||
return len(interfaces) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractInterfaces interprets the results of a single page from a List() call,
|
|
||||||
// producing a slice of Interface structs.
|
|
||||||
func ExtractInterfaces(r pagination.Page) ([]Interface, error) {
|
|
||||||
var s struct {
|
|
||||||
Interfaces []Interface `json:"interfaceAttachments"`
|
|
||||||
}
|
|
||||||
err := (r.(InterfacePage)).ExtractInto(&s)
|
|
||||||
return s.Interfaces, err
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package attachinterfaces
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string {
|
|
||||||
return client.ServiceURL("servers", serverID, "os-interface")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string {
|
|
||||||
return client.ServiceURL("servers", serverID, "os-interface", portID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createInterfaceURL(client *gophercloud.ServiceClient, serverID string) string {
|
|
||||||
return client.ServiceURL("servers", serverID, "os-interface")
|
|
||||||
}
|
|
||||||
func deleteInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string {
|
|
||||||
return client.ServiceURL("servers", serverID, "os-interface", portID)
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
Package availabilityzones provides the ability to get lists and detailed
|
|
||||||
availability zone information and to extend a server result with
|
|
||||||
availability zone information.
|
|
||||||
|
|
||||||
Example of Extend server result with Availability Zone Information:
|
|
||||||
|
|
||||||
type ServerWithAZ struct {
|
|
||||||
servers.Server
|
|
||||||
availabilityzones.ServerAvailabilityZoneExt
|
|
||||||
}
|
|
||||||
|
|
||||||
var allServers []ServerWithAZ
|
|
||||||
|
|
||||||
allPages, err := servers.List(client, nil).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic("Unable to retrieve servers: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = servers.ExtractServersInto(allPages, &allServers)
|
|
||||||
if err != nil {
|
|
||||||
panic("Unable to extract servers: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, server := range allServers {
|
|
||||||
fmt.Println(server.AvailabilityZone)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example of Get Availability Zone Information
|
|
||||||
|
|
||||||
allPages, err := availabilityzones.List(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zoneInfo := range availabilityZoneInfo {
|
|
||||||
fmt.Printf("%+v\n", zoneInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example of Get Detailed Availability Zone Information
|
|
||||||
|
|
||||||
allPages, err := availabilityzones.ListDetail(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zoneInfo := range availabilityZoneInfo {
|
|
||||||
fmt.Printf("%+v\n", zoneInfo)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package availabilityzones
|
|
|
@ -1,20 +0,0 @@
|
||||||
package availabilityzones
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List will return the existing availability zones.
|
|
||||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return AvailabilityZonePage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListDetail will return the existing availability zones with detailed information.
|
|
||||||
func ListDetail(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listDetailURL(client), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return AvailabilityZonePage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package availabilityzones
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ServerAvailabilityZoneExt is an extension to the base Server object.
|
|
||||||
type ServerAvailabilityZoneExt struct {
|
|
||||||
// AvailabilityZone is the availabilty zone the server is in.
|
|
||||||
AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServiceState represents the state of a service in an AvailabilityZone.
|
|
||||||
type ServiceState struct {
|
|
||||||
Active bool `json:"active"`
|
|
||||||
Available bool `json:"available"`
|
|
||||||
UpdatedAt time.Time `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON to override default
|
|
||||||
func (r *ServiceState) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp ServiceState
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*r = ServiceState(s.tmp)
|
|
||||||
|
|
||||||
r.UpdatedAt = time.Time(s.UpdatedAt)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Services is a map of services contained in an AvailabilityZone.
|
|
||||||
type Services map[string]ServiceState
|
|
||||||
|
|
||||||
// Hosts is map of hosts/nodes contained in an AvailabilityZone.
|
|
||||||
// Each host can have multiple services.
|
|
||||||
type Hosts map[string]Services
|
|
||||||
|
|
||||||
// ZoneState represents the current state of the availability zone.
|
|
||||||
type ZoneState struct {
|
|
||||||
// Returns true if the availability zone is available
|
|
||||||
Available bool `json:"available"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AvailabilityZone contains all the information associated with an OpenStack
|
|
||||||
// AvailabilityZone.
|
|
||||||
type AvailabilityZone struct {
|
|
||||||
Hosts Hosts `json:"hosts"`
|
|
||||||
// The availability zone name
|
|
||||||
ZoneName string `json:"zoneName"`
|
|
||||||
ZoneState ZoneState `json:"zoneState"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AvailabilityZonePage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a
|
|
||||||
// single page of results.
|
|
||||||
func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) {
|
|
||||||
var s struct {
|
|
||||||
AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"`
|
|
||||||
}
|
|
||||||
err := (r.(AvailabilityZonePage)).ExtractInto(&s)
|
|
||||||
return s.AvailabilityZoneInfo, err
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package availabilityzones
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("os-availability-zone")
|
|
||||||
}
|
|
||||||
|
|
||||||
func listDetailURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("os-availability-zone", "detail")
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
/*
|
|
||||||
Package bootfromvolume extends a server create request with the ability to
|
|
||||||
specify block device options. This can be used to boot a server from a block
|
|
||||||
storage volume as well as specify multiple ephemeral disks upon creation.
|
|
||||||
|
|
||||||
It is recommended to refer to the Block Device Mapping documentation to see
|
|
||||||
all possible ways to configure a server's block devices at creation time:
|
|
||||||
|
|
||||||
https://docs.openstack.org/nova/latest/user/block-device-mapping.html
|
|
||||||
|
|
||||||
Note that this package implements `block_device_mapping_v2`.
|
|
||||||
|
|
||||||
Example of Creating a Server From an Image
|
|
||||||
|
|
||||||
This example will boot a server from an image and use a standard ephemeral
|
|
||||||
disk as the server's root disk. This is virtually no different than creating
|
|
||||||
a server without using block device mappings.
|
|
||||||
|
|
||||||
blockDevices := []bootfromvolume.BlockDevice{
|
|
||||||
bootfromvolume.BlockDevice{
|
|
||||||
BootIndex: 0,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
DestinationType: bootfromvolume.DestinationLocal,
|
|
||||||
SourceType: bootfromvolume.SourceImage,
|
|
||||||
UUID: "image-uuid",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_name",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
ImageRef: "image-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := bootfromvolume.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
BlockDevice: blockDevices,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := bootfromvolume.Create(client, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example of Creating a Server From a New Volume
|
|
||||||
|
|
||||||
This example will create a block storage volume based on the given Image. The
|
|
||||||
server will use this volume as its root disk.
|
|
||||||
|
|
||||||
blockDevices := []bootfromvolume.BlockDevice{
|
|
||||||
bootfromvolume.BlockDevice{
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
DestinationType: bootfromvolume.DestinationVolume,
|
|
||||||
SourceType: bootfromvolume.SourceImage,
|
|
||||||
UUID: "image-uuid",
|
|
||||||
VolumeSize: 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_name",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := bootfromvolume.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
BlockDevice: blockDevices,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := bootfromvolume.Create(client, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example of Creating a Server From an Existing Volume
|
|
||||||
|
|
||||||
This example will create a server with an existing volume as its root disk.
|
|
||||||
|
|
||||||
blockDevices := []bootfromvolume.BlockDevice{
|
|
||||||
bootfromvolume.BlockDevice{
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
DestinationType: bootfromvolume.DestinationVolume,
|
|
||||||
SourceType: bootfromvolume.SourceVolume,
|
|
||||||
UUID: "volume-uuid",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_name",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := bootfromvolume.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
BlockDevice: blockDevices,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := bootfromvolume.Create(client, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example of Creating a Server with Multiple Ephemeral Disks
|
|
||||||
|
|
||||||
This example will create a server with multiple ephemeral disks. The first
|
|
||||||
block device will be based off of an existing Image. Each additional
|
|
||||||
ephemeral disks must have an index of -1.
|
|
||||||
|
|
||||||
blockDevices := []bootfromvolume.BlockDevice{
|
|
||||||
bootfromvolume.BlockDevice{
|
|
||||||
BootIndex: 0,
|
|
||||||
DestinationType: bootfromvolume.DestinationLocal,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
SourceType: bootfromvolume.SourceImage,
|
|
||||||
UUID: "image-uuid",
|
|
||||||
VolumeSize: 5,
|
|
||||||
},
|
|
||||||
bootfromvolume.BlockDevice{
|
|
||||||
BootIndex: -1,
|
|
||||||
DestinationType: bootfromvolume.DestinationLocal,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
GuestFormat: "ext4",
|
|
||||||
SourceType: bootfromvolume.SourceBlank,
|
|
||||||
VolumeSize: 1,
|
|
||||||
},
|
|
||||||
bootfromvolume.BlockDevice{
|
|
||||||
BootIndex: -1,
|
|
||||||
DestinationType: bootfromvolume.DestinationLocal,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
GuestFormat: "ext4",
|
|
||||||
SourceType: bootfromvolume.SourceBlank,
|
|
||||||
VolumeSize: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_name",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
ImageRef: "image-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := bootfromvolume.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
BlockDevice: blockDevices,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := bootfromvolume.Create(client, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package bootfromvolume
|
|
|
@ -1,128 +0,0 @@
|
||||||
package bootfromvolume
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// DestinationType represents the type of medium being used as the
|
|
||||||
// destination of the bootable device.
|
|
||||||
DestinationType string
|
|
||||||
|
|
||||||
// SourceType represents the type of medium being used as the source of the
|
|
||||||
// bootable device.
|
|
||||||
SourceType string
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DestinationLocal DestinationType is for using an ephemeral disk as the
|
|
||||||
// destination.
|
|
||||||
DestinationLocal DestinationType = "local"
|
|
||||||
|
|
||||||
// DestinationVolume DestinationType is for using a volume as the destination.
|
|
||||||
DestinationVolume DestinationType = "volume"
|
|
||||||
|
|
||||||
// SourceBlank SourceType is for a "blank" or empty source.
|
|
||||||
SourceBlank SourceType = "blank"
|
|
||||||
|
|
||||||
// SourceImage SourceType is for using images as the source of a block device.
|
|
||||||
SourceImage SourceType = "image"
|
|
||||||
|
|
||||||
// SourceSnapshot SourceType is for using a volume snapshot as the source of
|
|
||||||
// a block device.
|
|
||||||
SourceSnapshot SourceType = "snapshot"
|
|
||||||
|
|
||||||
// SourceVolume SourceType is for using a volume as the source of block
|
|
||||||
// device.
|
|
||||||
SourceVolume SourceType = "volume"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlockDevice is a structure with options for creating block devices in a
|
|
||||||
// server. The block device may be created from an image, snapshot, new volume,
|
|
||||||
// or existing volume. The destination may be a new volume, existing volume
|
|
||||||
// which will be attached to the instance, ephemeral disk, or boot device.
|
|
||||||
type BlockDevice struct {
|
|
||||||
// SourceType must be one of: "volume", "snapshot", "image", or "blank".
|
|
||||||
SourceType SourceType `json:"source_type" required:"true"`
|
|
||||||
|
|
||||||
// UUID is the unique identifier for the existing volume, snapshot, or
|
|
||||||
// image (see above).
|
|
||||||
UUID string `json:"uuid,omitempty"`
|
|
||||||
|
|
||||||
// BootIndex is the boot index. It defaults to 0.
|
|
||||||
BootIndex int `json:"boot_index"`
|
|
||||||
|
|
||||||
// DeleteOnTermination specifies whether or not to delete the attached volume
|
|
||||||
// when the server is deleted. Defaults to `false`.
|
|
||||||
DeleteOnTermination bool `json:"delete_on_termination"`
|
|
||||||
|
|
||||||
// DestinationType is the type that gets created. Possible values are "volume"
|
|
||||||
// and "local".
|
|
||||||
DestinationType DestinationType `json:"destination_type,omitempty"`
|
|
||||||
|
|
||||||
// GuestFormat specifies the format of the block device.
|
|
||||||
GuestFormat string `json:"guest_format,omitempty"`
|
|
||||||
|
|
||||||
// VolumeSize is the size of the volume to create (in gigabytes). This can be
|
|
||||||
// omitted for existing volumes.
|
|
||||||
VolumeSize int `json:"volume_size,omitempty"`
|
|
||||||
|
|
||||||
// DeviceType specifies the device type of the block devices.
|
|
||||||
// Examples of this are disk, cdrom, floppy, lun, etc.
|
|
||||||
DeviceType string `json:"device_type,omitempty"`
|
|
||||||
|
|
||||||
// DiskBus is the bus type of the block devices.
|
|
||||||
// Examples of this are ide, usb, virtio, scsi, etc.
|
|
||||||
DiskBus string `json:"disk_bus,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsExt is a structure that extends the server `CreateOpts` structure
|
|
||||||
// by allowing for a block device mapping.
|
|
||||||
type CreateOptsExt struct {
|
|
||||||
servers.CreateOptsBuilder
|
|
||||||
BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToServerCreateMap adds the block device mapping option to the base server
|
|
||||||
// creation options.
|
|
||||||
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
|
||||||
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.BlockDevice) == 0 {
|
|
||||||
err := gophercloud.ErrMissingInput{}
|
|
||||||
err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice"
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
serverMap := base["server"].(map[string]interface{})
|
|
||||||
|
|
||||||
blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
|
|
||||||
|
|
||||||
for i, bd := range opts.BlockDevice {
|
|
||||||
b, err := gophercloud.BuildRequestBody(bd, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
blockDevice[i] = b
|
|
||||||
}
|
|
||||||
serverMap["block_device_mapping_v2"] = blockDevice
|
|
||||||
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create requests the creation of a server from the given block device mapping.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) {
|
|
||||||
b, err := opts.ToServerCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200, 202},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package bootfromvolume
|
|
||||||
|
|
||||||
import (
|
|
||||||
os "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateResult temporarily contains the response from a Create call.
|
|
||||||
// It embeds the standard servers.CreateResults type and so can be used the
|
|
||||||
// same way as a standard server request result.
|
|
||||||
type CreateResult struct {
|
|
||||||
os.CreateResult
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package bootfromvolume
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL("os-volumes_boot")
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
Package floatingips provides the ability to manage floating ips through the
|
|
||||||
Nova API.
|
|
||||||
|
|
||||||
This API has been deprecated and will be removed from a future release of the
|
|
||||||
Nova API service.
|
|
||||||
|
|
||||||
For environements that support this extension, this package can be used
|
|
||||||
regardless of if either Neutron or nova-network is used as the cloud's network
|
|
||||||
service.
|
|
||||||
|
|
||||||
Example to List Floating IPs
|
|
||||||
|
|
||||||
allPages, err := floatingips.List(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fip := range allFloatingIPs {
|
|
||||||
fmt.Printf("%+v\n", fip)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a Floating IP
|
|
||||||
|
|
||||||
createOpts := floatingips.CreateOpts{
|
|
||||||
Pool: "nova",
|
|
||||||
}
|
|
||||||
|
|
||||||
fip, err := floatingips.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Delete a Floating IP
|
|
||||||
|
|
||||||
err := floatingips.Delete(computeClient, "floatingip-id").ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Associate a Floating IP With a Server
|
|
||||||
|
|
||||||
associateOpts := floatingips.AssociateOpts{
|
|
||||||
FloatingIP: "10.10.10.2",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := floatingips.AssociateInstance(computeClient, "server-id", associateOpts).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Disassociate a Floating IP From a Server
|
|
||||||
|
|
||||||
disassociateOpts := floatingips.DisassociateOpts{
|
|
||||||
FloatingIP: "10.10.10.2",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := floatingips.DisassociateInstance(computeClient, "server-id", disassociateOpts).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package floatingips
|
|
|
@ -1,114 +0,0 @@
|
||||||
package floatingips
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List returns a Pager that allows you to iterate over a collection of FloatingIPs.
|
|
||||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return FloatingIPPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToFloatingIPCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts specifies a Floating IP allocation request.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// Pool is the pool of Floating IPs to allocate one from.
|
|
||||||
Pool string `json:"pool" required:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToFloatingIPCreateMap constructs a request body from CreateOpts.
|
|
||||||
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create requests the creation of a new Floating IP.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToFloatingIPCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns data about a previously created Floating IP.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete requests the deletion of a previous allocated Floating IP.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssociateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Associate request.
|
|
||||||
type AssociateOptsBuilder interface {
|
|
||||||
ToFloatingIPAssociateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssociateOpts specifies the required information to associate a Floating IP with an instance
|
|
||||||
type AssociateOpts struct {
|
|
||||||
// FloatingIP is the Floating IP to associate with an instance.
|
|
||||||
FloatingIP string `json:"address" required:"true"`
|
|
||||||
|
|
||||||
// FixedIP is an optional fixed IP address of the server.
|
|
||||||
FixedIP string `json:"fixed_address,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToFloatingIPAssociateMap constructs a request body from AssociateOpts.
|
|
||||||
func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "addFloatingIp")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssociateInstance pairs an allocated Floating IP with a server.
|
|
||||||
func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) {
|
|
||||||
b, err := opts.ToFloatingIPAssociateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(associateURL(client, serverID), b, nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisassociateOptsBuilder allows extensions to add additional parameters to
|
|
||||||
// the Disassociate request.
|
|
||||||
type DisassociateOptsBuilder interface {
|
|
||||||
ToFloatingIPDisassociateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisassociateOpts specifies the required information to disassociate a
|
|
||||||
// Floating IP with a server.
|
|
||||||
type DisassociateOpts struct {
|
|
||||||
FloatingIP string `json:"address" required:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToFloatingIPDisassociateMap constructs a request body from DisassociateOpts.
|
|
||||||
func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "removeFloatingIp")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisassociateInstance decouples an allocated Floating IP from an instance
|
|
||||||
func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) {
|
|
||||||
b, err := opts.ToFloatingIPDisassociateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package floatingips
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A FloatingIP is an IP that can be associated with a server.
|
|
||||||
type FloatingIP struct {
|
|
||||||
// ID is a unique ID of the Floating IP
|
|
||||||
ID string `json:"-"`
|
|
||||||
|
|
||||||
// FixedIP is a specific IP on the server to pair the Floating IP with.
|
|
||||||
FixedIP string `json:"fixed_ip,omitempty"`
|
|
||||||
|
|
||||||
// InstanceID is the ID of the server that is using the Floating IP.
|
|
||||||
InstanceID string `json:"instance_id"`
|
|
||||||
|
|
||||||
// IP is the actual Floating IP.
|
|
||||||
IP string `json:"ip"`
|
|
||||||
|
|
||||||
// Pool is the pool of Floating IPs that this Floating IP belongs to.
|
|
||||||
Pool string `json:"pool"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *FloatingIP) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp FloatingIP
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
ID interface{} `json:"id"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = FloatingIP(s.tmp)
|
|
||||||
|
|
||||||
switch t := s.ID.(type) {
|
|
||||||
case float64:
|
|
||||||
r.ID = strconv.FormatFloat(t, 'f', -1, 64)
|
|
||||||
case string:
|
|
||||||
r.ID = t
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FloatingIPPage stores a single page of FloatingIPs from a List call.
|
|
||||||
type FloatingIPPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty determines whether or not a FloatingIPsPage is empty.
|
|
||||||
func (page FloatingIPPage) IsEmpty() (bool, error) {
|
|
||||||
va, err := ExtractFloatingIPs(page)
|
|
||||||
return len(va) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractFloatingIPs interprets a page of results as a slice of FloatingIPs.
|
|
||||||
func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) {
|
|
||||||
var s struct {
|
|
||||||
FloatingIPs []FloatingIP `json:"floating_ips"`
|
|
||||||
}
|
|
||||||
err := (r.(FloatingIPPage)).ExtractInto(&s)
|
|
||||||
return s.FloatingIPs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FloatingIPResult is the raw result from a FloatingIP request.
|
|
||||||
type FloatingIPResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract is a method that attempts to interpret any FloatingIP resource
|
|
||||||
// response as a FloatingIP struct.
|
|
||||||
func (r FloatingIPResult) Extract() (*FloatingIP, error) {
|
|
||||||
var s struct {
|
|
||||||
FloatingIP *FloatingIP `json:"floating_ip"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.FloatingIP, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult is the response from a Create operation. Call its Extract method
|
|
||||||
// to interpret it as a FloatingIP.
|
|
||||||
type CreateResult struct {
|
|
||||||
FloatingIPResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the response from a Get operation. Call its Extract method to
|
|
||||||
// interpret it as a FloatingIP.
|
|
||||||
type GetResult struct {
|
|
||||||
FloatingIPResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the call succeeded or failed.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssociateResult is the response from a Delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the call succeeded or failed.
|
|
||||||
type AssociateResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisassociateResult is the response from a Delete operation. Call its
|
|
||||||
// ExtractErr method to determine if the call succeeded or failed.
|
|
||||||
type DisassociateResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package floatingips
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
const resourcePath = "os-floating-ips"
|
|
||||||
|
|
||||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL(resourcePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL(resourcePath, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return getURL(c, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverURL(c *gophercloud.ServiceClient, serverID string) string {
|
|
||||||
return c.ServiceURL("servers/" + serverID + "/action")
|
|
||||||
}
|
|
||||||
|
|
||||||
func associateURL(c *gophercloud.ServiceClient, serverID string) string {
|
|
||||||
return serverURL(c, serverID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func disassociateURL(c *gophercloud.ServiceClient, serverID string) string {
|
|
||||||
return serverURL(c, serverID)
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
Package keypairs provides the ability to manage key pairs as well as create
|
|
||||||
servers with a specified key pair.
|
|
||||||
|
|
||||||
Example to List Key Pairs
|
|
||||||
|
|
||||||
allPages, err := keypairs.List(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allKeyPairs, err := keypairs.ExtractKeyPairs(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, kp := range allKeyPairs {
|
|
||||||
fmt.Printf("%+v\n", kp)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a Key Pair
|
|
||||||
|
|
||||||
createOpts := keypairs.CreateOpts{
|
|
||||||
Name: "keypair-name",
|
|
||||||
}
|
|
||||||
|
|
||||||
keypair, err := keypairs.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%+v", keypair)
|
|
||||||
|
|
||||||
Example to Import a Key Pair
|
|
||||||
|
|
||||||
createOpts := keypairs.CreateOpts{
|
|
||||||
Name: "keypair-name",
|
|
||||||
PublicKey: "public-key",
|
|
||||||
}
|
|
||||||
|
|
||||||
keypair, err := keypairs.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Delete a Key Pair
|
|
||||||
|
|
||||||
err := keypairs.Delete(computeClient, "keypair-name").ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a Server With a Key Pair
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_name",
|
|
||||||
ImageRef: "image-uuid",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := keypairs.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
KeyName: "keypair-name",
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := servers.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package keypairs
|
|
|
@ -1,86 +0,0 @@
|
||||||
package keypairs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateOptsExt adds a KeyPair option to the base CreateOpts.
|
|
||||||
type CreateOptsExt struct {
|
|
||||||
servers.CreateOptsBuilder
|
|
||||||
|
|
||||||
// KeyName is the name of the key pair.
|
|
||||||
KeyName string `json:"key_name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToServerCreateMap adds the key_name to the base server creation options.
|
|
||||||
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
|
||||||
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.KeyName == "" {
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
serverMap := base["server"].(map[string]interface{})
|
|
||||||
serverMap["key_name"] = opts.KeyName
|
|
||||||
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns a Pager that allows you to iterate over a collection of KeyPairs.
|
|
||||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return KeyPairPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToKeyPairCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts specifies KeyPair creation or import parameters.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// Name is a friendly name to refer to this KeyPair in other services.
|
|
||||||
Name string `json:"name" required:"true"`
|
|
||||||
|
|
||||||
// PublicKey [optional] is a pregenerated OpenSSH-formatted public key.
|
|
||||||
// If provided, this key will be imported and no new key will be created.
|
|
||||||
PublicKey string `json:"public_key,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToKeyPairCreateMap constructs a request body from CreateOpts.
|
|
||||||
func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "keypair")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create requests the creation of a new KeyPair on the server, or to import a
|
|
||||||
// pre-existing keypair.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToKeyPairCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns public data about a previously uploaded KeyPair.
|
|
||||||
func Get(client *gophercloud.ServiceClient, name string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, name), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete requests the deletion of a previous stored KeyPair from the server.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteURL(client, name), nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package keypairs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KeyPair is an SSH key known to the OpenStack Cloud that is available to be
|
|
||||||
// injected into servers.
|
|
||||||
type KeyPair struct {
|
|
||||||
// Name is used to refer to this keypair from other services within this
|
|
||||||
// region.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Fingerprint is a short sequence of bytes that can be used to authenticate
|
|
||||||
// or validate a longer public key.
|
|
||||||
Fingerprint string `json:"fingerprint"`
|
|
||||||
|
|
||||||
// PublicKey is the public key from this pair, in OpenSSH format.
|
|
||||||
// "ssh-rsa AAAAB3Nz..."
|
|
||||||
PublicKey string `json:"public_key"`
|
|
||||||
|
|
||||||
// PrivateKey is the private key from this pair, in PEM format.
|
|
||||||
// "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..."
|
|
||||||
// It is only present if this KeyPair was just returned from a Create call.
|
|
||||||
PrivateKey string `json:"private_key"`
|
|
||||||
|
|
||||||
// UserID is the user who owns this KeyPair.
|
|
||||||
UserID string `json:"user_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyPairPage stores a single page of all KeyPair results from a List call.
|
|
||||||
// Use the ExtractKeyPairs function to convert the results to a slice of
|
|
||||||
// KeyPairs.
|
|
||||||
type KeyPairPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty determines whether or not a KeyPairPage is empty.
|
|
||||||
func (page KeyPairPage) IsEmpty() (bool, error) {
|
|
||||||
ks, err := ExtractKeyPairs(page)
|
|
||||||
return len(ks) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractKeyPairs interprets a page of results as a slice of KeyPairs.
|
|
||||||
func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) {
|
|
||||||
type pair struct {
|
|
||||||
KeyPair KeyPair `json:"keypair"`
|
|
||||||
}
|
|
||||||
var s struct {
|
|
||||||
KeyPairs []pair `json:"keypairs"`
|
|
||||||
}
|
|
||||||
err := (r.(KeyPairPage)).ExtractInto(&s)
|
|
||||||
results := make([]KeyPair, len(s.KeyPairs))
|
|
||||||
for i, pair := range s.KeyPairs {
|
|
||||||
results[i] = pair.KeyPair
|
|
||||||
}
|
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type keyPairResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract is a method that attempts to interpret any KeyPair resource response
|
|
||||||
// as a KeyPair struct.
|
|
||||||
func (r keyPairResult) Extract() (*KeyPair, error) {
|
|
||||||
var s struct {
|
|
||||||
KeyPair *KeyPair `json:"keypair"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.KeyPair, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult is the response from a Create operation. Call its Extract method
|
|
||||||
// to interpret it as a KeyPair.
|
|
||||||
type CreateResult struct {
|
|
||||||
keyPairResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the response from a Get operation. Call its Extract method to
|
|
||||||
// interpret it as a KeyPair.
|
|
||||||
type GetResult struct {
|
|
||||||
keyPairResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the call succeeded or failed.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package keypairs
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
const resourcePath = "os-keypairs"
|
|
||||||
|
|
||||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL(resourcePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, name string) string {
|
|
||||||
return c.ServiceURL(resourcePath, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, name string) string {
|
|
||||||
return getURL(c, name)
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
Package schedulerhints extends the server create request with the ability to
|
|
||||||
specify additional parameters which determine where the server will be
|
|
||||||
created in the OpenStack cloud.
|
|
||||||
|
|
||||||
Example to Add a Server to a Server Group
|
|
||||||
|
|
||||||
schedulerHints := schedulerhints.SchedulerHints{
|
|
||||||
Group: "servergroup-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_name",
|
|
||||||
ImageRef: "image-uuid",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := schedulerhints.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
SchedulerHints: schedulerHints,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := servers.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Place Server B on a Different Host than Server A
|
|
||||||
|
|
||||||
schedulerHints := schedulerhints.SchedulerHints{
|
|
||||||
DifferentHost: []string{
|
|
||||||
"server-a-uuid",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_b",
|
|
||||||
ImageRef: "image-uuid",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := schedulerhints.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
SchedulerHints: schedulerHints,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := servers.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Place Server B on the Same Host as Server A
|
|
||||||
|
|
||||||
schedulerHints := schedulerhints.SchedulerHints{
|
|
||||||
SameHost: []string{
|
|
||||||
"server-a-uuid",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateOpts := servers.CreateOpts{
|
|
||||||
Name: "server_b",
|
|
||||||
ImageRef: "image-uuid",
|
|
||||||
FlavorRef: "flavor-uuid",
|
|
||||||
}
|
|
||||||
|
|
||||||
createOpts := schedulerhints.CreateOptsExt{
|
|
||||||
CreateOptsBuilder: serverCreateOpts,
|
|
||||||
SchedulerHints: schedulerHints,
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err := servers.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package schedulerhints
|
|
|
@ -1,164 +0,0 @@
|
||||||
package schedulerhints
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SchedulerHints represents a set of scheduling hints that are passed to the
|
|
||||||
// OpenStack scheduler.
|
|
||||||
type SchedulerHints struct {
|
|
||||||
// Group specifies a Server Group to place the instance in.
|
|
||||||
Group string
|
|
||||||
|
|
||||||
// DifferentHost will place the instance on a compute node that does not
|
|
||||||
// host the given instances.
|
|
||||||
DifferentHost []string
|
|
||||||
|
|
||||||
// SameHost will place the instance on a compute node that hosts the given
|
|
||||||
// instances.
|
|
||||||
SameHost []string
|
|
||||||
|
|
||||||
// Query is a conditional statement that results in compute nodes able to
|
|
||||||
// host the instance.
|
|
||||||
Query []interface{}
|
|
||||||
|
|
||||||
// TargetCell specifies a cell name where the instance will be placed.
|
|
||||||
TargetCell string `json:"target_cell,omitempty"`
|
|
||||||
|
|
||||||
// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
|
|
||||||
BuildNearHostIP string
|
|
||||||
|
|
||||||
// AdditionalProperies are arbitrary key/values that are not validated by nova.
|
|
||||||
AdditionalProperties map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder builds the scheduler hints into a serializable format.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToServerSchedulerHintsCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
|
|
||||||
func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) {
|
|
||||||
sh := make(map[string]interface{})
|
|
||||||
|
|
||||||
uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
|
|
||||||
|
|
||||||
if opts.Group != "" {
|
|
||||||
if !uuidRegex.MatchString(opts.Group) {
|
|
||||||
err := gophercloud.ErrInvalidInput{}
|
|
||||||
err.Argument = "schedulerhints.SchedulerHints.Group"
|
|
||||||
err.Value = opts.Group
|
|
||||||
err.Info = "Group must be a UUID"
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sh["group"] = opts.Group
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.DifferentHost) > 0 {
|
|
||||||
for _, diffHost := range opts.DifferentHost {
|
|
||||||
if !uuidRegex.MatchString(diffHost) {
|
|
||||||
err := gophercloud.ErrInvalidInput{}
|
|
||||||
err.Argument = "schedulerhints.SchedulerHints.DifferentHost"
|
|
||||||
err.Value = opts.DifferentHost
|
|
||||||
err.Info = "The hosts must be in UUID format."
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sh["different_host"] = opts.DifferentHost
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.SameHost) > 0 {
|
|
||||||
for _, sameHost := range opts.SameHost {
|
|
||||||
if !uuidRegex.MatchString(sameHost) {
|
|
||||||
err := gophercloud.ErrInvalidInput{}
|
|
||||||
err.Argument = "schedulerhints.SchedulerHints.SameHost"
|
|
||||||
err.Value = opts.SameHost
|
|
||||||
err.Info = "The hosts must be in UUID format."
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sh["same_host"] = opts.SameHost
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Query can be something simple like:
|
|
||||||
[">=", "$free_ram_mb", 1024]
|
|
||||||
|
|
||||||
Or more complex like:
|
|
||||||
['and',
|
|
||||||
['>=', '$free_ram_mb', 1024],
|
|
||||||
['>=', '$free_disk_mb', 200 * 1024]
|
|
||||||
]
|
|
||||||
|
|
||||||
Because of the possible complexity, just make sure the length is a minimum of 3.
|
|
||||||
*/
|
|
||||||
if len(opts.Query) > 0 {
|
|
||||||
if len(opts.Query) < 3 {
|
|
||||||
err := gophercloud.ErrInvalidInput{}
|
|
||||||
err.Argument = "schedulerhints.SchedulerHints.Query"
|
|
||||||
err.Value = opts.Query
|
|
||||||
err.Info = "Must be a conditional statement in the format of [op,variable,value]"
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sh["query"] = opts.Query
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.TargetCell != "" {
|
|
||||||
sh["target_cell"] = opts.TargetCell
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.BuildNearHostIP != "" {
|
|
||||||
if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
|
|
||||||
err := gophercloud.ErrInvalidInput{}
|
|
||||||
err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP"
|
|
||||||
err.Value = opts.BuildNearHostIP
|
|
||||||
err.Info = "Must be a valid subnet in the form 192.168.1.1/24"
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ipParts := strings.Split(opts.BuildNearHostIP, "/")
|
|
||||||
sh["build_near_host_ip"] = ipParts[0]
|
|
||||||
sh["cidr"] = "/" + ipParts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.AdditionalProperties != nil {
|
|
||||||
for k, v := range opts.AdditionalProperties {
|
|
||||||
sh[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sh, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
|
|
||||||
type CreateOptsExt struct {
|
|
||||||
servers.CreateOptsBuilder
|
|
||||||
|
|
||||||
// SchedulerHints provides a set of hints to the scheduler.
|
|
||||||
SchedulerHints CreateOptsBuilder
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToServerCreateMap adds the SchedulerHints option to the base server creation options.
|
|
||||||
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
|
||||||
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(schedulerHints) == 0 {
|
|
||||||
return base, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
base["os:scheduler_hints"] = schedulerHints
|
|
||||||
|
|
||||||
return base, nil
|
|
||||||
}
|
|
112
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/doc.go
generated
vendored
112
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/doc.go
generated
vendored
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
Package secgroups provides the ability to manage security groups through the
|
|
||||||
Nova API.
|
|
||||||
|
|
||||||
This API has been deprecated and will be removed from a future release of the
|
|
||||||
Nova API service.
|
|
||||||
|
|
||||||
For environments that support this extension, this package can be used
|
|
||||||
regardless of if either Neutron or nova-network is used as the cloud's network
|
|
||||||
service.
|
|
||||||
|
|
||||||
Example to List Security Groups
|
|
||||||
|
|
||||||
allPages, err := secroups.List(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allSecurityGroups, err := secgroups.ExtractSecurityGroups(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sg := range allSecurityGroups {
|
|
||||||
fmt.Printf("%+v\n", sg)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to List Security Groups by Server
|
|
||||||
|
|
||||||
serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36"
|
|
||||||
|
|
||||||
allPages, err := secroups.ListByServer(computeClient, serverID).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allSecurityGroups, err := secgroups.ExtractSecurityGroups(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sg := range allSecurityGroups {
|
|
||||||
fmt.Printf("%+v\n", sg)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a Security Group
|
|
||||||
|
|
||||||
createOpts := secgroups.CreateOpts{
|
|
||||||
Name: "group_name",
|
|
||||||
Description: "A Security Group",
|
|
||||||
}
|
|
||||||
|
|
||||||
sg, err := secgroups.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a Security Group Rule
|
|
||||||
|
|
||||||
sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141"
|
|
||||||
|
|
||||||
createOpts := secgroups.CreateRuleOpts{
|
|
||||||
ParentGroupID: sgID,
|
|
||||||
FromPort: 22,
|
|
||||||
ToPort: 22,
|
|
||||||
IPProtocol: "tcp",
|
|
||||||
CIDR: "0.0.0.0/0",
|
|
||||||
}
|
|
||||||
|
|
||||||
rule, err := secgroups.CreateRule(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Add a Security Group to a Server
|
|
||||||
|
|
||||||
serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36"
|
|
||||||
sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141"
|
|
||||||
|
|
||||||
err := secgroups.AddServer(computeClient, serverID, sgID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Remove a Security Group from a Server
|
|
||||||
|
|
||||||
serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36"
|
|
||||||
sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141"
|
|
||||||
|
|
||||||
err := secgroups.RemoveServer(computeClient, serverID, sgID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Delete a Security Group
|
|
||||||
|
|
||||||
|
|
||||||
sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141"
|
|
||||||
err := secgroups.Delete(computeClient, sgID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Delete a Security Group Rule
|
|
||||||
|
|
||||||
ruleID := "6221fe3e-383d-46c9-a3a6-845e66c1e8b4"
|
|
||||||
err := secgroups.DeleteRule(computeClient, ruleID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package secgroups
|
|
|
@ -1,183 +0,0 @@
|
||||||
package secgroups
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
|
||||||
return SecurityGroupPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// List will return a collection of all the security groups for a particular
|
|
||||||
// tenant.
|
|
||||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return commonList(client, rootURL(client))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByServer will return a collection of all the security groups which are
|
|
||||||
// associated with a particular server.
|
|
||||||
func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
|
|
||||||
return commonList(client, listByServerURL(client, serverID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts is the struct responsible for creating a security group.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// the name of your security group.
|
|
||||||
Name string `json:"name" required:"true"`
|
|
||||||
// the description of your security group.
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToSecGroupCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSecGroupCreateMap builds a request body from CreateOpts.
|
|
||||||
func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "security_group")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create will create a new security group.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToSecGroupCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOpts is the struct responsible for updating an existing security group.
|
|
||||||
type UpdateOpts struct {
|
|
||||||
// the name of your security group.
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
// the description of your security group.
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Update request.
|
|
||||||
type UpdateOptsBuilder interface {
|
|
||||||
ToSecGroupUpdateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSecGroupUpdateMap builds a request body from UpdateOpts.
|
|
||||||
func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "security_group")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update will modify the mutable properties of a security group, notably its
|
|
||||||
// name and description.
|
|
||||||
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
|
||||||
b, err := opts.ToSecGroupUpdateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get will return details for a particular security group.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete will permanently delete a security group from the project.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(resourceURL(client, id), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRuleOpts represents the configuration for adding a new rule to an
|
|
||||||
// existing security group.
|
|
||||||
type CreateRuleOpts struct {
|
|
||||||
// ID is the ID of the group that this rule will be added to.
|
|
||||||
ParentGroupID string `json:"parent_group_id" required:"true"`
|
|
||||||
|
|
||||||
// FromPort is the lower bound of the port range that will be opened.
|
|
||||||
// Use -1 to allow all ICMP traffic.
|
|
||||||
FromPort int `json:"from_port"`
|
|
||||||
|
|
||||||
// ToPort is the upper bound of the port range that will be opened.
|
|
||||||
// Use -1 to allow all ICMP traffic.
|
|
||||||
ToPort int `json:"to_port"`
|
|
||||||
|
|
||||||
// IPProtocol the protocol type that will be allowed, e.g. TCP.
|
|
||||||
IPProtocol string `json:"ip_protocol" required:"true"`
|
|
||||||
|
|
||||||
// CIDR is the network CIDR to allow traffic from.
|
|
||||||
// This is ONLY required if FromGroupID is blank. This represents the IP
|
|
||||||
// range that will be the source of network traffic to your security group.
|
|
||||||
// Use 0.0.0.0/0 to allow all IP addresses.
|
|
||||||
CIDR string `json:"cidr,omitempty" or:"FromGroupID"`
|
|
||||||
|
|
||||||
// FromGroupID represents another security group to allow access.
|
|
||||||
// This is ONLY required if CIDR is blank. This value represents the ID of a
|
|
||||||
// group that forwards traffic to the parent group. So, instead of accepting
|
|
||||||
// network traffic from an entire IP range, you can instead refine the
|
|
||||||
// inbound source by an existing security group.
|
|
||||||
FromGroupID string `json:"group_id,omitempty" or:"CIDR"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRuleOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// CreateRule request.
|
|
||||||
type CreateRuleOptsBuilder interface {
|
|
||||||
ToRuleCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToRuleCreateMap builds a request body from CreateRuleOpts.
|
|
||||||
func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "security_group_rule")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRule will add a new rule to an existing security group (whose ID is
|
|
||||||
// specified in CreateRuleOpts). You have the option of controlling inbound
|
|
||||||
// traffic from either an IP range (CIDR) or from another security group.
|
|
||||||
func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) (r CreateRuleResult) {
|
|
||||||
b, err := opts.ToRuleCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRule will permanently delete a rule from a security group.
|
|
||||||
func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) {
|
|
||||||
_, r.Err = client.Delete(resourceRuleURL(client, id), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func actionMap(prefix, groupName string) map[string]map[string]string {
|
|
||||||
return map[string]map[string]string{
|
|
||||||
prefix + "SecurityGroup": map[string]string{"name": groupName},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddServer will associate a server and a security group, enforcing the
|
|
||||||
// rules of the group on the server.
|
|
||||||
func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) {
|
|
||||||
_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveServer will disassociate a server from a security group.
|
|
||||||
func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) {
|
|
||||||
_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,214 +0,0 @@
|
||||||
package secgroups
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SecurityGroup represents a security group.
|
|
||||||
type SecurityGroup struct {
|
|
||||||
// The unique ID of the group. If Neutron is installed, this ID will be
|
|
||||||
// represented as a string UUID; if Neutron is not installed, it will be a
|
|
||||||
// numeric ID. For the sake of consistency, we always cast it to a string.
|
|
||||||
ID string `json:"-"`
|
|
||||||
|
|
||||||
// The human-readable name of the group, which needs to be unique.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// The human-readable description of the group.
|
|
||||||
Description string `json:"description"`
|
|
||||||
|
|
||||||
// The rules which determine how this security group operates.
|
|
||||||
Rules []Rule `json:"rules"`
|
|
||||||
|
|
||||||
// The ID of the tenant to which this security group belongs.
|
|
||||||
TenantID string `json:"tenant_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SecurityGroup) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp SecurityGroup
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
ID interface{} `json:"id"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = SecurityGroup(s.tmp)
|
|
||||||
|
|
||||||
switch t := s.ID.(type) {
|
|
||||||
case float64:
|
|
||||||
r.ID = strconv.FormatFloat(t, 'f', -1, 64)
|
|
||||||
case string:
|
|
||||||
r.ID = t
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rule represents a security group rule, a policy which determines how a
|
|
||||||
// security group operates and what inbound traffic it allows in.
|
|
||||||
type Rule struct {
|
|
||||||
// The unique ID. If Neutron is installed, this ID will be
|
|
||||||
// represented as a string UUID; if Neutron is not installed, it will be a
|
|
||||||
// numeric ID. For the sake of consistency, we always cast it to a string.
|
|
||||||
ID string `json:"-"`
|
|
||||||
|
|
||||||
// The lower bound of the port range which this security group should open up.
|
|
||||||
FromPort int `json:"from_port"`
|
|
||||||
|
|
||||||
// The upper bound of the port range which this security group should open up.
|
|
||||||
ToPort int `json:"to_port"`
|
|
||||||
|
|
||||||
// The IP protocol (e.g. TCP) which the security group accepts.
|
|
||||||
IPProtocol string `json:"ip_protocol"`
|
|
||||||
|
|
||||||
// The CIDR IP range whose traffic can be received.
|
|
||||||
IPRange IPRange `json:"ip_range"`
|
|
||||||
|
|
||||||
// The security group ID to which this rule belongs.
|
|
||||||
ParentGroupID string `json:"-"`
|
|
||||||
|
|
||||||
// Not documented.
|
|
||||||
Group Group
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Rule) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Rule
|
|
||||||
var s struct {
|
|
||||||
tmp
|
|
||||||
ID interface{} `json:"id"`
|
|
||||||
ParentGroupID interface{} `json:"parent_group_id"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = Rule(s.tmp)
|
|
||||||
|
|
||||||
switch t := s.ID.(type) {
|
|
||||||
case float64:
|
|
||||||
r.ID = strconv.FormatFloat(t, 'f', -1, 64)
|
|
||||||
case string:
|
|
||||||
r.ID = t
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t := s.ParentGroupID.(type) {
|
|
||||||
case float64:
|
|
||||||
r.ParentGroupID = strconv.FormatFloat(t, 'f', -1, 64)
|
|
||||||
case string:
|
|
||||||
r.ParentGroupID = t
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPRange represents the IP range whose traffic will be accepted by the
|
|
||||||
// security group.
|
|
||||||
type IPRange struct {
|
|
||||||
CIDR string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group represents a group.
|
|
||||||
type Group struct {
|
|
||||||
TenantID string `json:"tenant_id"`
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecurityGroupPage is a single page of a SecurityGroup collection.
|
|
||||||
type SecurityGroupPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty determines whether or not a page of Security Groups contains any
|
|
||||||
// results.
|
|
||||||
func (page SecurityGroupPage) IsEmpty() (bool, error) {
|
|
||||||
users, err := ExtractSecurityGroups(page)
|
|
||||||
return len(users) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractSecurityGroups returns a slice of SecurityGroups contained in a
|
|
||||||
// single page of results.
|
|
||||||
func ExtractSecurityGroups(r pagination.Page) ([]SecurityGroup, error) {
|
|
||||||
var s struct {
|
|
||||||
SecurityGroups []SecurityGroup `json:"security_groups"`
|
|
||||||
}
|
|
||||||
err := (r.(SecurityGroupPage)).ExtractInto(&s)
|
|
||||||
return s.SecurityGroups, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult represents the result of a create operation. Call its Extract
|
|
||||||
// method to interpret the result as a SecurityGroup.
|
|
||||||
type CreateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult represents the result of a get operation. Call its Extract
|
|
||||||
// method to interpret the result as a SecurityGroup.
|
|
||||||
type GetResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateResult represents the result of an update operation. Call its Extract
|
|
||||||
// method to interpret the result as a SecurityGroup.
|
|
||||||
type UpdateResult struct {
|
|
||||||
commonResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will extract a SecurityGroup struct from most responses.
|
|
||||||
func (r commonResult) Extract() (*SecurityGroup, error) {
|
|
||||||
var s struct {
|
|
||||||
SecurityGroup *SecurityGroup `json:"security_group"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.SecurityGroup, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRuleResult represents the result when adding rules to a security group.
|
|
||||||
// Call its Extract method to interpret the result as a Rule.
|
|
||||||
type CreateRuleResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract will extract a Rule struct from a CreateRuleResult.
|
|
||||||
func (r CreateRuleResult) Extract() (*Rule, error) {
|
|
||||||
var s struct {
|
|
||||||
Rule *Rule `json:"security_group_rule"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.Rule, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult is the response from delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the request succeeded or failed.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRuleResult is the response from a DeleteRule operation. Call its
|
|
||||||
// ExtractErr method to determine if the request succeeded or failed.
|
|
||||||
type DeleteRuleResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddServerResult is the response from an AddServer operation. Call its
|
|
||||||
// ExtractErr method to determine if the request succeeded or failed.
|
|
||||||
type AddServerResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveServerResult is the response from a RemoveServer operation. Call its
|
|
||||||
// ExtractErr method to determine if the request succeeded or failed.
|
|
||||||
type RemoveServerResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package secgroups
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
const (
|
|
||||||
secgrouppath = "os-security-groups"
|
|
||||||
rulepath = "os-security-group-rules"
|
|
||||||
)
|
|
||||||
|
|
||||||
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL(secgrouppath, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rootURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL(secgrouppath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listByServerURL(c *gophercloud.ServiceClient, serverID string) string {
|
|
||||||
return c.ServiceURL("servers", serverID, secgrouppath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rootRuleURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL(rulepath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceRuleURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL(rulepath, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverActionURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL("servers", id, "action")
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
Package servergroups provides the ability to manage server groups.
|
|
||||||
|
|
||||||
Example to List Server Groups
|
|
||||||
|
|
||||||
allpages, err := servergroups.List(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allServerGroups, err := servergroups.ExtractServerGroups(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sg := range allServerGroups {
|
|
||||||
fmt.Printf("%#v\n", sg)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Create a Server Group
|
|
||||||
|
|
||||||
createOpts := servergroups.CreateOpts{
|
|
||||||
Name: "my_sg",
|
|
||||||
Policies: []string{"anti-affinity"},
|
|
||||||
}
|
|
||||||
|
|
||||||
sg, err := servergroups.Create(computeClient, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Delete a Server Group
|
|
||||||
|
|
||||||
sgID := "7a6f29ad-e34d-4368-951a-58a08f11cfb7"
|
|
||||||
err := servergroups.Delete(computeClient, sgID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package servergroups
|
|
|
@ -1,59 +0,0 @@
|
||||||
package servergroups
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List returns a Pager that allows you to iterate over a collection of
|
|
||||||
// ServerGroups.
|
|
||||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return ServerGroupPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add additional parameters to the
|
|
||||||
// Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToServerGroupCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts specifies Server Group creation parameters.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// Name is the name of the server group
|
|
||||||
Name string `json:"name" required:"true"`
|
|
||||||
|
|
||||||
// Policies are the server group policies
|
|
||||||
Policies []string `json:"policies" required:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToServerGroupCreateMap constructs a request body from CreateOpts.
|
|
||||||
func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "server_group")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create requests the creation of a new Server Group.
|
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToServerGroupCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns data about a previously created ServerGroup.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete requests the deletion of a previously allocated ServerGroup.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
package servergroups
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A ServerGroup creates a policy for instance placement in the cloud.
|
|
||||||
type ServerGroup struct {
|
|
||||||
// ID is the unique ID of the Server Group.
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
// Name is the common name of the server group.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Polices are the group policies.
|
|
||||||
//
|
|
||||||
// Normally a single policy is applied:
|
|
||||||
//
|
|
||||||
// "affinity" will place all servers within the server group on the
|
|
||||||
// same compute node.
|
|
||||||
//
|
|
||||||
// "anti-affinity" will place servers within the server group on different
|
|
||||||
// compute nodes.
|
|
||||||
Policies []string `json:"policies"`
|
|
||||||
|
|
||||||
// Members are the members of the server group.
|
|
||||||
Members []string `json:"members"`
|
|
||||||
|
|
||||||
// Metadata includes a list of all user-specified key-value pairs attached
|
|
||||||
// to the Server Group.
|
|
||||||
Metadata map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerGroupPage stores a single page of all ServerGroups results from a
|
|
||||||
// List call.
|
|
||||||
type ServerGroupPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty determines whether or not a ServerGroupsPage is empty.
|
|
||||||
func (page ServerGroupPage) IsEmpty() (bool, error) {
|
|
||||||
va, err := ExtractServerGroups(page)
|
|
||||||
return len(va) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractServerGroups interprets a page of results as a slice of
|
|
||||||
// ServerGroups.
|
|
||||||
func ExtractServerGroups(r pagination.Page) ([]ServerGroup, error) {
|
|
||||||
var s struct {
|
|
||||||
ServerGroups []ServerGroup `json:"server_groups"`
|
|
||||||
}
|
|
||||||
err := (r.(ServerGroupPage)).ExtractInto(&s)
|
|
||||||
return s.ServerGroups, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerGroupResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract is a method that attempts to interpret any Server Group resource
|
|
||||||
// response as a ServerGroup struct.
|
|
||||||
func (r ServerGroupResult) Extract() (*ServerGroup, error) {
|
|
||||||
var s struct {
|
|
||||||
ServerGroup *ServerGroup `json:"server_group"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.ServerGroup, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult is the response from a Create operation. Call its Extract method
|
|
||||||
// to interpret it as a ServerGroup.
|
|
||||||
type CreateResult struct {
|
|
||||||
ServerGroupResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the response from a Get operation. Call its Extract method to
|
|
||||||
// interpret it as a ServerGroup.
|
|
||||||
type GetResult struct {
|
|
||||||
ServerGroupResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the call succeeded or failed.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package servergroups
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
const resourcePath = "os-server-groups"
|
|
||||||
|
|
||||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL(resourcePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL(resourcePath, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return getURL(c, id)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
/*
|
|
||||||
Package startstop provides functionality to start and stop servers that have
|
|
||||||
been provisioned by the OpenStack Compute service.
|
|
||||||
|
|
||||||
Example to Stop and Start a Server
|
|
||||||
|
|
||||||
serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e"
|
|
||||||
|
|
||||||
err := startstop.Stop(computeClient, serverID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := startstop.Start(computeClient, serverID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package startstop
|
|
|
@ -1,19 +0,0 @@
|
||||||
package startstop
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
func actionURL(client *gophercloud.ServiceClient, id string) string {
|
|
||||||
return client.ServiceURL("servers", id, "action")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start is the operation responsible for starting a Compute server.
|
|
||||||
func Start(client *gophercloud.ServiceClient, id string) (r StartResult) {
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop is the operation responsible for stopping a Compute server.
|
|
||||||
func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) {
|
|
||||||
_, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package startstop
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
// StartResult is the response from a Start operation. Call its ExtractErr
|
|
||||||
// method to determine if the request succeeded or failed.
|
|
||||||
type StartResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopResult is the response from Stop operation. Call its ExtractErr
|
|
||||||
// method to determine if the request succeeded or failed.
|
|
||||||
type StopResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
Package tenantnetworks provides the ability for tenants to see information
|
|
||||||
about the networks they have access to.
|
|
||||||
|
|
||||||
This is a deprecated API and will be removed from the Nova API service in a
|
|
||||||
future version.
|
|
||||||
|
|
||||||
This API works in both Neutron and nova-network based OpenStack clouds.
|
|
||||||
|
|
||||||
Example to List Networks Available to a Tenant
|
|
||||||
|
|
||||||
allPages, err := tenantnetworks.List(computeClient).AllPages()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
allNetworks, err := tenantnetworks.ExtractNetworks(allPages)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, network := range allNetworks {
|
|
||||||
fmt.Printf("%+v\n", network)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package tenantnetworks
|
|
|
@ -1,19 +0,0 @@
|
||||||
package tenantnetworks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List returns a Pager that allows you to iterate over a collection of Networks.
|
|
||||||
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return NetworkPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns data about a previously created Network.
|
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package tenantnetworks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Network represents a network that a server communicates on.
|
|
||||||
type Network struct {
|
|
||||||
// CIDR is the IPv4 subnet.
|
|
||||||
CIDR string `json:"cidr"`
|
|
||||||
|
|
||||||
// ID is the UUID of the network.
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
// Name is the common name that the network has.
|
|
||||||
Name string `json:"label"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkPage stores a single page of all Networks results from a List call.
|
|
||||||
type NetworkPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty determines whether or not a NetworkPage is empty.
|
|
||||||
func (page NetworkPage) IsEmpty() (bool, error) {
|
|
||||||
va, err := ExtractNetworks(page)
|
|
||||||
return len(va) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractNetworks interprets a page of results as a slice of Network.
|
|
||||||
func ExtractNetworks(r pagination.Page) ([]Network, error) {
|
|
||||||
var s struct {
|
|
||||||
Networks []Network `json:"networks"`
|
|
||||||
}
|
|
||||||
err := (r.(NetworkPage)).ExtractInto(&s)
|
|
||||||
return s.Networks, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type NetworkResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract is a method that attempts to interpret any Network resource response
|
|
||||||
// as a Network struct.
|
|
||||||
func (r NetworkResult) Extract() (*Network, error) {
|
|
||||||
var s struct {
|
|
||||||
Network *Network `json:"network"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.Network, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the response from a Get operation. Call its Extract method to
|
|
||||||
// interpret it as a Network.
|
|
||||||
type GetResult struct {
|
|
||||||
NetworkResult
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package tenantnetworks
|
|
||||||
|
|
||||||
import "github.com/gophercloud/gophercloud"
|
|
||||||
|
|
||||||
const resourcePath = "os-tenant-networks"
|
|
||||||
|
|
||||||
func resourceURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return c.ServiceURL(resourcePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
|
||||||
return resourceURL(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getURL(c *gophercloud.ServiceClient, id string) string {
|
|
||||||
return c.ServiceURL(resourcePath, id)
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
Package volumeattach provides the ability to attach and detach volumes
|
|
||||||
from servers.
|
|
||||||
|
|
||||||
Example to Attach a Volume
|
|
||||||
|
|
||||||
serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d"
|
|
||||||
volumeID := "87463836-f0e2-4029-abf6-20c8892a3103"
|
|
||||||
|
|
||||||
createOpts := volumeattach.CreateOpts{
|
|
||||||
Device: "/dev/vdc",
|
|
||||||
VolumeID: volumeID,
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := volumeattach.Create(computeClient, serverID, createOpts).Extract()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
Example to Detach a Volume
|
|
||||||
|
|
||||||
serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d"
|
|
||||||
attachmentID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983"
|
|
||||||
|
|
||||||
err := volumeattach.Delete(computeClient, serverID, attachmentID).ExtractErr()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package volumeattach
|
|
|
@ -1,60 +0,0 @@
|
||||||
package volumeattach
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// List returns a Pager that allows you to iterate over a collection of
|
|
||||||
// VolumeAttachments.
|
|
||||||
func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
|
|
||||||
return pagination.NewPager(client, listURL(client, serverID), func(r pagination.PageResult) pagination.Page {
|
|
||||||
return VolumeAttachmentPage{pagination.SinglePageBase(r)}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOptsBuilder allows extensions to add parameters to the Create request.
|
|
||||||
type CreateOptsBuilder interface {
|
|
||||||
ToVolumeAttachmentCreateMap() (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOpts specifies volume attachment creation or import parameters.
|
|
||||||
type CreateOpts struct {
|
|
||||||
// Device is the device that the volume will attach to the instance as.
|
|
||||||
// Omit for "auto".
|
|
||||||
Device string `json:"device,omitempty"`
|
|
||||||
|
|
||||||
// VolumeID is the ID of the volume to attach to the instance.
|
|
||||||
VolumeID string `json:"volumeId" required:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToVolumeAttachmentCreateMap constructs a request body from CreateOpts.
|
|
||||||
func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) {
|
|
||||||
return gophercloud.BuildRequestBody(opts, "volumeAttachment")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create requests the creation of a new volume attachment on the server.
|
|
||||||
func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) {
|
|
||||||
b, err := opts.ToVolumeAttachmentCreateMap()
|
|
||||||
if err != nil {
|
|
||||||
r.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, r.Err = client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{
|
|
||||||
OkCodes: []int{200},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns public data about a previously created VolumeAttachment.
|
|
||||||
func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) {
|
|
||||||
_, r.Err = client.Get(getURL(client, serverID, attachmentID), &r.Body, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete requests the deletion of a previous stored VolumeAttachment from
|
|
||||||
// the server.
|
|
||||||
func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) {
|
|
||||||
_, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package volumeattach
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gophercloud/gophercloud"
|
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VolumeAttachment contains attachment information between a volume
|
|
||||||
// and server.
|
|
||||||
type VolumeAttachment struct {
|
|
||||||
// ID is a unique id of the attachment.
|
|
||||||
ID string `json:"id"`
|
|
||||||
|
|
||||||
// Device is what device the volume is attached as.
|
|
||||||
Device string `json:"device"`
|
|
||||||
|
|
||||||
// VolumeID is the ID of the attached volume.
|
|
||||||
VolumeID string `json:"volumeId"`
|
|
||||||
|
|
||||||
// ServerID is the ID of the instance that has the volume attached.
|
|
||||||
ServerID string `json:"serverId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeAttachmentPage stores a single page all of VolumeAttachment
|
|
||||||
// results from a List call.
|
|
||||||
type VolumeAttachmentPage struct {
|
|
||||||
pagination.SinglePageBase
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty determines whether or not a VolumeAttachmentPage is empty.
|
|
||||||
func (page VolumeAttachmentPage) IsEmpty() (bool, error) {
|
|
||||||
va, err := ExtractVolumeAttachments(page)
|
|
||||||
return len(va) == 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractVolumeAttachments interprets a page of results as a slice of
|
|
||||||
// VolumeAttachment.
|
|
||||||
func ExtractVolumeAttachments(r pagination.Page) ([]VolumeAttachment, error) {
|
|
||||||
var s struct {
|
|
||||||
VolumeAttachments []VolumeAttachment `json:"volumeAttachments"`
|
|
||||||
}
|
|
||||||
err := (r.(VolumeAttachmentPage)).ExtractInto(&s)
|
|
||||||
return s.VolumeAttachments, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeAttachmentResult is the result from a volume attachment operation.
|
|
||||||
type VolumeAttachmentResult struct {
|
|
||||||
gophercloud.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract is a method that attempts to interpret any VolumeAttachment resource
|
|
||||||
// response as a VolumeAttachment struct.
|
|
||||||
func (r VolumeAttachmentResult) Extract() (*VolumeAttachment, error) {
|
|
||||||
var s struct {
|
|
||||||
VolumeAttachment *VolumeAttachment `json:"volumeAttachment"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
|
||||||
return s.VolumeAttachment, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResult is the response from a Create operation. Call its Extract method
|
|
||||||
// to interpret it as a VolumeAttachment.
|
|
||||||
type CreateResult struct {
|
|
||||||
VolumeAttachmentResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the response from a Get operation. Call its Extract method to
|
|
||||||
// interpret it as a VolumeAttachment.
|
|
||||||
type GetResult struct {
|
|
||||||
VolumeAttachmentResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResult is the response from a Delete operation. Call its ExtractErr
|
|
||||||
// method to determine if the call succeeded or failed.
|
|
||||||
type DeleteResult struct {
|
|
||||||
gophercloud.ErrResult
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue