From 9ff41153b366fa5d9435cfe7373cd44db7141510 Mon Sep 17 00:00:00 2001 From: Ishaan Date: Sat, 25 Jul 2026 15:15:14 +0530 Subject: [PATCH] platform/openstack: update gophercloud v1 to v2 Signed-off-by: Ishaan --- go.mod | 4 +- go.sum | 19 +- platform/api/openstack/api.go | 288 ++-- platform/machine/openstack/machine.go | 2 +- .../gophercloud/gophercloud/README.md | 172 -- .../github.com/gophercloud/gophercloud/doc.go | 148 -- .../compute/v2/extensions/floatingips/doc.go | 68 - .../v2/extensions/floatingips/requests.go | 119 -- .../v2/extensions/floatingips/results.go | 119 -- .../compute/v2/extensions/floatingips/urls.go | 37 - .../openstack/compute/v2/images/doc.go | 32 - .../openstack/compute/v2/images/requests.go | 72 - .../openstack/compute/v2/images/results.go | 99 -- .../openstack/compute/v2/images/urls.go | 15 - .../openstack/compute/v2/servers/doc.go | 135 -- .../openstack/compute/v2/servers/requests.go | 784 --------- .../openstack/compute/v2/servers/util.go | 21 - .../gophercloud/{ => v2}/.gitignore | 1 + .../gophercloud/gophercloud/v2/.golangci.yaml | 34 + .../gophercloud/gophercloud/v2/AGENTS.md | 215 +++ .../gophercloud/{ => v2}/CHANGELOG.md | 271 +++- .../gophercloud/gophercloud/{ => v2}/LICENSE | 0 .../gophercloud/gophercloud/v2/Makefile | 131 ++ .../gophercloud/gophercloud/v2/README.md | 252 +++ .../gophercloud/{ => v2}/RELEASE.md | 0 .../gophercloud/{ => v2}/auth_options.go | 65 +- .../gophercloud/{ => v2}/auth_result.go | 6 +- .../gophercloud/{ => v2}/endpoint_search.go | 66 +- .../gophercloud/{ => v2}/errors.go | 260 +-- .../{ => v2}/openstack/auth_env.go | 8 +- .../gophercloud/{ => v2}/openstack/client.go | 239 +-- .../openstack/compute/v2/flavors/doc.go | 20 +- .../openstack/compute/v2/flavors/requests.go | 72 +- .../openstack/compute/v2/flavors/results.go | 12 +- .../openstack/compute/v2/flavors/urls.go | 2 +- .../openstack/compute/v2}/keypairs/doc.go | 16 +- .../compute/v2}/keypairs/requests.go | 28 +- .../openstack/compute/v2}/keypairs/results.go | 4 +- .../openstack/compute/v2}/keypairs/urls.go | 2 +- .../v2/openstack/compute/v2/servers/doc.go | 313 ++++ .../openstack/compute/v2/servers/errors.go | 2 +- .../openstack/compute/v2/servers/requests.go | 1405 +++++++++++++++++ .../openstack/compute/v2/servers/results.go | 321 +++- .../openstack/compute/v2/servers/urls.go | 2 +- .../v2/openstack/compute/v2/servers/util.go | 24 + .../gophercloud/{ => v2}/openstack/doc.go | 2 +- .../gophercloud/v2/openstack/endpoint.go | 190 +++ .../{ => v2}/openstack/endpoint_location.go | 104 +- .../gophercloud/{ => v2}/openstack/errors.go | 2 +- .../openstack/identity/v2/tenants/doc.go | 8 +- .../openstack/identity/v2/tenants/requests.go | 48 +- .../openstack/identity/v2/tenants/results.go | 4 +- .../openstack/identity/v2/tenants/urls.go | 2 +- .../openstack/identity/v2/tokens/doc.go | 6 +- .../openstack/identity/v2/tokens/requests.go | 23 +- .../openstack/identity/v2/tokens/results.go | 4 +- .../openstack/identity/v2/tokens/urls.go | 2 +- .../openstack/identity/v3}/ec2tokens/doc.go | 4 +- .../identity/v3}/ec2tokens/requests.go | 71 +- .../openstack/identity/v3}/ec2tokens/urls.go | 2 +- .../openstack/identity/v3}/oauth1/doc.go | 14 +- .../openstack/identity/v3}/oauth1/requests.go | 91 +- .../openstack/identity/v3}/oauth1/results.go | 4 +- .../openstack/identity/v3}/oauth1/urls.go | 2 +- .../openstack/identity/v3/tokens/doc.go | 14 +- .../openstack/identity/v3/tokens/requests.go | 35 +- .../openstack/identity/v3/tokens/results.go | 25 +- .../openstack/identity/v3/tokens/urls.go | 2 +- .../openstack/image}/v2/imagedata/doc.go | 8 +- .../openstack/image}/v2/imagedata/requests.go | 17 +- .../openstack/image}/v2/imagedata/results.go | 2 +- .../openstack/image}/v2/imagedata/urls.go | 2 +- .../openstack/image}/v2/imageimport/doc.go | 6 +- .../image}/v2/imageimport/requests.go | 20 +- .../image}/v2/imageimport/results.go | 2 +- .../openstack/image}/v2/imageimport/urls.go | 2 +- .../openstack/image}/v2/images/doc.go | 8 +- .../openstack/image}/v2/images/requests.go | 69 +- .../openstack/image}/v2/images/results.go | 30 +- .../openstack/image}/v2/images/types.go | 6 +- .../openstack/image}/v2/images/urls.go | 4 +- .../layer3/floatingips/constants.go | 7 + .../v2/extensions/layer3/floatingips/doc.go | 71 + .../extensions/layer3/floatingips/requests.go | 206 +++ .../extensions/layer3/floatingips/results.go | 184 +++ .../v2/extensions/layer3/floatingips/urls.go | 13 + .../v2/extensions/security/groups/doc.go | 8 +- .../v2/extensions/security/groups/requests.go | 78 +- .../v2/extensions/security/groups/results.go | 9 +- .../v2/extensions/security/groups/urls.go | 2 +- .../v2/extensions/security/rules/doc.go | 6 +- .../v2/extensions/security/rules/requests.go | 113 +- .../v2/extensions/security/rules/results.go | 77 +- .../v2/extensions/security/rules/urls.go | 2 +- .../networking/v2/networks/constants.go | 8 + .../openstack/networking/v2/networks/doc.go | 8 +- .../networking/v2/networks/requests.go | 62 +- .../networking/v2/networks/results.go | 12 +- .../openstack/networking/v2/networks/urls.go | 2 +- .../networking/v2/ports/constants.go | 8 + .../v2/openstack/networking/v2/ports/doc.go | 73 + .../openstack/networking/v2/ports/requests.go | 248 +++ .../openstack/networking/v2/ports/results.go | 206 +++ .../v2/openstack/networking/v2/ports/urls.go | 31 + .../{ => v2}/openstack/utils/base_endpoint.go | 19 +- .../openstack/utils/choose_version.go | 23 +- .../v2/openstack/utils/discovery.go | 372 +++++ .../gophercloud/{ => v2}/pagination/http.go | 15 +- .../gophercloud/{ => v2}/pagination/linked.go | 16 +- .../gophercloud/{ => v2}/pagination/marker.go | 8 +- .../gophercloud/{ => v2}/pagination/pager.go | 58 +- .../gophercloud/{ => v2}/pagination/pkg.go | 0 .../gophercloud/{ => v2}/pagination/single.go | 8 +- .../gophercloud/{ => v2}/params.go | 111 +- .../gophercloud/{ => v2}/provider_client.go | 154 +- .../gophercloud/{ => v2}/results.go | 55 +- .../gophercloud/{ => v2}/service_client.go | 44 +- .../gophercloud/gophercloud/{ => v2}/util.go | 54 +- .../gophercloud/utils/{ => v2}/LICENSE | 0 .../v2/extensions/security/groups/utils.go | 10 +- vendor/modules.txt | 50 +- 121 files changed, 6014 insertions(+), 3157 deletions(-) delete mode 100644 vendor/github.com/gophercloud/gophercloud/README.md delete mode 100644 vendor/github.com/gophercloud/gophercloud/doc.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/.gitignore (80%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/.golangci.yaml create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/AGENTS.md rename vendor/github.com/gophercloud/gophercloud/{ => v2}/CHANGELOG.md (76%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/LICENSE (100%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/Makefile create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/README.md rename vendor/github.com/gophercloud/gophercloud/{ => v2}/RELEASE.md (100%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/auth_options.go (90%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/auth_result.go (87%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/endpoint_search.go (51%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/errors.go (61%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/auth_env.go (94%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/client.go (63%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/flavors/doc.go (75%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/flavors/requests.go (75%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/flavors/results.go (94%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/flavors/urls.go (97%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/keypairs/doc.go (75%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/keypairs/requests.go (83%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/keypairs/results.go (96%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/keypairs/urls.go (91%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/doc.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/servers/errors.go (98%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/requests.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/servers/results.go (57%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/servers/urls.go (96%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/util.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/doc.go (86%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/endpoint_location.go (52%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/errors.go (97%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tenants/doc.go (81%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tenants/requests.go (63%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tenants/results.go (96%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tenants/urls.go (92%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tokens/doc.go (76%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tokens/requests.go (82%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tokens/results.go (97%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v2/tokens/urls.go (87%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/ec2tokens/doc.go (82%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/ec2tokens/requests.go (87%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/ec2tokens/urls.go (81%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/oauth1/doc.go (81%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/oauth1/requests.go (82%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/oauth1/results.go (98%) rename vendor/github.com/gophercloud/gophercloud/{openstack/identity/v3/extensions => v2/openstack/identity/v3}/oauth1/urls.go (96%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v3/tokens/doc.go (74%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v3/tokens/requests.go (80%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v3/tokens/results.go (89%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/identity/v3/tokens/urls.go (70%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imagedata/doc.go (72%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imagedata/requests.go (51%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imagedata/results.go (95%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imagedata/urls.go (92%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imageimport/doc.go (71%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imageimport/requests.go (66%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imageimport/results.go (95%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/imageimport/urls.go (87%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/images/doc.go (73%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/images/requests.go (83%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/images/results.go (87%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/images/types.go (93%) rename vendor/github.com/gophercloud/gophercloud/{openstack/imageservice => v2/openstack/image}/v2/images/urls.go (94%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/constants.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/urls.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/groups/doc.go (78%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/groups/requests.go (63%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/groups/results.go (93%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/groups/urls.go (83%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/rules/doc.go (79%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/rules/requests.go (59%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/rules/results.go (69%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/extensions/security/rules/urls.go (84%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/constants.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/networks/doc.go (82%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/networks/requests.go (72%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/networks/results.go (93%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/networking/v2/networks/urls.go (93%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/constants.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/urls.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/utils/base_endpoint.go (56%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/utils/choose_version.go (68%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/discovery.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/pagination/http.go (76%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/pagination/linked.go (85%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/pagination/marker.go (88%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/pagination/pager.go (82%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/pagination/pkg.go (100%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/pagination/single.go (81%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/params.go (81%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/provider_client.go (78%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/results.go (90%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/service_client.go (68%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/util.go (68%) rename vendor/github.com/gophercloud/utils/{ => v2}/LICENSE (100%) rename vendor/github.com/gophercloud/utils/{ => v2}/openstack/networking/v2/extensions/security/groups/utils.go (68%) diff --git a/go.mod b/go.mod index eb6cf9adc..009163ce7 100644 --- a/go.mod +++ b/go.mod @@ -29,8 +29,8 @@ require ( github.com/flatcar/coreos-cloudinit v1.14.1-0.20240223132627-a7bc5f0050ab github.com/flatcar/ignition v0.36.2 github.com/golang/protobuf v1.5.4 - github.com/gophercloud/gophercloud v1.14.1 - github.com/gophercloud/utils v0.0.0-20220704184730-55bdbbaec4ba + github.com/gophercloud/gophercloud/v2 v2.13.0 + github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13 github.com/hetznercloud/hcloud-go/v2 v2.44.0 github.com/kballard/go-shellquote v0.0.0-20150810074751-d8ec1a69a250 github.com/kylelemons/godebug v1.1.0 diff --git a/go.sum b/go.sum index b4109ed41..90a3357dd 100644 --- a/go.sum +++ b/go.sum @@ -203,11 +203,10 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.23.0 h1:Tchl7qkvE7Ip3y+ztvNufYFvkfqTe7NfLTYGIdJRLuE= github.com/googleapis/gax-go/v2 v2.23.0/go.mod h1:rBQKOVJCdb8IFEzg+FCwlt1LP/xMDGuqUXhUG+XMXEg= -github.com/gophercloud/gophercloud v0.20.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw= -github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= -github.com/gophercloud/utils v0.0.0-20220704184730-55bdbbaec4ba h1:PTa/ilBNI0ZE2WjSnu4drZi7W81wkYjnHPk5AEEgOGk= -github.com/gophercloud/utils v0.0.0-20220704184730-55bdbbaec4ba/go.mod h1:qOGlfG6OIJ193/c3Xt/XjOfHataNZdQcVgiu93LxBUM= +github.com/gophercloud/gophercloud/v2 v2.13.0 h1:yEyJG+kABd8x2ttTqLsomihU6Kg2YheJSZhvP/QSx+8= +github.com/gophercloud/gophercloud/v2 v2.13.0/go.mod h1:KZRLVs6gcoy/pEFdkZqFjdYqnS0emMHv66UqdM5lMjU= +github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13 h1:Dnid+JYEmkqPWw/vJHRUzZsjO3mdoyCRo2l5UYCqh8k= +github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13/go.mod h1:zpDKeT3ElgCs1UA+7B8+XlDu3R+jkK/CBC1di5qOeow= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= @@ -215,7 +214,6 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hetznercloud/hcloud-go/v2 v2.44.0 h1:1p9qwaZ/H55nLP9c7WfuwUA0LfsexS2YHOhD8+iBga8= @@ -273,7 +271,6 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -409,9 +406,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= @@ -462,7 +457,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -496,14 +490,12 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -516,7 +508,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -530,7 +521,6 @@ 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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -632,7 +622,6 @@ gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss= gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/platform/api/openstack/api.go b/platform/api/openstack/api.go index 629a54098..fca8a386f 100644 --- a/platform/api/openstack/api.go +++ b/platform/api/openstack/api.go @@ -15,6 +15,7 @@ package openstack import ( + "context" "fmt" "net/url" "os" @@ -22,21 +23,21 @@ import ( "time" "github.com/coreos/pkg/capnslog" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" - "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" - computeImages "github.com/gophercloud/gophercloud/openstack/compute/v2/images" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/pagination" - ugroups "github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/pagination" + ugroups "github.com/gophercloud/utils/v2/openstack/networking/v2/extensions/security/groups" "github.com/flatcar/mantle/auth" "github.com/flatcar/mantle/platform" @@ -81,13 +82,20 @@ type Server struct { } type API struct { - opts *Options + opts *Options + computeClient *gophercloud.ServiceClient imageClient *gophercloud.ServiceClient networkClient *gophercloud.ServiceClient + + // floatingNetworkID is the UUID of the external network for floating IPs. + // Resolved from Options.FloatingIPPool (Name or ID) during API init. + floatingNetworkID string } func New(opts *Options) (*API, error) { + ctx := context.Background() + profiles, err := auth.ReadOpenStackConfig(opts.ConfigPath) if err != nil { return nil, fmt.Errorf("couldn't read OpenStack config: %v", err) @@ -119,11 +127,11 @@ func New(opts *Options) (*API, error) { // token whenever it receives a 401 response, preventing // intermittent "Authentication failed" errors during console // log retrieval, security‑group operations, etc. - // See https://pkg.go.dev/github.com/gophercloud/gophercloud#AuthOptions + // See https://pkg.go.dev/github.com/gophercloud/gophercloud/v2#AuthOptions AllowReauth: true, } - provider, err := openstack.AuthenticatedClient(osOpts) + provider, err := openstack.AuthenticatedClient(ctx, osOpts) if err != nil { return nil, fmt.Errorf("failed creating provider: %v", err) } @@ -140,7 +148,7 @@ func New(opts *Options) (*API, error) { return nil, fmt.Errorf("failed to create compute client: %v", err) } - imageClient, err := openstack.NewImageServiceV2(provider, gophercloud.EndpointOpts{ + imageClient, err := openstack.NewImageV2(provider, gophercloud.EndpointOpts{ Name: "glance", Region: opts.Region, }) @@ -152,6 +160,9 @@ func New(opts *Options) (*API, error) { Name: "neutron", Region: opts.Region, }) + if err != nil { + return nil, fmt.Errorf("failed to create network client: %v", err) + } a := &API{ opts: opts, @@ -161,7 +172,7 @@ func New(opts *Options) (*API, error) { } if a.opts.Flavor != "" { - tmp, err := a.resolveFlavor() + tmp, err := a.resolveFlavor(ctx) if err != nil { return nil, fmt.Errorf("resolving flavor: %v", err) } @@ -177,7 +188,7 @@ func New(opts *Options) (*API, error) { } if a.opts.Network != "" { - tmp, err := a.resolveNetwork() + tmp, err := a.resolveNetwork(ctx) if err != nil { return nil, fmt.Errorf("resolving network: %v", err) } @@ -188,15 +199,23 @@ func New(opts *Options) (*API, error) { a.opts.FloatingIPPool = profile.FloatingIPPool } + if a.opts.FloatingIPPool != "" { + netID, err := a.resolveFloatingNetwork(ctx) + if err != nil { + return nil, fmt.Errorf("resolving floating IP pool: %v", err) + } + a.floatingNetworkID = netID + } + return a, nil } -func unwrapPages(pager pagination.Pager, allowEmpty bool) (pagination.Page, error) { +func unwrapPages(ctx context.Context, pager pagination.Pager, allowEmpty bool) (pagination.Page, error) { if pager.Err != nil { return nil, fmt.Errorf("retrieving pager: %v", pager.Err) } - pages, err := pager.AllPages() + pages, err := pager.AllPages(ctx) if err != nil { return nil, fmt.Errorf("retrieving pages: %v", err) } @@ -213,10 +232,10 @@ func unwrapPages(pager pagination.Pager, allowEmpty bool) (pagination.Page, erro return pages, nil } -func (a *API) resolveFlavor() (string, error) { +func (a *API) resolveFlavor(ctx context.Context) (string, error) { pager := flavors.ListDetail(a.computeClient, flavors.ListOpts{}) - pages, err := unwrapPages(pager, false) + pages, err := unwrapPages(ctx, pager, false) if err != nil { return "", fmt.Errorf("flavors: %v", err) } @@ -236,19 +255,22 @@ func (a *API) resolveFlavor() (string, error) { } func (a *API) ResolveImage(img string) (string, error) { - pager := computeImages.ListDetail(a.computeClient, computeImages.ListOpts{}) + ctx := context.Background() + // Use the Glance image service instead of Nova (removed in gophercloud v2). + // Go through available images and match by ID or name. + pager := images.List(a.imageClient, images.ListOpts{}) - pages, err := unwrapPages(pager, false) + pages, err := unwrapPages(ctx, pager, false) if err != nil { return "", fmt.Errorf("images: %v", err) } - images, err := computeImages.ExtractImages(pages) + allImages, err := images.ExtractImages(pages) if err != nil { return "", fmt.Errorf("extracting images: %v", err) } - for _, image := range images { + for _, image := range allImages { if image.ID == img || image.Name == img { return image.ID, nil } @@ -257,13 +279,13 @@ func (a *API) ResolveImage(img string) (string, error) { return "", fmt.Errorf("specified image %q not found", img) } -func (a *API) resolveNetwork() (string, error) { - networks, err := a.getNetworks() +func (a *API) resolveNetwork(ctx context.Context) (string, error) { + nets, err := a.getNetworks(ctx) if err != nil { return "", err } - for _, network := range networks { + for _, network := range nets { if network.ID == a.opts.Network || network.Name == a.opts.Network { return network.ID, nil } @@ -272,6 +294,32 @@ func (a *API) resolveNetwork() (string, error) { return "", fmt.Errorf("specified network %q not found", a.opts.Network) } +// resolveFloatingNetwork resolves FloatingIPPool to a network UUID. +// Pool may be name or UUID, so all networks listed and matched. +// If nothing matches, pool is treated as-is (raw UUID). +func (a *API) resolveFloatingNetwork(ctx context.Context) (string, error) { + pager := networks.List(a.networkClient, networks.ListOpts{}) + + pages, err := unwrapPages(ctx, pager, true) + if err != nil { + return "", fmt.Errorf("listing networks for floating IP pool: %v", err) + } + + allNetworks, err := networks.ExtractNetworks(pages) + if err != nil { + return "", fmt.Errorf("extracting networks: %v", err) + } + + for _, network := range allNetworks { + if network.ID == a.opts.FloatingIPPool || network.Name == a.opts.FloatingIPPool { + return network.ID, nil + } + } + + // If no match, assume FloatingIPPool as an external network UUID already. + return a.opts.FloatingIPPool, nil +} + func (a *API) PreflightCheck() error { if err := servers.List(a.computeClient, servers.ListOpts{}).Err; err != nil { return fmt.Errorf("listing servers: %v", err) @@ -280,21 +328,23 @@ func (a *API) PreflightCheck() error { } func (a *API) CreateServer(name, sshKeyID, userdata string) (*Server, error) { + ctx := context.Background() + networkID := a.opts.Network if networkID == "" { - networks, err := a.getNetworks() + networks, err := a.getNetworks(ctx) if err != nil { return nil, fmt.Errorf("getting network: %v", err) } networkID = networks[0].ID } - securityGroup, err := a.getSecurityGroup() + securityGroup, err := a.getSecurityGroup(ctx) if err != nil { return nil, fmt.Errorf("retrieving security group: %v", err) } - server, err := servers.Create(a.computeClient, keypairs.CreateOptsExt{ + server, err := servers.Create(ctx, a.computeClient, keypairs.CreateOptsExt{ CreateOptsBuilder: servers.CreateOpts{ Name: name, FlavorRef: a.opts.Flavor, @@ -311,7 +361,7 @@ func (a *API) CreateServer(name, sshKeyID, userdata string) (*Server, error) { UserData: []byte(userdata), }, KeyName: sshKeyID, - }).Extract() + }, nil).Extract() if err != nil { return nil, fmt.Errorf("creating server: %v", err) } @@ -320,7 +370,7 @@ func (a *API) CreateServer(name, sshKeyID, userdata string) (*Server, error) { err = util.WaitUntilReady(5*time.Minute, 10*time.Second, func() (bool, error) { var err error - server, err = servers.Get(a.computeClient, serverID).Extract() + server, err = servers.Get(ctx, a.computeClient, serverID).Extract() if err != nil { return false, err } @@ -333,23 +383,20 @@ func (a *API) CreateServer(name, sshKeyID, userdata string) (*Server, error) { var floatingip *floatingips.FloatingIP if a.opts.FloatingIPPool != "" { - floatingip, err = a.createFloatingIP() + floatingip, err = a.createFloatingIP(ctx) if err != nil { a.DeleteServer(serverID) return nil, fmt.Errorf("creating floating ip: %v", err) } - err = floatingips.AssociateInstance(a.computeClient, serverID, floatingips.AssociateOpts{ - FloatingIP: floatingip.IP, - }).ExtractErr() - if err != nil { + if err := a.associateFloatingIP(ctx, serverID, floatingip.ID); err != nil { a.DeleteServer(serverID) // Explicitly delete the floating ip as DeleteServer only deletes floating IPs that are // associated with servers - a.deleteFloatingIP(floatingip.ID) + a.deleteFloatingIP(ctx, floatingip.ID) return nil, fmt.Errorf("associating floating ip: %v", err) } - server, err = servers.Get(a.computeClient, serverID).Extract() + server, err = servers.Get(ctx, a.computeClient, serverID).Extract() if err != nil { a.DeleteServer(serverID) return nil, fmt.Errorf("retrieving server info: %v", err) @@ -362,10 +409,10 @@ func (a *API) CreateServer(name, sshKeyID, userdata string) (*Server, error) { }, nil } -func (a *API) getNetworks() ([]networks.Network, error) { +func (a *API) getNetworks(ctx context.Context) ([]networks.Network, error) { pager := networks.List(a.networkClient, networks.ListOpts{}) - pages, err := unwrapPages(pager, false) + pages, err := unwrapPages(ctx, pager, false) if err != nil { return nil, fmt.Errorf("networks: %v", err) } @@ -377,19 +424,38 @@ func (a *API) getNetworks() ([]networks.Network, error) { return networks, nil } -func (a *API) getSecurityGroup() (string, error) { - id, err := ugroups.IDFromName(a.networkClient, "kola") +// getServerPorts returns the Neutron ports belonging to the given server. +// A VM's ports have their DeviceID set to the server UUID. +func (a *API) getServerPorts(ctx context.Context, serverID string) ([]ports.Port, error) { + pager := ports.List(a.networkClient, ports.ListOpts{ + DeviceID: serverID, + }) + + pages, err := unwrapPages(ctx, pager, true) + if err != nil { + return nil, fmt.Errorf("listing ports for server %s: %v", serverID, err) + } + + allPorts, err := ports.ExtractPorts(pages) + if err != nil { + return nil, fmt.Errorf("extracting ports: %v", err) + } + return allPorts, nil +} + +func (a *API) getSecurityGroup(ctx context.Context) (string, error) { + id, err := ugroups.IDFromName(ctx, a.networkClient, "kola") if err != nil { if _, ok := err.(gophercloud.ErrResourceNotFound); ok { - return a.createSecurityGroup() + return a.createSecurityGroup(ctx) } return "", fmt.Errorf("finding security group: %v", err) } return id, nil } -func (a *API) createSecurityGroup() (string, error) { - securityGroup, err := groups.Create(a.networkClient, groups.CreateOpts{ +func (a *API) createSecurityGroup(ctx context.Context) (string, error) { + securityGroup, err := groups.Create(ctx, a.networkClient, groups.CreateOpts{ Name: "kola", }).Extract() if err != nil { @@ -434,7 +500,7 @@ func (a *API) createSecurityGroup() (string, error) { } for _, rule := range ruleSet { - _, err = rules.Create(a.networkClient, rules.CreateOpts{ + _, err = rules.Create(ctx, a.networkClient, rules.CreateOpts{ Direction: rule.Direction, EtherType: rule.EtherType, SecGroupID: securityGroup.ID, @@ -445,7 +511,7 @@ func (a *API) createSecurityGroup() (string, error) { RemoteIPPrefix: rule.RemoteIPPrefix, }).Extract() if err != nil { - a.deleteSecurityGroup(securityGroup.ID) + a.deleteSecurityGroup(ctx, securityGroup.ID) return "", fmt.Errorf("adding security rule: %v", err) } } @@ -453,42 +519,80 @@ func (a *API) createSecurityGroup() (string, error) { return securityGroup.ID, nil } -func (a *API) deleteSecurityGroup(id string) error { - return groups.Delete(a.networkClient, id).ExtractErr() +func (a *API) deleteSecurityGroup(ctx context.Context, id string) error { + return groups.Delete(ctx, a.networkClient, id).ExtractErr() } -func (a *API) createFloatingIP() (*floatingips.FloatingIP, error) { - return floatingips.Create(a.computeClient, floatingips.CreateOpts{ - Pool: a.opts.FloatingIPPool, +func (a *API) createFloatingIP(ctx context.Context) (*floatingips.FloatingIP, error) { + return floatingips.Create(ctx, a.networkClient, floatingips.CreateOpts{ + FloatingNetworkID: a.floatingNetworkID, }).Extract() } -func (a *API) disassociateFloatingIP(serverID, id string) error { - return floatingips.DisassociateInstance(a.computeClient, serverID, floatingips.DisassociateOpts{ - FloatingIP: id, - }).ExtractErr() +// associateFloatingIP associates the given floating IP with the first port +// belonging to the server identified by serverID. +func (a *API) associateFloatingIP(ctx context.Context, serverID, floatingIPID string) error { + serverPorts, err := a.getServerPorts(ctx, serverID) + if err != nil { + return fmt.Errorf("getting server ports: %v", err) + } + if len(serverPorts) == 0 { + return fmt.Errorf("no port found for server %s", serverID) + } + + portID := serverPorts[0].ID + _, err = floatingips.Update(ctx, a.networkClient, floatingIPID, floatingips.UpdateOpts{ + PortID: &portID, + }).Extract() + return err } -func (a *API) deleteFloatingIP(id string) error { - return floatingips.Delete(a.computeClient, id).ExtractErr() +// disassociateFloatingIP detaches the floating IP from any port it is +// associated with. +func (a *API) disassociateFloatingIP(ctx context.Context, floatingIPID string) error { + emptyPortID := "" + _, err := floatingips.Update(ctx, a.networkClient, floatingIPID, floatingips.UpdateOpts{ + PortID: &emptyPortID, + }).Extract() + return err } -func (a *API) findFloatingIP(serverID string) (*floatingips.FloatingIP, error) { - pager := floatingips.List(a.computeClient) +func (a *API) deleteFloatingIP(ctx context.Context, id string) error { + return floatingips.Delete(ctx, a.networkClient, id).ExtractErr() +} - pages, err := unwrapPages(pager, true) +// findFloatingIP returns the floating IP associated with the given server (if exists). +// A floating IP is considered associated with a server when its PortID matches +// one of the server's Neutron ports. +func (a *API) findFloatingIP(ctx context.Context, serverID string) (*floatingips.FloatingIP, error) { + serverPorts, err := a.getServerPorts(ctx, serverID) + if err != nil { + return nil, fmt.Errorf("getting server ports: %v", err) + } + portIDs := make(map[string]struct{}, len(serverPorts)) + for _, p := range serverPorts { + portIDs[p.ID] = struct{}{} + } + + pager := floatingips.List(a.networkClient, floatingips.ListOpts{}) + + pages, err := unwrapPages(ctx, pager, true) if err != nil { return nil, fmt.Errorf("floating ips: %v", err) } - floatingiplist, err := floatingips.ExtractFloatingIPs(pages) + allFIPs, err := floatingips.ExtractFloatingIPs(pages) if err != nil { return nil, fmt.Errorf("extracting floating ips: %v", err) } - for _, floatingip := range floatingiplist { - if floatingip.InstanceID == serverID { - return &floatingip, nil + for i := range allFIPs { + fip := allFIPs[i] + if fip.PortID == "" { + continue + } + if _, ok := portIDs[fip.PortID]; ok { + return &fip, nil } } @@ -497,22 +601,24 @@ func (a *API) findFloatingIP(serverID string) (*floatingips.FloatingIP, error) { // Deletes the server, and disassociates & deletes any floating IP associated with the given server. func (a *API) DeleteServer(id string) error { - fip, err := a.findFloatingIP(id) + ctx := context.Background() + + fip, err := a.findFloatingIP(ctx, id) if err != nil { return err } if fip != nil { - if err := a.disassociateFloatingIP(id, fip.IP); err != nil { + if err := a.disassociateFloatingIP(ctx, fip.ID); err != nil { return fmt.Errorf("couldn't disassociate floating ip %s from server %s: %v", fip.ID, id, err) } - if err := a.deleteFloatingIP(fip.ID); err != nil { + if err := a.deleteFloatingIP(ctx, fip.ID); err != nil { // if the deletion of this floating IP fails then mantle cannot detect the floating IP was tied to the // server anymore. as such warn and continue deleting the server. plog.Warningf("couldn't delete floating ip %s: %v", fip.ID, err) } } - if err := servers.Delete(a.computeClient, id).ExtractErr(); err != nil { + if err := servers.Delete(ctx, a.computeClient, id).ExtractErr(); err != nil { return fmt.Errorf("deleting server: %v: %v", id, err) } @@ -520,16 +626,17 @@ func (a *API) DeleteServer(id string) error { } func (a *API) GetConsoleOutput(id string) (string, error) { - return servers.ShowConsoleOutput(a.computeClient, id, servers.ShowConsoleOutputOpts{}).Extract() + return servers.ShowConsoleOutput(context.Background(), a.computeClient, id, servers.ShowConsoleOutputOpts{}).Extract() } func (a *API) webUpload(ID, URI string) error { + ctx := context.Background() createOpts := imageimport.CreateOpts{ Name: imageimport.WebDownloadMethod, URI: URI, } - if err := imageimport.Create(a.imageClient, ID, createOpts).ExtractErr(); err != nil { + if err := imageimport.Create(ctx, a.imageClient, ID, createOpts).ExtractErr(); err != nil { return fmt.Errorf("importing web image: %w", err) } @@ -537,7 +644,9 @@ func (a *API) webUpload(ID, URI string) error { } func (a *API) UploadImage(name, path string) (string, error) { - image, err := images.Create(a.imageClient, images.CreateOpts{ + ctx := context.Background() + + image, err := images.Create(ctx, a.imageClient, images.CreateOpts{ Name: name, ContainerFormat: "bare", DiskFormat: "qcow2", @@ -557,7 +666,7 @@ func (a *API) UploadImage(name, path string) (string, error) { // It usually takes around 10 seconds to extract the image. if err := util.WaitUntilReady(1*time.Minute, 5*time.Second, func() (bool, error) { - image, err = images.Get(a.imageClient, image.ID).Extract() + image, err = images.Get(ctx, a.imageClient, image.ID).Extract() if err != nil { return false, fmt.Errorf("getting image status: %w", err) } @@ -579,7 +688,7 @@ func (a *API) UploadImage(name, path string) (string, error) { } defer data.Close() - err = imagedata.Upload(a.imageClient, image.ID, data).ExtractErr() + err = imagedata.Upload(ctx, a.imageClient, image.ID, data).ExtractErr() if err != nil { a.DeleteImage(image.ID) return "", fmt.Errorf("uploading image data: %v", err) @@ -589,16 +698,17 @@ func (a *API) UploadImage(name, path string) (string, error) { } func (a *API) DeleteImage(imageID string) error { - return images.Delete(a.imageClient, imageID).ExtractErr() + return images.Delete(context.Background(), a.imageClient, imageID).ExtractErr() } func (a *API) PruneKeys(olderThan time.Duration) error { + ctx := context.Background() // Build a set of keypair names that are still in use by active servers so // that we don't delete keys that are currently required. usedKeys := make(map[string]struct{}) srvPager := servers.List(a.computeClient, servers.ListOpts{}) - srvPages, err := unwrapPages(srvPager, true) + srvPages, err := unwrapPages(ctx, srvPager, true) if err != nil { return fmt.Errorf("listing servers: %v", err) } @@ -616,7 +726,7 @@ func (a *API) PruneKeys(olderThan time.Duration) error { // List all keypairs in the project. kpPager := keypairs.List(a.computeClient, keypairs.ListOpts{}) - kpPages, err := unwrapPages(kpPager, true) + kpPages, err := unwrapPages(ctx, kpPager, true) if err != nil { return fmt.Errorf("listing keypairs: %v", err) } @@ -642,7 +752,7 @@ func (a *API) PruneKeys(olderThan time.Duration) error { } `json:"keypair"` } - if err := keypairs.Get(a.computeClient, kp.Name, nil).ExtractInto(&detail); err != nil { + if err := keypairs.Get(ctx, a.computeClient, kp.Name, nil).ExtractInto(&detail); err != nil { // If we fail to obtain details, skip deletion to be safe. plog.Warningf("could not get details for keypair %s: %v", kp.Name, err) continue @@ -693,7 +803,7 @@ func (a *API) PruneKeys(olderThan time.Duration) error { } func (a *API) AddKey(name, key string) error { - _, err := keypairs.Create(a.computeClient, keypairs.CreateOpts{ + _, err := keypairs.Create(context.Background(), a.computeClient, keypairs.CreateOpts{ Name: name, PublicKey: key, }).Extract() @@ -701,13 +811,14 @@ func (a *API) AddKey(name, key string) error { } func (a *API) DeleteKey(name string) error { - return keypairs.Delete(a.computeClient, name, nil).ExtractErr() + return keypairs.Delete(context.Background(), a.computeClient, name, nil).ExtractErr() } func (a *API) listServersWithMetadata(metadata map[string]string) ([]servers.Server, error) { + ctx := context.Background() pager := servers.List(a.computeClient, servers.ListOpts{}) - pages, err := unwrapPages(pager, true) + pages, err := unwrapPages(ctx, pager, true) if err != nil { return nil, fmt.Errorf("servers: %v", err) } @@ -733,11 +844,12 @@ func (a *API) listServersWithMetadata(metadata map[string]string) ([]servers.Ser } func (a *API) listImagesWithTags(tags []string) ([]images.Image, error) { + ctx := context.Background() listOpts := images.ListOpts{ Tags: tags, } - allPages, err := images.List(a.imageClient, listOpts).AllPages() + allPages, err := images.List(a.imageClient, listOpts).AllPages(ctx) if err != nil { return nil, fmt.Errorf("listing images: %w", err) } diff --git a/platform/machine/openstack/machine.go b/platform/machine/openstack/machine.go index 65a9bc6ef..e984f7852 100644 --- a/platform/machine/openstack/machine.go +++ b/platform/machine/openstack/machine.go @@ -39,7 +39,7 @@ func (om *machine) ID() string { func (om *machine) IP() string { if om.mach.FloatingIP != nil { - return om.mach.FloatingIP.IP + return om.mach.FloatingIP.FloatingIP } // we try to get the IPv4 address. diff --git a/vendor/github.com/gophercloud/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md deleted file mode 100644 index 4e6e57dad..000000000 --- a/vendor/github.com/gophercloud/gophercloud/README.md +++ /dev/null @@ -1,172 +0,0 @@ -# Gophercloud: an OpenStack SDK for Go -[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=v1)](https://coveralls.io/github/gophercloud/gophercloud?branch=v1) - -Gophercloud is an OpenStack Go SDK. - -## Useful links - -* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) -* [Effective Go](https://golang.org/doc/effective_go.html) - -## How to install - -Reference a Gophercloud package in your code: - -```go -import "github.com/gophercloud/gophercloud" -``` - -Then update your `go.mod`: - -```shell -go mod tidy -``` - -## Getting started - -### Credentials - -Because you'll be hitting an API, you will need to retrieve your OpenStack -credentials and either store them in a `clouds.yaml` file, as environment -variables, or in your local Go files. The first method is recommended because -it decouples credential information from source code, allowing you to push the -latter to your version control system without any security risk. - -You will need to retrieve the following: - -* A valid Keystone identity URL -* Credentials. These can be a username/password combo, a set of Application - Credentials, a pre-generated token, or any other supported authentication - mechanism. - -For users who have the OpenStack dashboard installed, there's a shortcut. If -you visit the `project/api_access` path in Horizon and click on the -"Download OpenStack RC File" button at the top right hand corner, you can -download either a `clouds.yaml` file or an `openrc` bash file that exports all -of your access details to environment variables. To use the `clouds.yaml` file, -place it at `~/.config/openstack/clouds.yaml`. To use the `openrc` file, run -`source openrc` and you will be prompted for your password. - -### Authentication - -Once you have access to your credentials, you can begin plugging them into -Gophercloud. The next step is authentication, which is handled by a base -"Provider" struct. There are number of ways to construct such a struct. - -**With `gophercloud/utils`** - -The [github.com/gophercloud/utils](https://github.com/gophercloud/utils) -library provides the `clientconfig` package to simplify authentication. It -provides additional functionality, such as the ability to read `clouds.yaml` -files. To generate a "Provider" struct using the `clientconfig` package: - -```go -import ( - "github.com/gophercloud/utils/openstack/clientconfig" -) - -// You can also skip configuring this and instead set 'OS_CLOUD' in your -// environment -opts := new(clientconfig.ClientOpts) -opts.Cloud = "devstack-admin" - -provider, err := clientconfig.AuthenticatedClient(opts) -``` - -A provider client is a top-level client that all of your OpenStack service -clients derive from. The provider contains all of the authentication details -that allow your Go code to access the API - such as the base URL and token ID. - -Once we have a base Provider, we inject it as a dependency into each OpenStack -service. For example, in order to work with the Compute API, we need a Compute -service client. This can be created like so: - -```go -client, err := clientconfig.NewServiceClient("compute", opts) -``` - -**Without `gophercloud/utils`** - -> *Note* -> gophercloud doesn't provide support for `clouds.yaml` file so you need to -> implement this functionality yourself if you don't wish to use -> `gophercloud/utils`. - -You can also generate a "Provider" struct without using the `clientconfig` -package from `gophercloud/utils`. To do this, you can either pass in your -credentials explicitly or tell Gophercloud to use environment variables: - -```go -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" -) - -// Option 1: Pass in the values yourself -opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", -} - -// Option 2: Use a utility function to retrieve all your environment variables -opts, err := openstack.AuthOptionsFromEnv() -``` - -Once you have the `opts` variable, you can pass it in and get back a -`ProviderClient` struct: - -```go -provider, err := openstack.AuthenticatedClient(opts) -``` - -As above, you can then use this provider client to generate a service client -for a particular OpenStack service: - -```go -client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), -}) -``` - -### Provision a server - -We can use the Compute service client generated above for any Compute API -operation we want. In our case, we want to provision a new server. To do this, -we invoke the `Create` method and pass in the flavor ID (hardware -specification) and image ID (operating system) we're interested in: - -```go -import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - -server, err := servers.Create(client, servers.CreateOpts{ - Name: "My new server!", - FlavorRef: "flavor_id", - ImageRef: "image_id", -}).Extract() -``` - -The above code sample creates a new server with the parameters, and embodies the -new resource in the `server` variable (a -[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct). - -## Advanced Usage - -Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. - -## Backwards-Compatibility Guarantees - -Gophercloud versioning follows [semver](https://semver.org/spec/v2.0.0.html). - -Before `v1.0.0`, there were no guarantees. Starting with v1, there will be no breaking changes within a major release. - -See the [Release instructions](./RELEASE.md). - -## Contributing - -See the [contributing guide](./.github/CONTRIBUTING.md). - -## Help and feedback - -If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). diff --git a/vendor/github.com/gophercloud/gophercloud/doc.go b/vendor/github.com/gophercloud/gophercloud/doc.go deleted file mode 100644 index 19b64d650..000000000 --- a/vendor/github.com/gophercloud/gophercloud/doc.go +++ /dev/null @@ -1,148 +0,0 @@ -/* -Package gophercloud provides a multi-vendor interface to OpenStack-compatible -clouds. The library has a three-level hierarchy: providers, services, and -resources. - -# Authenticating with Providers - -Provider structs represent the cloud providers that offer and manage a -collection of services. You will generally want to create one Provider -client per OpenStack cloud. - - It is now recommended to use the `clientconfig` package found at - https://github.com/gophercloud/utils/tree/master/openstack/clientconfig - for all authentication purposes. - - The below documentation is still relevant. clientconfig simply implements - the below and presents it in an easier and more flexible way. - -Use your OpenStack credentials to create a Provider client. The -IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in -information provided by the cloud operator. Additionally, the cloud may refer to -TenantID or TenantName as project_id and project_name. Credentials are -specified like so: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(opts) - -You can authenticate with a token by doing: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - TokenID: "{token_id}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(opts) - -You may also use the openstack.AuthOptionsFromEnv() helper function. This -function reads in standard environment variables frequently found in an -OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" -instead of "project". - - opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) - -# Service Clients - -Service structs are specific to a provider and handle all of the logic and -operations for a particular OpenStack service. Examples of services include: -Compute, Object Storage, Block Storage. In order to define one, you need to -pass in the parent provider, like so: - - opts := gophercloud.EndpointOpts{Region: "RegionOne"} - - client, err := openstack.NewComputeV2(provider, opts) - -# Resources - -Resource structs are the domain models that services make use of in order -to work with and represent the state of API resources: - - server, err := servers.Get(client, "{serverId}").Extract() - -Intermediate Result structs are returned for API operations, which allow -generic access to the HTTP headers, response body, and any errors associated -with the network transaction. To turn a result into a usable resource struct, -you must call the Extract method which is chained to the response, or an -Extract function from an applicable extension: - - result := servers.Get(client, "{serverId}") - - // Attempt to extract the disk configuration from the OS-DCF disk config - // extension: - config, err := diskconfig.ExtractGet(result) - -All requests that enumerate a collection return a Pager struct that is used to -iterate through the results one page at a time. Use the EachPage method on that -Pager to handle each successive Page in a closure, then use the appropriate -extraction method from that request's package to interpret that Page as a slice -of results: - - err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - - // Handle the []servers.Server slice. - - // Return "false" or an error to prematurely stop fetching new pages. - return true, nil - }) - -If you want to obtain the entire collection of pages without doing any -intermediary processing on each page, you can use the AllPages method: - - allPages, err := servers.List(client, nil).AllPages() - allServers, err := servers.ExtractServers(allPages) - -This top-level package contains utility functions and data types that are used -throughout the provider and service packages. Of particular note for end users -are the AuthOptions and EndpointOpts structs. - -An example retry backoff function, which respects the 429 HTTP response code and a "Retry-After" header: - - endpoint := "http://localhost:5000" - provider, err := openstack.NewClient(endpoint) - if err != nil { - panic(err) - } - provider.MaxBackoffRetries = 3 // max three retries - provider.RetryBackoffFunc = func(ctx context.Context, respErr *ErrUnexpectedResponseCode, e error, retries uint) error { - retryAfter := respErr.ResponseHeader.Get("Retry-After") - if retryAfter == "" { - return e - } - - var sleep time.Duration - - // Parse delay seconds or HTTP date - if v, err := strconv.ParseUint(retryAfter, 10, 32); err == nil { - sleep = time.Duration(v) * time.Second - } else if v, err := time.Parse(http.TimeFormat, retryAfter); err == nil { - sleep = time.Until(v) - } else { - return e - } - - if ctx != nil { - select { - case <-time.After(sleep): - case <-ctx.Done(): - return e - } - } else { - time.Sleep(sleep) - } - - return nil - } -*/ -package gophercloud diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go deleted file mode 100644 index f5dbdbf8b..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go +++ /dev/null @@ -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 diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go deleted file mode 100644 index 0d8104d24..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go +++ /dev/null @@ -1,119 +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 - } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get returns data about a previously created Floating IP. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete requests the deletion of a previous allocated Floating IP. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// 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 - } - resp, err := client.Post(associateURL(client, serverID), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - 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 - } - resp, err := client.Post(disassociateURL(client, serverID), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go deleted file mode 100644 index 4009b8336..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go +++ /dev/null @@ -1,119 +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) { - if page.StatusCode == 204 { - return true, nil - } - - 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 -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go deleted file mode 100644 index 4768e5a89..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go deleted file mode 100644 index 22410a79a..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Package images provides information and interaction with the images through -the OpenStack Compute service. - -This API is deprecated and will be removed from a future version of the Nova -API service. - -An image is a collection of files used to create or rebuild a server. -Operators provide a number of pre-built OS images by default. You may also -create custom images from cloud servers you have launched. - -Example to List Images - - listOpts := images.ListOpts{ - Limit: 2, - } - - allPages, err := images.ListDetail(computeClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allImages, err := images.ExtractImages(allPages) - if err != nil { - panic(err) - } - - for _, image := range allImages { - fmt.Printf("%+v\n", image) - } -*/ -package images diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go deleted file mode 100644 index c51de8dc3..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go +++ /dev/null @@ -1,72 +0,0 @@ -package images - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// ListDetail request. -type ListOptsBuilder interface { - ToImageListQuery() (string, error) -} - -// ListOpts contain options filtering Images returned from a call to ListDetail. -type ListOpts struct { - // ChangesSince filters Images based on the last changed status (in date-time - // format). - ChangesSince string `q:"changes-since"` - - // Limit limits the number of Images to return. - Limit int `q:"limit"` - - // Mark is an Image UUID at which to set a marker. - Marker string `q:"marker"` - - // Name is the name of the Image. - Name string `q:"name"` - - // Server is the name of the Server (in URL format). - Server string `q:"server"` - - // Status is the current status of the Image. - Status string `q:"status"` - - // Type is the type of image (e.g. BASE, SERVER, ALL). - Type string `q:"type"` -} - -// ToImageListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToImageListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListDetail enumerates the available images. -func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listDetailURL(client) - if opts != nil { - query, err := opts.ToImageListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ImagePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get returns data about a specific image by its ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete deletes the specified image ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go deleted file mode 100644 index ca30befeb..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go +++ /dev/null @@ -1,99 +0,0 @@ -package images - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as an Image. -type GetResult struct { - gophercloud.Result -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// Extract interprets a GetResult as an Image. -func (r GetResult) Extract() (*Image, error) { - var s struct { - Image *Image `json:"image"` - } - err := r.ExtractInto(&s) - return s.Image, err -} - -// Image represents an Image returned by the Compute API. -type Image struct { - // ID is the unique ID of an image. - ID string - - // Created is the date when the image was created. - Created string - - // MinDisk is the minimum amount of disk a flavor must have to be able - // to create a server based on the image, measured in GB. - MinDisk int - - // MinRAM is the minimum amount of RAM a flavor must have to be able - // to create a server based on the image, measured in MB. - MinRAM int - - // Name provides a human-readable moniker for the OS image. - Name string - - // The Progress and Status fields indicate image-creation status. - Progress int - - // Status is the current status of the image. - Status string - - // Update is the date when the image was updated. - Updated string - - // Metadata provides free-form key/value pairs that further describe the - // image. - Metadata map[string]interface{} -} - -// ImagePage contains a single page of all Images returne from a ListDetail -// operation. Use ExtractImages to convert it into a slice of usable structs. -type ImagePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if an ImagePage contains no Image results. -func (page ImagePage) IsEmpty() (bool, error) { - if page.StatusCode == 204 { - return true, nil - } - - images, err := ExtractImages(page) - return len(images) == 0, err -} - -// NextPageURL uses the response's embedded link reference to navigate to the -// next page of results. -func (page ImagePage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"images_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// ExtractImages converts a page of List results into a slice of usable Image -// structs. -func ExtractImages(r pagination.Page) ([]Image, error) { - var s struct { - Images []Image `json:"images"` - } - err := (r.(ImagePage)).ExtractInto(&s) - return s.Images, err -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go deleted file mode 100644 index 57787fb72..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go +++ /dev/null @@ -1,15 +0,0 @@ -package images - -import "github.com/gophercloud/gophercloud" - -func listDetailURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("images", "detail") -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("images", id) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("images", id) -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go deleted file mode 100644 index bab72c152..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Package servers provides information and interaction with the server API -resource in the OpenStack Compute service. - -A server is a virtual machine instance in the compute system. In order for -one to be provisioned, a valid flavor and image are required. - -Example to List Servers - - listOpts := servers.ListOpts{ - AllTenants: true, - } - - allPages, err := servers.ListSimple(computeClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allServers, err := servers.ExtractServers(allPages) - if err != nil { - panic(err) - } - - for _, server := range allServers { - fmt.Printf("%+v\n", server) - } - -Example to List Detail Servers - - listOpts := servers.ListOpts{ - AllTenants: true, - } - - allPages, err := servers.List(computeClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allServers, err := servers.ExtractServers(allPages) - if err != nil { - panic(err) - } - - for _, server := range allServers { - fmt.Printf("%+v\n", server) - } - -Example to Create a Server - - createOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - server, err := servers.Create(computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Server - - serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - err := servers.Delete(computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Force Delete a Server - - serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - err := servers.ForceDelete(computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Reboot a Server - - rebootOpts := servers.RebootOpts{ - Type: servers.SoftReboot, - } - - serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - - err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example to Rebuild a Server - - rebuildOpts := servers.RebuildOpts{ - Name: "new_name", - ImageID: "image-uuid", - } - - serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - - server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract() - if err != nil { - panic(err) - } - -Example to Resize a Server - - resizeOpts := servers.ResizeOpts{ - FlavorRef: "flavor-uuid", - } - - serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - - err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr() - if err != nil { - panic(err) - } - - err = servers.ConfirmResize(computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Snapshot a Server - - snapshotOpts := servers.CreateImageOpts{ - Name: "snapshot_name", - } - - serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - - image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID() - if err != nil { - panic(err) - } -*/ -package servers diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go deleted file mode 100644 index d6a903aab..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go +++ /dev/null @@ -1,784 +0,0 @@ -package servers - -import ( - "encoding/base64" - "encoding/json" - "fmt" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToServerListQuery() (string, error) -} - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the server attributes you want to see returned. Marker and Limit are used -// for pagination. -type ListOpts struct { - // ChangesSince is a time/date stamp for when the server last changed status. - ChangesSince string `q:"changes-since"` - - // Image is the name of the image in URL format. - Image string `q:"image"` - - // Flavor is the name of the flavor in URL format. - Flavor string `q:"flavor"` - - // IP is a regular expression to match the IPv4 address of the server. - IP string `q:"ip"` - - // This requires the client to be set to microversion 2.5 or later, unless - // the user is an admin. - // IP is a regular expression to match the IPv6 address of the server. - IP6 string `q:"ip6"` - - // Name of the server as a string; can be queried with regular expressions. - // Realize that ?name=bob returns both bob and bobb. If you need to match bob - // only, you can use a regular expression matching the syntax of the - // underlying database server implemented for Compute. - Name string `q:"name"` - - // Status is the value of the status of the server so that you can filter on - // "ACTIVE" for example. - Status string `q:"status"` - - // Host is the name of the host as a string. - Host string `q:"host"` - - // Marker is a UUID of the server at which you want to set a marker. - Marker string `q:"marker"` - - // Limit is an integer value for the limit of values to return. - Limit int `q:"limit"` - - // AllTenants is a bool to show all tenants. - AllTenants bool `q:"all_tenants"` - - // TenantID lists servers for a particular tenant. - // Setting "AllTenants = true" is required. - TenantID string `q:"tenant_id"` - - // This requires the client to be set to microversion 2.83 or later, unless - // the user is an admin. - // UserID lists servers for a particular user. - UserID string `q:"user_id"` - - // This requires the client to be set to microversion 2.26 or later. - // Tags filters on specific server tags. All tags must be present for the server. - Tags string `q:"tags"` - - // This requires the client to be set to microversion 2.26 or later. - // TagsAny filters on specific server tags. At least one of the tags must be present for the server. - TagsAny string `q:"tags-any"` - - // This requires the client to be set to microversion 2.26 or later. - // NotTags filters on specific server tags. All tags must be absent for the server. - NotTags string `q:"not-tags"` - - // This requires the client to be set to microversion 2.26 or later. - // NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server. - NotTagsAny string `q:"not-tags-any"` - - // Display servers based on their availability zone (Admin only until microversion 2.82). - AvailabilityZone string `q:"availability_zone"` -} - -// ToServerListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToServerListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListSimple makes a request against the API to list servers accessible to you. -func ListSimple(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToServerListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ServerPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// List makes a request against the API to list servers details accessible to you. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listDetailURL(client) - if opts != nil { - query, err := opts.ToServerListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ServerPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToServerCreateMap() (map[string]interface{}, error) -} - -// Network is used within CreateOpts to control a new server's network -// attachments. -type Network struct { - // UUID of a network to attach to the newly provisioned server. - // Required unless Port is provided. - UUID string - - // Port of a neutron network to attach to the newly provisioned server. - // Required unless UUID is provided. - Port string - - // FixedIP specifies a fixed IPv4 address to be used on this network. - FixedIP string - - // Tag may contain an optional device role tag for the server's virtual - // network interface. This can be used to identify network interfaces when - // multiple networks are connected to one server. - // - // Requires microversion 2.32 through 2.36 or 2.42 or later. - Tag string -} - -// Personality is an array of files that are injected into the server at launch. -type Personality []*File - -// File is used within CreateOpts and RebuildOpts to inject a file into the -// server at launch. -// File implements the json.Marshaler interface, so when a Create or Rebuild -// operation is requested, json.Marshal will call File's MarshalJSON method. -type File struct { - // Path of the file. - Path string - - // Contents of the file. Maximum content size is 255 bytes. - Contents []byte -} - -// MarshalJSON marshals the escaped file, base64 encoding the contents. -func (f *File) MarshalJSON() ([]byte, error) { - file := struct { - Path string `json:"path"` - Contents string `json:"contents"` - }{ - Path: f.Path, - Contents: base64.StdEncoding.EncodeToString(f.Contents), - } - return json.Marshal(file) -} - -// CreateOpts specifies server creation parameters. -type CreateOpts struct { - // Name is the name to assign to the newly launched server. - Name string `json:"name" required:"true"` - - // ImageRef is the ID or full URL to the image that contains the - // server's OS and initial state. - // Also optional if using the boot-from-volume extension. - ImageRef string `json:"imageRef"` - - // FlavorRef is the ID or full URL to the flavor that describes the server's specs. - FlavorRef string `json:"flavorRef"` - - // SecurityGroups lists the names of the security groups to which this server - // should belong. - SecurityGroups []string `json:"-"` - - // UserData contains configuration information or scripts to use upon launch. - // Create will base64-encode it for you, if it isn't already. - UserData []byte `json:"-"` - - // AvailabilityZone in which to launch the server. - AvailabilityZone string `json:"availability_zone,omitempty"` - - // Networks dictates how this server will be attached to available networks. - // By default, the server will be attached to all isolated networks for the - // tenant. - // Starting with microversion 2.37 networks can also be an "auto" or "none" - // string. - Networks interface{} `json:"-"` - - // Metadata contains key-value pairs (up to 255 bytes each) to attach to the - // server. - Metadata map[string]string `json:"metadata,omitempty"` - - // Personality includes files to inject into the server at launch. - // Create will base64-encode file contents for you. - Personality Personality `json:"personality,omitempty"` - - // ConfigDrive enables metadata injection through a configuration drive. - ConfigDrive *bool `json:"config_drive,omitempty"` - - // AdminPass sets the root user password. If not set, a randomly-generated - // password will be created and returned in the response. - AdminPass string `json:"adminPass,omitempty"` - - // AccessIPv4 specifies an IPv4 address for the instance. - AccessIPv4 string `json:"accessIPv4,omitempty"` - - // AccessIPv6 specifies an IPv6 address for the instance. - AccessIPv6 string `json:"accessIPv6,omitempty"` - - // Min specifies Minimum number of servers to launch. - Min int `json:"min_count,omitempty"` - - // Max specifies Maximum number of servers to launch. - Max int `json:"max_count,omitempty"` - - // Tags allows a server to be tagged with single-word metadata. - // Requires microversion 2.52 or later. - Tags []string `json:"tags,omitempty"` -} - -// ToServerCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - if opts.UserData != nil { - var userData string - if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil { - userData = base64.StdEncoding.EncodeToString(opts.UserData) - } else { - userData = string(opts.UserData) - } - b["user_data"] = &userData - } - - if len(opts.SecurityGroups) > 0 { - securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups)) - for i, groupName := range opts.SecurityGroups { - securityGroups[i] = map[string]interface{}{"name": groupName} - } - b["security_groups"] = securityGroups - } - - switch v := opts.Networks.(type) { - case []Network: - if len(v) > 0 { - networks := make([]map[string]interface{}, len(v)) - for i, net := range v { - networks[i] = make(map[string]interface{}) - if net.UUID != "" { - networks[i]["uuid"] = net.UUID - } - if net.Port != "" { - networks[i]["port"] = net.Port - } - if net.FixedIP != "" { - networks[i]["fixed_ip"] = net.FixedIP - } - if net.Tag != "" { - networks[i]["tag"] = net.Tag - } - } - b["networks"] = networks - } - case string: - if v == "auto" || v == "none" { - b["networks"] = v - } else { - return nil, fmt.Errorf(`networks must be a slice of Network struct or a string with "auto" or "none" values, current value is %q`, v) - } - } - - if opts.Min != 0 { - b["min_count"] = opts.Min - } - - if opts.Max != 0 { - b["max_count"] = opts.Max - } - - return map[string]interface{}{"server": b}, nil -} - -// Create requests a server to be provisioned to the user in the current tenant. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - reqBody, err := opts.ToServerCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(listURL(client), reqBody, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete requests that a server previously provisioned be removed from your -// account. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ForceDelete forces the deletion of a server. -func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get requests details on a single server, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 203}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional attributes to the -// Update request. -type UpdateOptsBuilder interface { - ToServerUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts specifies the base attributes that may be updated on an existing -// server. -type UpdateOpts struct { - // Name changes the displayed name of the server. - // The server host name will *not* change. - // Server names are not constrained to be unique, even within the same tenant. - Name string `json:"name,omitempty"` - - // AccessIPv4 provides a new IPv4 address for the instance. - AccessIPv4 string `json:"accessIPv4,omitempty"` - - // AccessIPv6 provides a new IPv6 address for the instance. - AccessIPv6 string `json:"accessIPv6,omitempty"` -} - -// ToServerUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "server") -} - -// Update requests that various attributes of the indicated server be changed. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToServerUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ChangeAdminPassword alters the administrator or root password for a specified -// server. -func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { - b := map[string]interface{}{ - "changePassword": map[string]string{ - "adminPass": newPassword, - }, - } - resp, err := client.Post(actionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// RebootMethod describes the mechanisms by which a server reboot can be requested. -type RebootMethod string - -// These constants determine how a server should be rebooted. -// See the Reboot() function for further details. -const ( - SoftReboot RebootMethod = "SOFT" - HardReboot RebootMethod = "HARD" - OSReboot = SoftReboot - PowerCycle = HardReboot -) - -// RebootOptsBuilder allows extensions to add additional parameters to the -// reboot request. -type RebootOptsBuilder interface { - ToServerRebootMap() (map[string]interface{}, error) -} - -// RebootOpts provides options to the reboot request. -type RebootOpts struct { - // Type is the type of reboot to perform on the server. - Type RebootMethod `json:"type" required:"true"` -} - -// ToServerRebootMap builds a body for the reboot request. -func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "reboot") -} - -/* -Reboot requests that a given server reboot. - -Two methods exist for rebooting a server: - -HardReboot (aka PowerCycle) starts the server instance by physically cutting -power to the machine, or if a VM, terminating it at the hypervisor level. -It's done. Caput. Full stop. -Then, after a brief while, power is restored or the VM instance restarted. - -SoftReboot (aka OSReboot) simply tells the OS to restart under its own -procedure. -E.g., in Linux, asking it to enter runlevel 6, or executing -"sudo shutdown -r now", or by asking Windows to rtart the machine. -*/ -func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { - b, err := opts.ToServerRebootMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// RebuildOptsBuilder allows extensions to provide additional parameters to the -// rebuild request. -type RebuildOptsBuilder interface { - ToServerRebuildMap() (map[string]interface{}, error) -} - -// RebuildOpts represents the configuration options used in a server rebuild -// operation. -type RebuildOpts struct { - // AdminPass is the server's admin password - AdminPass string `json:"adminPass,omitempty"` - - // ImageRef is the ID of the image you want your server to be provisioned on. - ImageRef string `json:"imageRef"` - - // Name to set the server to - Name string `json:"name,omitempty"` - - // AccessIPv4 [optional] provides a new IPv4 address for the instance. - AccessIPv4 string `json:"accessIPv4,omitempty"` - - // AccessIPv6 [optional] provides a new IPv6 address for the instance. - AccessIPv6 string `json:"accessIPv6,omitempty"` - - // Metadata [optional] contains key-value pairs (up to 255 bytes each) - // to attach to the server. - Metadata map[string]string `json:"metadata,omitempty"` - - // Personality [optional] includes files to inject into the server at launch. - // Rebuild will base64-encode file contents for you. - Personality Personality `json:"personality,omitempty"` -} - -// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON -func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - return map[string]interface{}{"rebuild": b}, nil -} - -// Rebuild will reprovision the server according to the configuration options -// provided in the RebuildOpts struct. -func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { - b, err := opts.ToServerRebuildMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ResizeOptsBuilder allows extensions to add additional parameters to the -// resize request. -type ResizeOptsBuilder interface { - ToServerResizeMap() (map[string]interface{}, error) -} - -// ResizeOpts represents the configuration options used to control a Resize -// operation. -type ResizeOpts struct { - // FlavorRef is the ID of the flavor you wish your server to become. - FlavorRef string `json:"flavorRef" required:"true"` -} - -// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON -// request body for the Resize request. -func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "resize") -} - -// Resize instructs the provider to change the flavor of the server. -// -// Note that this implies rebuilding it. -// -// Unfortunately, one cannot pass rebuild parameters to the resize function. -// When the resize completes, the server will be in VERIFY_RESIZE state. -// While in this state, you can explore the use of the new server's -// configuration. If you like it, call ConfirmResize() to commit the resize -// permanently. Otherwise, call RevertResize() to restore the old configuration. -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { - b, err := opts.ToServerResizeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ConfirmResize confirms a previous resize operation on a server. -// See Resize() for more details. -func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ - OkCodes: []int{201, 202, 204}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// RevertResize cancels a previous resize operation on a server. -// See Resize() for more details. -func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ResetMetadataOptsBuilder allows extensions to add additional parameters to -// the Reset request. -type ResetMetadataOptsBuilder interface { - ToMetadataResetMap() (map[string]interface{}, error) -} - -// MetadataOpts is a map that contains key-value pairs. -type MetadataOpts map[string]string - -// ToMetadataResetMap assembles a body for a Reset request based on the contents -// of a MetadataOpts. -func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) { - return map[string]interface{}{"metadata": opts}, nil -} - -// ToMetadataUpdateMap assembles a body for an Update request based on the -// contents of a MetadataOpts. -func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { - return map[string]interface{}{"metadata": opts}, nil -} - -// ResetMetadata will create multiple new key-value pairs for the given server -// ID. -// Note: Using this operation will erase any already-existing metadata and -// create the new metadata provided. To keep any already-existing metadata, -// use the UpdateMetadatas or UpdateMetadata function. -func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { - b, err := opts.ToMetadataResetMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Metadata requests all the metadata for the given server ID. -func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { - resp, err := client.Get(metadataURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to -// the Create request. -type UpdateMetadataOptsBuilder interface { - ToMetadataUpdateMap() (map[string]interface{}, error) -} - -// UpdateMetadata updates (or creates) all the metadata specified by opts for -// the given server ID. This operation does not affect already-existing metadata -// that is not specified by opts. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { - b, err := opts.ToMetadataUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// MetadatumOptsBuilder allows extensions to add additional parameters to the -// Create request. -type MetadatumOptsBuilder interface { - ToMetadatumCreateMap() (map[string]interface{}, string, error) -} - -// MetadatumOpts is a map of length one that contains a key-value pair. -type MetadatumOpts map[string]string - -// ToMetadatumCreateMap assembles a body for a Create request based on the -// contents of a MetadataumOpts. -func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) { - if len(opts) != 1 { - err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.MetadatumOpts" - err.Info = "Must have 1 and only 1 key-value pair" - return nil, "", err - } - metadatum := map[string]interface{}{"meta": opts} - var key string - for k := range metadatum["meta"].(MetadatumOpts) { - key = k - } - return metadatum, key, nil -} - -// CreateMetadatum will create or update the key-value pair with the given key -// for the given server ID. -func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { - b, key, err := opts.ToMetadatumCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Metadatum requests the key-value pair with the given key for the given -// server ID. -func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { - resp, err := client.Get(metadatumURL(client, id, key), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DeleteMetadatum will delete the key-value pair with the given key for the -// given server ID. -func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { - resp, err := client.Delete(metadatumURL(client, id, key), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListAddresses makes a request against the API to list the servers IP -// addresses. -func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { - return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { - return AddressPage{pagination.SinglePageBase(r)} - }) -} - -// ListAddressesByNetwork makes a request against the API to list the servers IP -// addresses for the given network. -func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { - return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { - return NetworkAddressPage{pagination.SinglePageBase(r)} - }) -} - -// CreateImageOptsBuilder allows extensions to add additional parameters to the -// CreateImage request. -type CreateImageOptsBuilder interface { - ToServerCreateImageMap() (map[string]interface{}, error) -} - -// CreateImageOpts provides options to pass to the CreateImage request. -type CreateImageOpts struct { - // Name of the image/snapshot. - Name string `json:"name" required:"true"` - - // Metadata contains key-value pairs (up to 255 bytes each) to attach to - // the created image. - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToServerCreateImageMap formats a CreateImageOpts structure into a request -// body. -func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "createImage") -} - -// CreateImage makes a request against the nova API to schedule an image to be -// created of the server -func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { - b, err := opts.ToServerCreateImageMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// GetPassword makes a request against the nova API to get the encrypted -// administrative password. -func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { - resp, err := client.Get(passwordURL(client, serverId), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be -// used as ShowConsoleOutput options -type ShowConsoleOutputOptsBuilder interface { - ToServerShowConsoleOutputMap() (map[string]interface{}, error) -} - -// ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder -type ShowConsoleOutputOpts struct { - // The number of lines to fetch from the end of console log. - // All lines will be returned if this is not specified. - Length int `json:"length,omitempty"` -} - -// ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. -func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") -} - -// ShowConsoleOutput makes a request against the nova API to get console log from the server -func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { - b, err := opts.ToServerShowConsoleOutputMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go deleted file mode 100644 index cadef0545..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go +++ /dev/null @@ -1,21 +0,0 @@ -package servers - -import "github.com/gophercloud/gophercloud" - -// WaitForStatus will continually poll a server until it successfully -// transitions to a specified status. It will do this for at most the number -// of seconds specified. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/vendor/github.com/gophercloud/gophercloud/.gitignore b/vendor/github.com/gophercloud/gophercloud/v2/.gitignore similarity index 80% rename from vendor/github.com/gophercloud/gophercloud/.gitignore rename to vendor/github.com/gophercloud/gophercloud/v2/.gitignore index d7a5e5293..8b1b79e61 100644 --- a/vendor/github.com/gophercloud/gophercloud/.gitignore +++ b/vendor/github.com/gophercloud/gophercloud/v2/.gitignore @@ -2,3 +2,4 @@ .idea .vscode testing_*.coverprofile +/cover.out diff --git a/vendor/github.com/gophercloud/gophercloud/v2/.golangci.yaml b/vendor/github.com/gophercloud/gophercloud/v2/.golangci.yaml new file mode 100644 index 000000000..2de1155c0 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/.golangci.yaml @@ -0,0 +1,34 @@ +version: "2" +linters: + default: none + enable: + - errcheck + - govet + - staticcheck + - unparam + - unused + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - staticcheck + text: "SA1019: (x509.EncryptPEMBlock|strings.Title)" + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/gophercloud/gophercloud/v2/AGENTS.md b/vendor/github.com/gophercloud/gophercloud/v2/AGENTS.md new file mode 100644 index 000000000..1b5a20580 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/AGENTS.md @@ -0,0 +1,215 @@ +# AGENTS.md + +Quick reference guide for AI coding agents working in the Gophercloud repository. + +**Project:** Gophercloud - Go SDK for OpenStack services +**Module:** `github.com/gophercloud/gophercloud/v2` +**Language:** Go (see version in [go.mod](go.mod)) +**Stable Branch:** v2 (main development on `main`) + +## Build, Test & Lint Commands + +### Running Tests + +**Unit tests (default):** +```bash +make unit +``` + +**Unit tests with verbose output:** +```bash +go test -v ./... +``` + +**Run single test by name:** +```bash +cd openstack/compute/v2/servers +go test -run TestCreateServer ./... +``` + +**Coverage:** +```bash +make coverage +``` + +**Acceptance tests (requires live OpenStack - may incur charges):** +```bash +make acceptance # All services +make acceptance-compute # Specific service +``` + +**Run single acceptance test:** +```bash +cd internal/acceptance/openstack/compute/v2 +go test -timeout 60m -tags "acceptance" -run TestServersList +``` + +### Linting & Formatting + +```bash +make lint # Run golangci-lint in container (Docker/Podman) +make format # Run gofmt with simplify flag +``` + +**Note:** If lint fails with SELinux errors, run: +```bash +chcon -Rt svirt_sandbox_file_t . +chcon -Rt svirt_sandbox_file_t ~/.cache/golangci-lint +``` + +## Code Style Guidelines + +### Import Organization + +Group imports in this order (separated by blank lines): +1. Standard library (alphabetically) +2. External dependencies (alphabetically) +3. Gophercloud internal packages (alphabetically) + +Example: +```go +import ( + "context" + "encoding/json" + "fmt" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) +``` + +### File Structure + +Standard package structure under `openstack////`: +- **`requests.go`** - HTTP request functions and OptsBuilder types +- **`results.go`** - Response structs and extraction methods +- **`urls.go`** - Endpoint URL construction helpers +- **`microversions.go`** - Microversion-specific types (when needed) +- **`testing/`** - Unit tests with HTTP mocking + +### Naming Conventions + +**Result receivers and variables:** +- Result method receiver: `r` +- Unmarshalled variable: `s` +- Request function return value: `r` + +**OptsBuilder pattern:** +- Interface name: `OptsBuilder` (e.g., `CreateOptsBuilder`, `ListOptsBuilder`) +- Method for request body: `ToMap` (e.g., `ToServerCreateMap`) +- Method for query string: `ToQuery` (e.g., `ToServerListQuery`) + +Example: +```go +type CreateOptsBuilder interface { + ToServerCreateMap() (map[string]interface{}, error) +} + +type CreateOpts struct { + Name string `json:"name"` +} + +func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "server") +} +``` + +### Types & Pointers + +- **New response fields (microversions):** Use pointer types to allow nil-checking +- **Optional request fields:** Always use `omitempty` JSON tag +- **Required fields:** No `omitempty` tag + +### Error Handling + +- Use `gophercloud.Result` and `gophercloud.ErrResult` types +- Extract errors with `.ExtractErr()` method +- Return errors directly, don't wrap unless adding context + +### Documentation + +- **All struct fields** must have GoDoc comments +- **Microversion-dependent fields** must document required version in GoDoc +- **Package documentation** goes in `doc.go` +- Follow existing comment style in similar packages + +Example: +```go +// This requires the client to be set to microversion 2.52 or later. +// Tags is the list of server tags. +Tags []string `json:"tags,omitempty"` +``` + +### Testing Requirements + +**Unit tests (in `testing/` subdirectory):** +- Use `testhelper` package to mock HTTP +- `fakeServer := th.SetupHTTP()` / `defer fakeServer.Teardown()` for setup/teardown +- `fakeServer.Mux.HandleFunc()` to register mock endpoints +- Test ALL options (every field in request/response structs) +- Use assertion helpers from `testhelper/convenience.go` (value assertions) and `testhelper/http_responses.go` (HTTP request assertions) +- `Assert*` variants are fatal (`t.Fatalf`), `Check*` variants are non-fatal (`t.Errorf`) +- Assertion argument order is **expected first, actual second**: `th.AssertEquals(t, "expected_value", actual.Field)` + +**Acceptance tests:** +- Located in `internal/acceptance/openstack//` +- Test against real OpenStack APIs +- Cover all operation variants + +## Microversions + +Set microversion on ServiceClient: +```go +client.Microversion = "2.52" +``` + +**Implementation rules:** +- **New request fields:** Must use `omitempty` + document microversion +- **New response fields:** Add as pointer types +- **Changed response types:** Create new structs in `microversions.go` + +See `docs/MICROVERSIONS.md` for details. + +## Pull Request Requirements + +**Before opening PR:** +1. **GitHub issue must exist** with core contributor approval +2. **PR description must include:** + - `For #` reference + - Link(s) to OpenStack source code (non-master branch) proving validity +3. **Keep PRs focused:** Group related operations together; avoid mixing unrelated changes +4. **Tests required:** Unit tests AND acceptance tests covering all options +5. **Work-in-progress:** Prefix title with `[wip]` until ready +6. **Dependencies:** Prefix with `[Pending #PRNUM]` if depends on another PR + +**During review:** +- Do NOT squash commits (only append) +- Follow existing patterns in codebase +- Address all reviewer feedback + +## Common Patterns + +**Context usage:** +Always pass `context.Context` to API operations: +```go +servers.List(client, opts).EachPage(ctx, func(ctx context.Context, page pagination.Page) (bool, error) { + // ... +}) +``` + +**Pagination:** +```go +pager := servers.List(client, servers.ListOpts{}) +err := pager.EachPage(ctx, func(ctx context.Context, page pagination.Page) (bool, error) { + servers, err := servers.ExtractServers(page) + // process... + return true, nil +}) +``` + +## Key Reminders + +- Module path: `github.com/gophercloud/gophercloud/v2` (note the `/v2`) +- Gophercloud does NOT validate microversion compatibility +- PRs target `main` branch, not `v2` +- Documentation auto-generated from GoDoc comments diff --git a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md b/vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md similarity index 76% rename from vendor/github.com/gophercloud/gophercloud/CHANGELOG.md rename to vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md index b19b5e775..c759354df 100644 --- a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md +++ b/vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md @@ -1,18 +1,263 @@ -## v1.14.1 (2024-09-18) +## v2.13.0 (2026-06-25) + +* [GH-3717](https://github.com/gophercloud/gophercloud/pull/3717) build(deps): bump golang.org/x/crypto from 0.49.0 to 0.50.0 +* [GH-3720](https://github.com/gophercloud/gophercloud/pull/3720) build(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 +* [GH-3723](https://github.com/gophercloud/gophercloud/pull/3723) build(deps): bump github/codeql-action from 4.35.1 to 4.35.2 +* [GH-3728](https://github.com/gophercloud/gophercloud/pull/3728) [v2] Implement resource provider allocations +* [GH-3730](https://github.com/gophercloud/gophercloud/pull/3730) [v2] fix: skip non-struct fields in extractIntoPtr slice path to prevent unmarshal panic +* [GH-3731](https://github.com/gophercloud/gophercloud/pull/3731) [v2] Implement usages +* [GH-3732](https://github.com/gophercloud/gophercloud/pull/3732) [v2] Leverage RandomUUID() in Placement resourceproviders tests +* [GH-3737](https://github.com/gophercloud/gophercloud/pull/3737) [v2] Bump golangci-lint +* [GH-3744](https://github.com/gophercloud/gophercloud/pull/3744) build(deps): bump actions/labeler from 6.0.1 to 6.1.0 +* [GH-3752](https://github.com/gophercloud/gophercloud/pull/3752) build(deps): bump github/codeql-action from 4.35.2 to 4.35.4 +* [GH-3758](https://github.com/gophercloud/gophercloud/pull/3758) build(deps): bump golang.org/x/crypto from 0.50.0 to 0.51.0 +* [GH-3760](https://github.com/gophercloud/gophercloud/pull/3760) [v2] fix(vpnaas): change encryption and auth method in acceptance tests +* [GH-3762](https://github.com/gophercloud/gophercloud/pull/3762) [v2] Allow clearing Placement resource provider parent_provider_uuid +* [GH-3764](https://github.com/gophercloud/gophercloud/pull/3764) build(deps): bump github/codeql-action from 4.35.4 to 4.35.5 +* [GH-3769](https://github.com/gophercloud/gophercloud/pull/3769) [v2] db/instances: remove hardcoded 300GB size cap +* [GH-3770](https://github.com/gophercloud/gophercloud/pull/3770) [v2] neutron: properly enable port-forwarding extension in devstack +* [GH-3771](https://github.com/gophercloud/gophercloud/pull/3771) [v2] tests: Prefer extension check over environment variable +* [GH-3772](https://github.com/gophercloud/gophercloud/pull/3772) [v2] Add Placement resourceprovider list post 1.39 +* [GH-3774](https://github.com/gophercloud/gophercloud/pull/3774) [v2] Add support for listing recordsets across all zones +* [GH-3775](https://github.com/gophercloud/gophercloud/pull/3775) [v2] CI: Use pre-built amphora images for Octavia jobs +* [GH-3776](https://github.com/gophercloud/gophercloud/pull/3776) [v2] allow port ranges in floating ip portforwarding +* [GH-3781](https://github.com/gophercloud/gophercloud/pull/3781) build(deps): bump golang.org/x/crypto from 0.51.0 to 0.52.0 +* [GH-3784](https://github.com/gophercloud/gophercloud/pull/3784) build(deps): bump github/codeql-action from 4.35.5 to 4.36.0 +* [GH-3787](https://github.com/gophercloud/gophercloud/pull/3787) [v2] docs: relax one-operation-per-PR rule +* [GH-3790](https://github.com/gophercloud/gophercloud/pull/3790) [v2] Add AGENTS.md +* [GH-3794](https://github.com/gophercloud/gophercloud/pull/3794) build(deps): bump github/codeql-action from 4.36.0 to 4.36.1 +* [GH-3802](https://github.com/gophercloud/gophercloud/pull/3802) [v2] dns: add AllProjects support to zones.List via X-Auth-All-Projects header +* [GH-3808](https://github.com/gophercloud/gophercloud/pull/3808) dns: fix SudoTenantID type in zones ListOpts + + +## v2.12.0 (2026-04-10) + +* [GH-3653](https://github.com/gophercloud/gophercloud/pull/3653) [v2] Fix clouds.yaml search path to use XDG convention on all platforms +* [GH-3660](https://github.com/gophercloud/gophercloud/pull/3660) [v2] Use jammy amphora in octavia epoxy job +* [GH-3670](https://github.com/gophercloud/gophercloud/pull/3670) [v2] Implement READ operations on Placement traits +* [GH-3673](https://github.com/gophercloud/gophercloud/pull/3673) [v2] Github action fixes +* [GH-3674](https://github.com/gophercloud/gophercloud/pull/3674) [v2] Update acceptance README to remove mentions of Packstack +* [GH-3678](https://github.com/gophercloud/gophercloud/pull/3678) [v2] Implement CREATE/UPDATE operation on Placement traits +* [GH-3679](https://github.com/gophercloud/gophercloud/pull/3679) [v2] Implement DELETE operations on Placement traits +* [GH-3682](https://github.com/gophercloud/gophercloud/pull/3682) [v2] Implement resource_classes GET operations +* [GH-3683](https://github.com/gophercloud/gophercloud/pull/3683) [v2] Implement resource classes CREATE operations +* [GH-3684](https://github.com/gophercloud/gophercloud/pull/3684) [v2] Implement resource classes DELETE operation +* [GH-3690](https://github.com/gophercloud/gophercloud/pull/3690) [v2] Add metric-storage (Aetos) v1 service support +* [GH-3691](https://github.com/gophercloud/gophercloud/pull/3691) [v2] Implement GET/UPDATE/DELETE operations on Placement resource providers inventories +* [GH-3692](https://github.com/gophercloud/gophercloud/pull/3692) [v2] Fix TestCreateTempURL flaky test by removing hardcoded port dependency +* [GH-3695](https://github.com/gophercloud/gophercloud/pull/3695) [v2] Remove legacy workflows +* [GH-3698](https://github.com/gophercloud/gophercloud/pull/3698) build(deps): bump golang.org/x/crypto from 0.33.0 to 0.49.0 +* [GH-3699](https://github.com/gophercloud/gophercloud/pull/3699) build(deps): bump actions/setup-go from 6.3.0 to 6.4.0 +* [GH-3700](https://github.com/gophercloud/gophercloud/pull/3700) build(deps): bump github/codeql-action from 4.34.1 to 4.35.1 +* [GH-3703](https://github.com/gophercloud/gophercloud/pull/3703) [v2] CI: prefer github mirrors whenever possible +* [GH-3708](https://github.com/gophercloud/gophercloud/pull/3708) [v2] Implement Placement allocationcandidates + +## v2.11.1 (2026-03-10) + +* [GH-3648](https://github.com/gophercloud/gophercloud/pull/3648) [v2] Do not specify go patch version + +## v2.11.0 (2026-03-04) + +* [GH-3602](https://github.com/gophercloud/gophercloud/pull/3602) [v2] Add PCIAddress field to baremetal InterfaceType +* [GH-3610](https://github.com/gophercloud/gophercloud/pull/3610) [v2] Networking V2: Added support for ML2 extension port_trusted_vif +* [GH-3611](https://github.com/gophercloud/gophercloud/pull/3611) [v2] networking/v2/layer3/routers: Add external gateways management +* [GH-3625](https://github.com/gophercloud/gophercloud/pull/3625) [v2] Use jimmy amphora in octavia job +* [GH-3629](https://github.com/gophercloud/gophercloud/pull/3629) [v2] Add a new Ironic field representing node health to Gophercloud +* [GH-3630](https://github.com/gophercloud/gophercloud/pull/3630) [v2] Bump go +* [GH-3632](https://github.com/gophercloud/gophercloud/pull/3632) [v2] CI: Fix fwaas jobs +* [GH-3633](https://github.com/gophercloud/gophercloud/pull/3633) [v2] Add TSIG key support for OpenStack DNS v2 API +* [GH-3640](https://github.com/gophercloud/gophercloud/pull/3640) [v2] fix: networkipavailabilities: handle scientific notation in IP counts + +## v2.10.0 (2026-01-05) + +* [GH-3569](https://github.com/gophercloud/gophercloud/pull/3569) identity/role: restore backward compatibility for description +* [GH-3570](https://github.com/gophercloud/gophercloud/pull/3570) [v2] identityv3/service: add `name` and `description` fields +* [GH-3577](https://github.com/gophercloud/gophercloud/pull/3577) [v2] add: description field to identity.v3.Endpoint +* [GH-3580](https://github.com/gophercloud/gophercloud/pull/3580) [v2] keystone: add support for per page limit +* [GH-3582](https://github.com/gophercloud/gophercloud/pull/3582) [v2] network.v2.Port: add support for update mac_address +* [GH-3585](https://github.com/gophercloud/gophercloud/pull/3585) [v2] identity.v3.Endpoint: add enabled field on creation and on update +* [GH-3590](https://github.com/gophercloud/gophercloud/pull/3590) [v2] fix (image/v2/images): image v2 unmarshal issue +* [GH-3594](https://github.com/gophercloud/gophercloud/pull/3594) [v2] Add new fields for ListOpts volumetypes +* [GH-3595](https://github.com/gophercloud/gophercloud/pull/3595) [v2] identity.v3.Endpoint: make name as optional on creation + +## v2.9.0 (2025-11-17) + +* [GH-3508](https://github.com/gophercloud/gophercloud/pull/3508) [v2] Trigger "hold" workflow on merge groups +* [GH-3511](https://github.com/gophercloud/gophercloud/pull/3511) [v2] Closes #2321 - Fix TestRolesCRUD by including DomainID to TestRolesCRUD +* [GH-3513](https://github.com/gophercloud/gophercloud/pull/3513) [v2] build(deps): bump actions/labeler from 5 to 6 +* [GH-3516](https://github.com/gophercloud/gophercloud/pull/3516) [v2] refactor: Trivial fixes +* [GH-3524](https://github.com/gophercloud/gophercloud/pull/3524) [v2] [glance]: Add 'uploading' status +* [GH-3525](https://github.com/gophercloud/gophercloud/pull/3525) [v2] compute: Add host aggregate uuid field +* [GH-3526](https://github.com/gophercloud/gophercloud/pull/3526) [v2] Enable deletion for network and loadbalancer quotas +* [GH-3541](https://github.com/gophercloud/gophercloud/pull/3541) [v2] docs: Document tested releases for acceptance tests +* [GH-3544](https://github.com/gophercloud/gophercloud/pull/3544) [v2] Identity V3: Add Options field to roles. +* [GH-3547](https://github.com/gophercloud/gophercloud/pull/3547) [v2] Add config_drive to server struct +* [GH-3548](https://github.com/gophercloud/gophercloud/pull/3548) [v2] Identity: Add description field to roles +* [GH-3549](https://github.com/gophercloud/gophercloud/pull/3549) [v2] compute: add cpu info topology cells entry +* [GH-3550](https://github.com/gophercloud/gophercloud/pull/3550) [v2] Migrate epoxy jobs to Ubuntu 24.04 (Noble), drop caracal jobs +* [GH-3551](https://github.com/gophercloud/gophercloud/pull/3551) [v2] build(deps): bump github/codeql-action from 3 to 4 +* [GH-3557](https://github.com/gophercloud/gophercloud/pull/3557) [v2] Fix EC2 authentication to work with new Keystone auth requirement +* [GH-3558](https://github.com/gophercloud/gophercloud/pull/3558) [v2] identity/services: add omitempty to the `type` field +* [GH-3559](https://github.com/gophercloud/gophercloud/pull/3559) [v2] fix: handle Nova create image response for microversion 2.45 and above + +## v2.8.0 (2025-08-18) + +* [GH-3348](https://github.com/gophercloud/gophercloud/pull/3348) [v2] [networking] add ExtractRoutersInto func helper to routers +* [GH-3354](https://github.com/gophercloud/gophercloud/pull/3354) [v2] Fix a small typo +* [GH-3358](https://github.com/gophercloud/gophercloud/pull/3358) [v2] tests: fix devstack master branch tests +* [GH-3361](https://github.com/gophercloud/gophercloud/pull/3361) [v2] octavia: fix http_version type to float +* [GH-3362](https://github.com/gophercloud/gophercloud/pull/3362) [v2] tests: fix containerinfra template creation +* [GH-3367](https://github.com/gophercloud/gophercloud/pull/3367) [v2] Use Makefile for CI jobs +* [GH-3375](https://github.com/gophercloud/gophercloud/pull/3375) [v2] core: add missing Builder interfaces +* [GH-3378](https://github.com/gophercloud/gophercloud/pull/3378) [v2] tests: fix failing rabbitmq service +* [GH-3379](https://github.com/gophercloud/gophercloud/pull/3379) [v2] CI: Remove Bobcat +* [GH-3384](https://github.com/gophercloud/gophercloud/pull/3384) [v2] Move master CI jobs to Ubuntu 24.04 +* [GH-3386](https://github.com/gophercloud/gophercloud/pull/3386) [v2] tests: Fix TestBGPAgentCRUD +* [GH-3387](https://github.com/gophercloud/gophercloud/pull/3387) [v2] Update the doc of openstack.AuthOptionsFromEnv function +* [GH-3389](https://github.com/gophercloud/gophercloud/pull/3389) [v2] networking: add constants for statuses +* [GH-3391](https://github.com/gophercloud/gophercloud/pull/3391) [v2] CI: Add Epoxy +* [GH-3393](https://github.com/gophercloud/gophercloud/pull/3393) [v2] dns: implement shared zones list +* [GH-3394](https://github.com/gophercloud/gophercloud/pull/3394) [v2] acceptance: Prevent 409 when bulk-creating secgroup rules +* [GH-3396](https://github.com/gophercloud/gophercloud/pull/3396) [v2] identity: add support for string boolean in users' enabled member +* [GH-3397](https://github.com/gophercloud/gophercloud/pull/3397) [v2] Adjust List func to accept a Builder in tenants, routers and security groups packages +* [GH-3399](https://github.com/gophercloud/gophercloud/pull/3399) [v2] blockstorage: add manage-existing and unmanage api call +* [GH-3401](https://github.com/gophercloud/gophercloud/pull/3401) [v2] Added address groups to Networking extensions, with tests. +* [GH-3407](https://github.com/gophercloud/gophercloud/pull/3407) [v2] neutron: add segment_id support to subnets +* [GH-3413](https://github.com/gophercloud/gophercloud/pull/3413) [v2] build(deps): bump joelanford/go-apidiff from 0.8.2 to 0.8.3 +* [GH-3416](https://github.com/gophercloud/gophercloud/pull/3416) [v2] tests: bump devstack-action +* [GH-3422](https://github.com/gophercloud/gophercloud/pull/3422) [v2] Fix documentation for gateway_ip in subnet update +* [GH-3431](https://github.com/gophercloud/gophercloud/pull/3431) [v2] Use container-infra for OpenStack-API-Version +* [GH-3433](https://github.com/gophercloud/gophercloud/pull/3433) [v2] make: Use fixed version of gotestsum +* [GH-3434](https://github.com/gophercloud/gophercloud/pull/3434) [v2] Randomize test order for unit tests +* [GH-3435](https://github.com/gophercloud/gophercloud/pull/3435) [v2] Add versioned endpoint discovery +* [GH-3438](https://github.com/gophercloud/gophercloud/pull/3438) [v2] dns: add support for /v2/quotas +* [GH-3439](https://github.com/gophercloud/gophercloud/pull/3439) [v2] neutron: add segments extension package +* [GH-3446](https://github.com/gophercloud/gophercloud/pull/3446) nova: add support for hostname updates +* [GH-3452](https://github.com/gophercloud/gophercloud/pull/3452) [v2] neutron: allow omission of subnet_id for IP address +* [GH-3454](https://github.com/gophercloud/gophercloud/pull/3454) [v2] blockstorage: add isPublic query option for volume types +* [GH-3458](https://github.com/gophercloud/gophercloud/pull/3458) [v2] Fix pagination for messaging client +* [GH-3465](https://github.com/gophercloud/gophercloud/pull/3465) [v2] tests: Fix TestVLANTransparentCRUD test +* [GH-3466](https://github.com/gophercloud/gophercloud/pull/3466) [v2] tests: fix tests for recent PR backports +* [GH-3469](https://github.com/gophercloud/gophercloud/pull/3469) [v2] tests: shorten GH-A job names +* [GH-3473](https://github.com/gophercloud/gophercloud/pull/3473) [v2] core: clone service type aliases instead of referencing global slice +* [GH-3475](https://github.com/gophercloud/gophercloud/pull/3475) [v2] Implement update & delete traits on resource provider +* [GH-3476](https://github.com/gophercloud/gophercloud/pull/3476) [v2] tests: fix volumetypes unit tests +* [GH-3477](https://github.com/gophercloud/gophercloud/pull/3477) [v2] script: Improve getenvvar helper +* [GH-3481](https://github.com/gophercloud/gophercloud/pull/3481) [v2] Implement hypervisors.GetExt: Get with Query parameter +* [GH-3487](https://github.com/gophercloud/gophercloud/pull/3487) [v2] Add networking taas tapmirror suite +* [GH-3489](https://github.com/gophercloud/gophercloud/pull/3489) [v2] Fix incorrect ICMP field description in PortRangeMax comment +* [GH-3494](https://github.com/gophercloud/gophercloud/pull/3494) [v2] Networking v2: Support two time formats for subnet, router, SG rule (#3492) +* [GH-3495](https://github.com/gophercloud/gophercloud/pull/3495) [v2] build(deps): bump actions/checkout from 4 to 5 + +## v2.7.0 (2025-04-03) + +* [GH-3306](https://github.com/gophercloud/gophercloud/pull/3306) [v2] identity: Add Get endpoint by ID +* [GH-3325](https://github.com/gophercloud/gophercloud/pull/3325) [v2] Switch to a version of gocovmerge compatible with go 1.22 +* [GH-3327](https://github.com/gophercloud/gophercloud/pull/3327) Merge pull request #3209 from shiftstack/proper-service-discovery +* [GH-3328](https://github.com/gophercloud/gophercloud/pull/3328) [v2] Improve support for `network standard-attr-*` extensions +* [GH-3330](https://github.com/gophercloud/gophercloud/pull/3330) [v2] Enhance Snapshot struct and add ListDetail function in V3 blockstorage +* [GH-3333](https://github.com/gophercloud/gophercloud/pull/3333) [v2] vpnaas: add support for more ciphers (auth, encryption, pfs modes) +* [GH-3334](https://github.com/gophercloud/gophercloud/pull/3334) [v2] Added support for VIF's in Baremetal +* [GH-3335](https://github.com/gophercloud/gophercloud/pull/3335) [v2] Baremetal virtual media Get API + +## v2.6.0 (2025-03-03) + +* [GH-3309](https://github.com/gophercloud/gophercloud/pull/3309) Backport: Added support for hypervisor_hostname to v2 + +## v2.5.0 (2025-02-11) + +* [GH-3278](https://github.com/gophercloud/gophercloud/pull/3278) [v2] test: Ensure that randomly created secgroup rules don't conflict +* [GH-3287](https://github.com/gophercloud/gophercloud/pull/3287) [v2] Fix panic in ExtractIntoStructPtr +* [GH-3288](https://github.com/gophercloud/gophercloud/pull/3288) [v2] Fix JSON field name hints in APIVersion structs +* [GH-3292](https://github.com/gophercloud/gophercloud/pull/3292) [v2] Add permissions to the label-issue workflow +* [GH-3294](https://github.com/gophercloud/gophercloud/pull/3294) [v2] Add support for zone sharing in DNS v2 +* [GH-3296](https://github.com/gophercloud/gophercloud/pull/3296) build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 +* [GH-3297](https://github.com/gophercloud/gophercloud/pull/3297) [v2] build(deps): bump golang.org/x/crypto from 0.31.0 to 0.32.0 +* [GH-3298](https://github.com/gophercloud/gophercloud/pull/3298) [v2] build(deps): bump golang.org/x/crypto from 0.32.0 to 0.33.0 + +## v2.4.0 (2024-12-18) + +* [GH-3270](https://github.com/gophercloud/gophercloud/pull/3270) [v2] SG rules: implement bulk create +* [GH-3273](https://github.com/gophercloud/gophercloud/pull/3273) [v2] Add missing fields in Objectstorage and compute API + +## v2.3.0 (2024-12-06) + +* [GH-3213](https://github.com/gophercloud/gophercloud/pull/3213) [v2] Handle nova api version > 2.87 for hypervisor +* [GH-3236](https://github.com/gophercloud/gophercloud/pull/3236) [v2] Added required fields for Node API Parity +* [GH-3248](https://github.com/gophercloud/gophercloud/pull/3248) [v2] Add support for disable_power_off +* [GH-3261](https://github.com/gophercloud/gophercloud/pull/3261) [v2] Misc cleanups +* [GH-3262](https://github.com/gophercloud/gophercloud/pull/3262) [v2] Address govet 1.24 issue + +## v2.2.0 (2024-10-18) + +* [GH-3176](https://github.com/gophercloud/gophercloud/pull/3176) [v2] [containerinfra]: add "MasterLBEnabled" in Cluster results +* [GH-3181](https://github.com/gophercloud/gophercloud/pull/3181) [v2] octavia: add new options to health monitors +* [GH-3182](https://github.com/gophercloud/gophercloud/pull/3182) [v2] octavia: add new security options to pools and listeners +* [GH-3195](https://github.com/gophercloud/gophercloud/pull/3195) [v2] [core]: handle empty response body +* [GH-3196](https://github.com/gophercloud/gophercloud/pull/3196) [v2] [fwaas_v2]: proper ParseResponse handling +* [GH-3198](https://github.com/gophercloud/gophercloud/pull/3198) [v2] compute: Fix expected and actual test results +* [GH-3199](https://github.com/gophercloud/gophercloud/pull/3199) [v2] [octavia] add an ability to filter flavors and flavorprofiles +* [GH-3214](https://github.com/gophercloud/gophercloud/pull/3214) [v2] [manila] add scheduler_hints to the shares CreateOpts +* [GH-3215](https://github.com/gophercloud/gophercloud/pull/3215) [v2] [manila] add share_group_id to share's CreateOpts +* [GH-3219](https://github.com/gophercloud/gophercloud/pull/3219) [v2] docs: Remove outdated godoc + +## v2.1.1 (2024-09-18) + +* [GH-3161](https://github.com/gophercloud/gophercloud/pull/3161) [v2] fix: create security group rule with any protocol + +## v2.1.0 (2024-07-24) + +* [GH-3078](https://github.com/gophercloud/gophercloud/pull/3078) [networking]: add BGP VPNs support +* [GH-3086](https://github.com/gophercloud/gophercloud/pull/3086) build(deps): bump golang.org/x/crypto from 0.24.0 to 0.25.0 +* [GH-3090](https://github.com/gophercloud/gophercloud/pull/3090) Adding support for field dns_publish_fixed_ip in a subnet +* [GH-3092](https://github.com/gophercloud/gophercloud/pull/3092) [neutron]: introduce Stateful argument for the security groups +* [GH-3094](https://github.com/gophercloud/gophercloud/pull/3094) [neutron]: introduce Description argument for the portforwarding +* [GH-3106](https://github.com/gophercloud/gophercloud/pull/3106) clouds: Parse trust_id from clouds.yaml +* [GH-3131](https://github.com/gophercloud/gophercloud/pull/3131) Align ServiceFail provisioning state value with Ironic +* [GH-3136](https://github.com/gophercloud/gophercloud/pull/3136) Added node.Retired + +## v2.0.0 (2024-05-27) + +MAIN BREAKING CHANGES: + +* **Gophercloud now requires Go v1.22.** +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Gophercloud now escapes container and object names in all `objects` and `containers` functions. If you were previously escaping names (with, for example: `url.PathEscape` or `url.QueryEscape`), then you should REMOVE that and pass the intended names to Gophercloud directly. +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) The `containers.ListOpts#Full` and `objects.ListOpts#Full` properties are REMOVED from the Gophercloud API. The reason for that is: plaintext listing is unfixably wrong and won't handle special characters reliably (i.e. `\n`). +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Empty container names, container names containing a slash (`/`), and empty object names are now rejected in Gophercloud before any call to Swift. +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) In `objectstorage`: `containers.ErrInvalidContainerName` is now `v1.ErrInvalidContainerName`. +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) New name validation errors in `objectstorage`: + * `v1.ErrEmptyContainerName` + * `v1.ErrEmptyObjectName` +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) In `objects.Copy`: the `destination` field (e.g. `objects.CopyOpts#Destination`) must be in the form `/container/object`: the function will reject a destination path if it doesn't start with a slash (`/`). +* [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts +* [GH-2886](https://github.com/gophercloud/gophercloud/pull/2886) ports: Fix value_specs implementation +* [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter +* [GH-2936](https://github.com/gophercloud/gophercloud/pull/2936) Make Gophercloud context-aware: all function signatures triggering an HTTP call now accept a context.Context for tracing and cancellation +* [GH-2970](https://github.com/gophercloud/gophercloud/pull/2970) Remove context from the Provider client +* [GH-2904](https://github.com/gophercloud/gophercloud/pull/2904) Remove error code types -* [GH-3162](https://github.com/gophercloud/gophercloud/pull/3162) Fix security group rule "any protocol" - -## v1.14.0 (2024-07-24) - -* [GH-3095](https://github.com/gophercloud/gophercloud/pull/3095) [neutron]: introduce Description argument for the portforwarding -* [GH-3098](https://github.com/gophercloud/gophercloud/pull/3098) [neutron]: introduce Stateful argument for the security groups -* [GH-3099](https://github.com/gophercloud/gophercloud/pull/3099) [networking]: subnet add field dns_publish_fixed_ip - -## v1.13.0 (2024-07-08) +New features and improvements: -* [GH-3044](https://github.com/gophercloud/gophercloud/pull/3044) [v1] Add ci jobs for openstack caracal -* [GH-3073](https://github.com/gophercloud/gophercloud/pull/3073) [v1] Adding missing QoS field for router -* [GH-3080](https://github.com/gophercloud/gophercloud/pull/3080) [networking]: add BGP VPNs support (backport to 1.x) +* [GH-2486](https://github.com/gophercloud/gophercloud/pull/2486) Fix BareMetalV1 version +* [GH-2492](https://github.com/gophercloud/gophercloud/pull/2492) Add tags for loadbalancer l7policy and l7rule +* [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts +* [GH-2561](https://github.com/gophercloud/gophercloud/pull/2561) compute: add ext_specs to flavor +* [GH-2613](https://github.com/gophercloud/gophercloud/pull/2613) Migrate baremetal inventory to a common location +* [GH-2724](https://github.com/gophercloud/gophercloud/pull/2724) baremetal: introduce Node Inventory API +* [GH-2725](https://github.com/gophercloud/gophercloud/pull/2725) baremetal: finish moving common inventory bits +* [GH-2736](https://github.com/gophercloud/gophercloud/pull/2736) Composable templates +* [GH-2781](https://github.com/gophercloud/gophercloud/pull/2781) baremetal: support ironic native PluginData +* [GH-2791](https://github.com/gophercloud/gophercloud/pull/2791) Add microversion utilities +* [GH-2806](https://github.com/gophercloud/gophercloud/pull/2806) Fix list ports with multiple fixedip parameters +* [GH-2809](https://github.com/gophercloud/gophercloud/pull/2809) Remove code for CDN (poppy) +* [GH-2812](https://github.com/gophercloud/gophercloud/pull/2812) Revert "Fix baremetal jobs on Ubuntu 20.04" +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) objects: Escape names in Gophercloud +* [GH-2828](https://github.com/gophercloud/gophercloud/pull/2828) Octavia: Add tags to resources missing them +* [GH-2834](https://github.com/gophercloud/gophercloud/pull/2834) baremetal: implemented ParsedLLDP in the standard PluginData +* [GH-2866](https://github.com/gophercloud/gophercloud/pull/2866) loadbalancer additional_vips by snigle +* [GH-2881](https://github.com/gophercloud/gophercloud/pull/2881) Adding missing QoS field for router +* [GH-2883](https://github.com/gophercloud/gophercloud/pull/2883) Context-aware methods to ProviderClient and ServiceClient +* [GH-2892](https://github.com/gophercloud/gophercloud/pull/2892) Authenticate with a clouds.yaml ## v1.12.0 (2024-05-27) diff --git a/vendor/github.com/gophercloud/gophercloud/LICENSE b/vendor/github.com/gophercloud/gophercloud/v2/LICENSE similarity index 100% rename from vendor/github.com/gophercloud/gophercloud/LICENSE rename to vendor/github.com/gophercloud/gophercloud/v2/LICENSE diff --git a/vendor/github.com/gophercloud/gophercloud/v2/Makefile b/vendor/github.com/gophercloud/gophercloud/v2/Makefile new file mode 100644 index 000000000..9041e0c4f --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/Makefile @@ -0,0 +1,131 @@ +undefine GOFLAGS + +GOLANGCI_LINT_VERSION?=v2.12.1 +GOTESTSUM_VERSION?=v1.13.0 +GO_TEST?=go run gotest.tools/gotestsum@$(GOTESTSUM_VERSION) --format testname -- +TIMEOUT := "60m" + +ifeq ($(shell command -v podman 2> /dev/null),) + RUNNER=docker +else + RUNNER=podman +endif + +# if the golangci-lint steps fails with one of the following error messages: +# +# directory prefix . does not contain main module or its selected dependencies +# +# failed to initialize build cache at /root/.cache/golangci-lint: mkdir /root/.cache/golangci-lint: permission denied +# +# you probably have to fix the SELinux security context for root directory plus your cache +# +# chcon -Rt svirt_sandbox_file_t . +# chcon -Rt svirt_sandbox_file_t ~/.cache/golangci-lint +lint: + mkdir -p ~/.cache/golangci-lint/$(GOLANGCI_LINT_VERSION) + $(RUNNER) run -t --rm \ + -v $(shell pwd):/app \ + -v ~/.cache/golangci-lint/$(GOLANGCI_LINT_VERSION):/root/.cache \ + -w /app \ + -e GOFLAGS="-tags=acceptance" \ + golangci/golangci-lint:$(GOLANGCI_LINT_VERSION) golangci-lint run -v --max-same-issues 50 +.PHONY: lint + +format: + gofmt -w -s $(shell pwd) +.PHONY: format + +unit: + $(GO_TEST) -shuffle on ./... +.PHONY: unit + +coverage: + $(GO_TEST) -shuffle on -covermode count -coverprofile cover.out -coverpkg=./... ./... +.PHONY: coverage + +acceptance: acceptance-basic acceptance-baremetal acceptance-blockstorage acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-image acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-metric acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow +.PHONY: acceptance + +acceptance-basic: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack +.PHONY: acceptance-basic + +acceptance-baremetal: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/baremetal/... +.PHONY: acceptance-baremetal + +acceptance-blockstorage: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/blockstorage/... +.PHONY: acceptance-blockstorage + +acceptance-compute: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/compute/... +.PHONY: acceptance-compute + +acceptance-container: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/container/... +.PHONY: acceptance-container + +acceptance-containerinfra: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/containerinfra/... +.PHONY: acceptance-containerinfra + +acceptance-db: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/db/... +.PHONY: acceptance-db + +acceptance-dns: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/dns/... +.PHONY: acceptance-dns + +acceptance-fwaas: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/... +.PHONY: acceptance-fwaas + +acceptance-identity: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/identity/... +.PHONY: acceptance-identity + +acceptance-image: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/image/... +.PHONY: acceptance-image + +acceptance-keymanager: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/keymanager/... +.PHONY: acceptance-keymanager + +acceptance-loadbalancer: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/loadbalancer/... +.PHONY: acceptance-loadbalancer + +acceptance-messaging: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/messaging/... +.PHONY: acceptance-messaging + +acceptance-metric: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/metric/... +.PHONY: acceptance-metric + +acceptance-networking: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/... +.PHONY: acceptance-networking + +acceptance-objectstorage: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/objectstorage/... +.PHONY: acceptance-objectstorage + +acceptance-orchestration: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/orchestration/... +.PHONY: acceptance-orchestration + +acceptance-placement: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/placement/... +.PHONY: acceptance-placement + +acceptance-sharedfilesystems: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/sharedfilesystems/... +.PHONY: acceptance-sharefilesystems + +acceptance-workflow: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/workflow/... +.PHONY: acceptance-workflow diff --git a/vendor/github.com/gophercloud/gophercloud/v2/README.md b/vendor/github.com/gophercloud/gophercloud/v2/README.md new file mode 100644 index 000000000..ca47f5b0b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/README.md @@ -0,0 +1,252 @@ +# Gophercloud: an OpenStack SDK for Go +[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) + +Gophercloud is a Go SDK for OpenStack. + +Join us on kubernetes slack, on [#gophercloud](https://kubernetes.slack.com/archives/C05G4NJ6P6X). Visit [slack.k8s.io](https://slack.k8s.io) for an invitation. + +> **Note** +> This branch contains the current stable branch of Gophercloud: `v2`. +> The legacy stable version can be found in the `v1` branch. + +## How to install + +Reference a Gophercloud package in your code: + +```go +import "github.com/gophercloud/gophercloud/v2" +``` + +Then update your `go.mod`: + +```shell +go mod tidy +``` + +## Getting started + +### Credentials + +Because you'll be hitting an API, you will need to retrieve your OpenStack +credentials and either store them in a `clouds.yaml` file, as environment +variables, or in your local Go files. The first method is recommended because +it decouples credential information from source code, allowing you to push the +latter to your version control system without any security risk. + +You will need to retrieve the following: + +* A valid Keystone identity URL +* Credentials. These can be a username/password combo, a set of Application + Credentials, a pre-generated token, or any other supported authentication + mechanism. + +For users who have the OpenStack dashboard installed, there's a shortcut. If +you visit the `project/api_access` path in Horizon and click on the +"Download OpenStack RC File" button at the top right hand corner, you can +download either a `clouds.yaml` file or an `openrc` bash file that exports all +of your access details to environment variables. To use the `clouds.yaml` file, +place it at `~/.config/openstack/clouds.yaml`. To use the `openrc` file, run +`source openrc` and you will be prompted for your password. + +### Gophercloud authentication + +Gophercloud authentication is organized into two layered abstractions: +* `ProviderClient` holds the authentication token and can be used to build a + `ServiceClient`. +* `ServiceClient` specializes against one specific OpenStack module and can + directly be used to make API calls. + +A provider client is a top-level client that all of your OpenStack service +clients derive from. The provider contains all of the authentication details +that allow your Go code to access the API - such as the base URL and token ID. + +One single Provider client can be used to build as many Service clients as needed. + +**With `clouds.yaml`** + +```go +package main + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/config" + "github.com/gophercloud/gophercloud/v2/openstack/config/clouds" +) + +func main() { + ctx := context.Background() + + // Fetch coordinates from a `cloud.yaml` in the current directory, or + // in the well-known config directories (different for each operating + // system). + authOptions, endpointOptions, tlsConfig, err := clouds.Parse() + if err != nil { + panic(err) + } + + // Call Keystone to get an authentication token, and use it to + // construct a ProviderClient. All functions hitting the OpenStack API + // accept a `context.Context` to enable tracing and cancellation. + providerClient, err := config.NewProviderClient(ctx, authOptions, config.WithTLSConfig(tlsConfig)) + if err != nil { + panic(err) + } + + // Use the ProviderClient and the endpoint options fetched from + // `clouds.yaml` to build a service client: a compute client in this + // case. Note that the contructor does not accept a `context.Context`: + // no further call to the OpenStack API is needed at this stage. + computeClient, err := openstack.NewComputeV2(providerClient, endpointOptions) + if err != nil { + panic(err) + } + + // use the computeClient +} +``` + +**With environment variables (`openrc`)** + +Gophercloud can parse the environment variables set by running `source openrc`: + +```go +package main + +import ( + "context" + "os" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" +) + +func main() { + ctx := context.Background() + + opts, err := openstack.AuthOptionsFromEnv() + if err != nil { + panic(err) + } + + providerClient, err := openstack.AuthenticatedClient(ctx, opts) + if err != nil { + panic(err) + } + + computeClient, err := openstack.NewComputeV2(providerClient, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) + if err != nil { + panic(err) + } + + // use the computeClient +} +``` + +**Manually** + +You can also generate a "Provider" by passing in your credentials +explicitly: + +```go +package main + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" +) + +func main() { + ctx := context.Background() + + providerClient, err := openstack.AuthenticatedClient(ctx, gophercloud.AuthOptions{ + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", + Username: "username", + Password: "password", + }) + if err != nil { + panic(err) + } + + computeClient, err := openstack.NewComputeV2(providerClient, gophercloud.EndpointOpts{ + Region: "RegionName", + }) + if err != nil { + panic(err) + } + + // use the computeClient +} +``` + +### Provision a server + +We can use the Compute service client generated above for any Compute API +operation we want. In our case, we want to provision a new server. To do this, +we invoke the `Create` method and pass in the flavor ID (hardware +specification) and image ID (operating system) we're interested in: + +```go +import "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + +func main() { + // [...] + + server, err := servers.Create(context.TODO(), computeClient, servers.CreateOpts{ + Name: "My new server!", + FlavorRef: "flavor_id", + ImageRef: "image_id", + }).Extract() + + // [...] +``` + +The above code sample creates a new server with the parameters, and returns a +[`servers.Server`](https://pkg.go.dev/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers#Server). + +## Supported Services + +| **Service** | **Name** | **Module** | **1.x** | **2.x** | +|:------------------------:|------------------|:----------------------------------:|:-------:|:-------:| +| Baremetal | Ironic | `openstack/baremetal` | ✔ | ✔ | +| Baremetal Introspection | Ironic Inspector | `openstack/baremetalintrospection` | ✔ | ✔ | +| Block Storage | Cinder | `openstack/blockstorage` | ✔ | ✔ | +| Clustering | Senlin | `openstack/clustering` | ✔ | ✘ | +| Compute | Nova | `openstack/compute` | ✔ | ✔ | +| Container | Zun | `openstack/container` | ✔ | ✔ | +| Container Infrastructure | Magnum | `openstack/containerinfra` | ✔ | ✔ | +| Database | Trove | `openstack/db` | ✔ | ✔ | +| DNS | Designate | `openstack/dns` | ✔ | ✔ | +| Identity | Keystone | `openstack/identity` | ✔ | ✔ | +| Image | Glance | `openstack/image` | ✔ | ✔ | +| Key Management | Barbican | `openstack/keymanager` | ✔ | ✔ | +| Load Balancing | Octavia | `openstack/loadbalancer` | ✔ | ✔ | +| Messaging | Zaqar | `openstack/messaging` | ✔ | ✔ | +| Networking | Neutron | `openstack/networking` | ✔ | ✔ | +| Object Storage | Swift | `openstack/objectstorage` | ✔ | ✔ | + +## Advanced Usage + +Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. + +## Backwards-Compatibility Guarantees + +Gophercloud versioning follows [semver](https://semver.org/spec/v2.0.0.html). + +Before `v1.0.0`, there were no guarantees. Starting with v1, there will be no breaking changes within a major release. + +See the [Release instructions](./RELEASE.md). + +## Contributing + +See the [contributing guide](./.github/CONTRIBUTING.md). + +## Help and feedback + +If you're struggling with something or have spotted a potential bug, feel free +to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). diff --git a/vendor/github.com/gophercloud/gophercloud/RELEASE.md b/vendor/github.com/gophercloud/gophercloud/v2/RELEASE.md similarity index 100% rename from vendor/github.com/gophercloud/gophercloud/RELEASE.md rename to vendor/github.com/gophercloud/gophercloud/v2/RELEASE.md diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/v2/auth_options.go similarity index 90% rename from vendor/github.com/gophercloud/gophercloud/auth_options.go rename to vendor/github.com/gophercloud/gophercloud/v2/auth_options.go index 335ce8795..616919d00 100644 --- a/vendor/github.com/gophercloud/gophercloud/auth_options.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/auth_options.go @@ -19,13 +19,13 @@ An example of manually providing authentication information: TenantID: "{tenant_id}", } - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) An example of using AuthOptionsFromEnv(), where the environment variables can be read from a file, such as a standard openrc file: opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) */ type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with @@ -102,17 +102,18 @@ type AuthScope struct { DomainID string DomainName string System bool + TrustID string } // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v2 tokens package -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]any, error) { // Populate the request map. - authMap := make(map[string]interface{}) + authMap := make(map[string]any) if opts.Username != "" { if opts.Password != "" { - authMap["passwordCredentials"] = map[string]interface{}{ + authMap["passwordCredentials"] = map[string]any{ "username": opts.Username, "password": opts.Password, } @@ -120,7 +121,7 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return nil, ErrMissingInput{Argument: "Password"} } } else if opts.TokenID != "" { - authMap["token"] = map[string]interface{}{ + authMap["token"] = map[string]any{ "id": opts.TokenID, } } else { @@ -134,23 +135,17 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { authMap["tenantName"] = opts.TenantName } - return map[string]interface{}{"auth": authMap}, nil + return map[string]any{"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]any) (map[string]any, error) { type domainReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` } - type projectReq struct { - Domain *domainReq `json:"domain,omitempty"` - Name *string `json:"name,omitempty"` - ID *string `json:"id,omitempty"` - } - type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` @@ -397,7 +392,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } if len(scope) != 0 { - b["auth"].(map[string]interface{})["scope"] = scope + b["auth"].(map[string]any)["scope"] = scope } return b, nil @@ -405,7 +400,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // 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]any, error) { // For backwards compatibility. // If AuthOptions.Scope was not set, try to determine it. // This works well for common scenarios. @@ -423,13 +418,21 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } if opts.Scope.System { - return map[string]interface{}{ - "system": map[string]interface{}{ + return map[string]any{ + "system": map[string]any{ "all": true, }, }, nil } + if opts.Scope.TrustID != "" { + return map[string]any{ + "OS-TRUST:trust": map[string]string{ + "id": opts.Scope.TrustID, + }, + }, nil + } + if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. @@ -442,20 +445,20 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { if opts.Scope.DomainID != "" { // ProjectName + DomainID - return map[string]interface{}{ - "project": map[string]interface{}{ + return map[string]any{ + "project": map[string]any{ "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, + "domain": map[string]any{"id": &opts.Scope.DomainID}, }, }, nil } if opts.Scope.DomainName != "" { // ProjectName + DomainName - return map[string]interface{}{ - "project": map[string]interface{}{ + return map[string]any{ + "project": map[string]any{ "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, + "domain": map[string]any{"name": &opts.Scope.DomainName}, }, }, nil } @@ -469,8 +472,8 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } // ProjectID - return map[string]interface{}{ - "project": map[string]interface{}{ + return map[string]any{ + "project": map[string]any{ "id": &opts.Scope.ProjectID, }, }, nil @@ -481,15 +484,15 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } // DomainID - return map[string]interface{}{ - "domain": map[string]interface{}{ + return map[string]any{ + "domain": map[string]any{ "id": &opts.Scope.DomainID, }, }, nil } else if opts.Scope.DomainName != "" { // DomainName - return map[string]interface{}{ - "domain": map[string]interface{}{ + return map[string]any{ + "domain": map[string]any{ "name": &opts.Scope.DomainName, }, }, nil @@ -509,6 +512,6 @@ func (opts AuthOptions) CanReauth() bool { // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. -func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { return nil, nil } diff --git a/vendor/github.com/gophercloud/gophercloud/auth_result.go b/vendor/github.com/gophercloud/gophercloud/v2/auth_result.go similarity index 87% rename from vendor/github.com/gophercloud/gophercloud/auth_result.go rename to vendor/github.com/gophercloud/gophercloud/v2/auth_result.go index 2e4699b97..9a49cce8e 100644 --- a/vendor/github.com/gophercloud/gophercloud/auth_result.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/auth_result.go @@ -12,9 +12,9 @@ The following types satisfy this interface: Usage example: import ( - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" ) func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) { diff --git a/vendor/github.com/gophercloud/gophercloud/endpoint_search.go b/vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go similarity index 51% rename from vendor/github.com/gophercloud/gophercloud/endpoint_search.go rename to vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go index 2fbc3c97f..34d76a1b8 100644 --- a/vendor/github.com/gophercloud/gophercloud/endpoint_search.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go @@ -1,5 +1,7 @@ package gophercloud +import "slices" + // Availability indicates to whom a specific service endpoint is accessible: // the internet at large, internal networks only, or only to administrators. // Different identity services use different terminology for these. Identity v2 @@ -22,6 +24,31 @@ const ( AvailabilityInternal Availability = "internal" ) +// ServiceTypeAliases contains a mapping of service types to any aliases, as +// defined by the OpenStack Service Types Authority. Only service types that +// we support are included. +var ServiceTypeAliases = map[string][]string{ + "application-container": {"container"}, + "baremetal": {"bare-metal"}, + "baremetal-introspection": {}, + "block-storage": {"block-store", "volume", "volumev2", "volumev3"}, + "compute": {}, + "container-infrastructure-management": {"container-infrastructure", "container-infra"}, + "database": {}, + "dns": {}, + "identity": {}, + "image": {}, + "key-manager": {}, + "load-balancer": {}, + "message": {"messaging"}, + "networking": {}, + "object-store": {}, + "orchestration": {}, + "placement": {}, + "shared-file-system": {"sharev2", "share"}, + "workflow": {"workflowv2"}, +} + // EndpointOpts specifies search criteria used by queries against an // OpenStack service catalog. The options must contain enough information to // unambiguously identify one, and only one, endpoint within the catalog. @@ -30,8 +57,9 @@ const ( // package, like "openstack.NewComputeV2()". type EndpointOpts struct { // Type [required] is the service type for the client (e.g., "compute", - // "object-store"). Generally, this will be supplied by the service client - // function, but a user-given value will be honored if provided. + // "object-store"), as defined by the OpenStack Service Types Authority. + // This will generally be supplied by the service client function, but a + // user-given value will be honored if provided. Type string // Name [optional] is the service name for the client (e.g., "nova") as it @@ -39,11 +67,23 @@ type EndpointOpts struct { // different Name, which is why both Type and Name are sometimes needed. Name string + // Aliases [optional] is the set of aliases of the service type (e.g. + // "volumev2"/"volumev3", "volume" and "block-store" for the + // "block-storage" service type), as defined by the OpenStack Service Types + // Authority. As with Type, this will generally be supplied by the service + // client function, but a user-given value will be honored if provided. + Aliases []string + // Region [required] is the geographic region in which the endpoint resides, // generally specifying which datacenter should house your resources. // Required only for services that span multiple regions. Region string + // Version [optional] is the major version of the service required. It it not + // a microversion. Use this to ensure the correct endpoint is selected when + // multiple API versions are available. + Version int + // Availability [optional] is the visibility of the endpoint to be returned. // Valid types include the constants AvailabilityPublic, AvailabilityInternal, // or AvailabilityAdmin from this package. @@ -73,4 +113,26 @@ func (eo *EndpointOpts) ApplyDefaults(t string) { if eo.Availability == "" { eo.Availability = AvailabilityPublic } + if len(eo.Aliases) == 0 { + if aliases, ok := ServiceTypeAliases[eo.Type]; ok { + // happy path: user requested a service type by its official name + eo.Aliases = slices.Clone(aliases) + } else { + // unhappy path: user requested a service type by its alias or an + // invalid/unsupported service type + // TODO(stephenfin): This should probably be an error in v3 + for t, aliases := range ServiceTypeAliases { + if slices.Contains(aliases, eo.Type) { + // we intentionally override the service type, even if it + // was explicitly requested by the user + eo.Type = t + eo.Aliases = slices.Clone(aliases) + } + } + } + } +} + +func (eo *EndpointOpts) Types() []string { + return append([]string{eo.Type}, eo.Aliases...) } diff --git a/vendor/github.com/gophercloud/gophercloud/errors.go b/vendor/github.com/gophercloud/gophercloud/v2/errors.go similarity index 61% rename from vendor/github.com/gophercloud/gophercloud/errors.go rename to vendor/github.com/gophercloud/gophercloud/v2/errors.go index 8ab592ca4..2698c2039 100644 --- a/vendor/github.com/gophercloud/gophercloud/errors.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/errors.go @@ -1,6 +1,8 @@ package gophercloud import ( + "bytes" + "errors" "fmt" "net/http" "strings" @@ -39,7 +41,7 @@ func (e ErrMissingInput) Error() string { // ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. type ErrInvalidInput struct { ErrMissingInput - Value interface{} + Value any } func (e ErrInvalidInput) Error() string { @@ -88,8 +90,8 @@ type ErrUnexpectedResponseCode struct { func (e ErrUnexpectedResponseCode) Error() string { e.DefaultErrString = fmt.Sprintf( - "Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", - e.Expected, e.Method, e.URL, e.Actual, e.Body, + "Expected HTTP response code %v when accessing [%s %s], but got %d instead: %s", + e.Expected, e.Method, e.URL, e.Actual, bytes.TrimSpace(e.Body), ) return e.choseErrString() } @@ -99,244 +101,22 @@ 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. +// ResponseCodeIs returns true if this error is or contains an ErrUnexpectedResponseCode reporting +// that the request failed with the given response code. For example, this checks if a request +// failed because of a 404 error: // -// 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. -type ErrDefault400 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault400) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault401 is the default error type returned on a 401 HTTP response code. -type ErrDefault401 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault401) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault403 is the default error type returned on a 403 HTTP response code. -type ErrDefault403 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault403) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault404 is the default error type returned on a 404 HTTP response code. -type ErrDefault404 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault404) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault405 is the default error type returned on a 405 HTTP response code. -type ErrDefault405 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault405) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault408 is the default error type returned on a 408 HTTP response code. -type ErrDefault408 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault408) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault409 is the default error type returned on a 409 HTTP response code. -type ErrDefault409 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault409) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault429 is the default error type returned on a 429 HTTP response code. -type ErrDefault429 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault429) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault500 is the default error type returned on a 500 HTTP response code. -type ErrDefault500 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault500) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault502 is the default error type returned on a 502 HTTP response code. -type ErrDefault502 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault502) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault503 is the default error type returned on a 503 HTTP response code. -type ErrDefault503 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault503) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault504 is the default error type returned on a 504 HTTP response code. -type ErrDefault504 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault504) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -func (e ErrDefault400) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Bad request with: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault401) Error() string { - return "Authentication failed" -} -func (e ErrDefault403) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Request forbidden: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault404) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Resource not found: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault405) Error() string { - return "Method not allowed" -} -func (e ErrDefault408) Error() string { - return "The server timed out waiting for the request" -} -func (e ErrDefault429) Error() string { - return "Too many requests have been sent in a given amount of time. Pause" + - " requests, wait up to one minute, and try again." -} -func (e ErrDefault500) Error() string { - return "Internal Server Error" -} -func (e ErrDefault502) Error() string { - return "Bad Gateway" -} -func (e ErrDefault503) Error() string { - return "The service is currently unable to handle the request due to a temporary" + - " overloading or maintenance. This is a temporary condition. Try again later." -} -func (e ErrDefault504) Error() string { - return "Gateway Timeout" -} - -// Err400er is the interface resource error types implement to override the error message -// from a 400 error. -type Err400er interface { - Error400(ErrUnexpectedResponseCode) error -} - -// Err401er is the interface resource error types implement to override the error message -// from a 401 error. -type Err401er interface { - Error401(ErrUnexpectedResponseCode) error -} - -// Err403er is the interface resource error types implement to override the error message -// from a 403 error. -type Err403er interface { - Error403(ErrUnexpectedResponseCode) error -} - -// Err404er is the interface resource error types implement to override the error message -// from a 404 error. -type Err404er interface { - Error404(ErrUnexpectedResponseCode) error -} - -// Err405er is the interface resource error types implement to override the error message -// from a 405 error. -type Err405er interface { - Error405(ErrUnexpectedResponseCode) error -} - -// Err408er is the interface resource error types implement to override the error message -// from a 408 error. -type Err408er interface { - Error408(ErrUnexpectedResponseCode) error -} - -// Err409er is the interface resource error types implement to override the error message -// from a 409 error. -type Err409er interface { - Error409(ErrUnexpectedResponseCode) error -} - -// Err429er is the interface resource error types implement to override the error message -// from a 429 error. -type Err429er interface { - Error429(ErrUnexpectedResponseCode) error -} - -// Err500er is the interface resource error types implement to override the error message -// from a 500 error. -type Err500er interface { - Error500(ErrUnexpectedResponseCode) error -} - -// Err502er is the interface resource error types implement to override the error message -// from a 502 error. -type Err502er interface { - Error502(ErrUnexpectedResponseCode) error -} - -// Err503er is the interface resource error types implement to override the error message -// from a 503 error. -type Err503er interface { - Error503(ErrUnexpectedResponseCode) error -} - -// Err504er is the interface resource error types implement to override the error message -// from a 504 error. -type Err504er interface { - Error504(ErrUnexpectedResponseCode) error +// allServers, err := servers.List(client, servers.ListOpts{}) +// if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { +// handleNotFound() +// } +// +// It is safe to pass a nil error, in which case this function always returns false. +func ResponseCodeIs(err error, status int) bool { + var codeError ErrUnexpectedResponseCode + if errors.As(err, &codeError) { + return codeError.Actual == status + } + return false } // ErrTimeOut is the error type returned when an operations times out. diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go similarity index 94% rename from vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go index 7c6d06f0c..9ecc5b4ef 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go @@ -3,7 +3,7 @@ package openstack import ( "os" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var nilOptions = gophercloud.AuthOptions{} @@ -24,14 +24,14 @@ OS_PROJECT_NAME and the latter are expected against a v3 auth api. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred as "tenant" in Gophercloud. -If OS_PROJECT_NAME is set, it requires OS_PROJECT_ID to be set as well to -handle projects not on the default domain. +If OS_PROJECT_NAME is set, it requires OS_DOMAIN_ID or OS_DOMAIN_NAME to be +set as well to handle projects not on the default domain. To use this function, first set the OS_* environment variables (for example, by sourcing an `openrc` file), then: opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) */ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { authURL := os.Getenv("OS_AUTH_URL") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/client.go similarity index 63% rename from vendor/github.com/gophercloud/gophercloud/openstack/client.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/client.go index 81c907c35..cd95e599e 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/client.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/client.go @@ -1,16 +1,18 @@ package openstack import ( + "context" + "errors" "fmt" "reflect" "strings" - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) const ( @@ -23,20 +25,18 @@ const ( v3 = "v3" ) -/* -NewClient prepares an unauthenticated ProviderClient instance. -Most users will probably prefer using the AuthenticatedClient function -instead. - -This is useful if you wish to explicitly control the version of the identity -service that's used for authentication explicitly, for example. - -A basic example of using this would be: - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.NewClient(ao.IdentityEndpoint) - client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) -*/ +// NewClient prepares an unauthenticated ProviderClient instance. +// Most users will probably prefer using the AuthenticatedClient function +// instead. +// +// This is useful if you wish to explicitly control the version of the identity +// service that's used for authentication explicitly, for example. +// +// A basic example of using this would be: +// +// ao, err := openstack.AuthOptionsFromEnv() +// provider, err := openstack.NewClient(ao.IdentityEndpoint) +// client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { base, err := utils.BaseEndpoint(endpoint) if err != nil { @@ -54,69 +54,73 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { return p, nil } -/* -AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint -specified by the options, acquires a token, and returns a Provider Client -instance that's ready to operate. - -If the full path to a versioned identity endpoint was specified (example: -http://example.com:5000/v3), that path will be used as the endpoint to query. - -If a versionless endpoint was specified (example: http://example.com:5000/), -the endpoint will be queried to determine which versions of the identity service -are available, then chooses the most recent or most supported version. - -Example: - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) -*/ -func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { +// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint +// specified by the options, acquires a token, and returns a Provider Client +// instance that's ready to operate. +// +// If the full path to a versioned identity endpoint was specified (example: +// http://example.com:5000/v3), that path will be used as the endpoint to query. +// +// If a versionless endpoint was specified (example: http://example.com:5000/), +// the endpoint will be queried to determine which versions of the identity service +// are available, then chooses the most recent or most supported version. +// +// Example: +// +// ao, err := openstack.AuthOptionsFromEnv() +// provider, err := openstack.AuthenticatedClient(ctx, ao) +// client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ +// Region: os.Getenv("OS_REGION_NAME"), +// }) +func AuthenticatedClient(ctx context.Context, options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { client, err := NewClient(options.IdentityEndpoint) if err != nil { return nil, err } - err = Authenticate(client, options) + err = Authenticate(ctx, client, options) if err != nil { return nil, err } return client, nil } -// Authenticate or re-authenticate against the most recent identity service -// supported at the provided endpoint. -func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { +// Authenticate authenticates or re-authenticates against the most +// recent identity service supported at the provided endpoint. +func Authenticate(ctx context.Context, client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { versions := []*utils.Version{ {ID: v2, Priority: 20, Suffix: "/v2.0/"}, {ID: v3, Priority: 30, Suffix: "/v3/"}, } - chosen, endpoint, err := utils.ChooseVersion(client, versions) + chosen, endpoint, err := utils.ChooseVersion(ctx, client, versions) if err != nil { return err } switch chosen.ID { case v2: - return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) + return v2auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) case v3: - return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) + return v3auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) default: // The switch statement must be out of date from the versions list. - return fmt.Errorf("Unrecognized identity version: %s", chosen.ID) + return fmt.Errorf("unrecognized identity version: %s", chosen.ID) } } // AuthenticateV2 explicitly authenticates against the identity v2 endpoint. -func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { - return v2auth(client, "", options, eo) +func AuthenticateV2(ctx context.Context, client *gophercloud.ProviderClient, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { + return v2auth(ctx, client, "", options, eo) } -func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { +type v2TokenNoReauth struct { + tokens2.AuthOptionsBuilder +} + +func (v2TokenNoReauth) CanReauth() bool { return false } + +func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { v2Client, err := NewIdentityV2(client, eo) if err != nil { return err @@ -126,17 +130,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc v2Client.Endpoint = endpoint } - v2Opts := tokens2.AuthOptions{ - IdentityEndpoint: options.IdentityEndpoint, - Username: options.Username, - Password: options.Password, - TenantID: options.TenantID, - TenantName: options.TenantName, - AllowReauth: options.AllowReauth, - TokenID: options.TokenID, - } - - result := tokens2.Create(v2Client, v2Opts) + result := tokens2.Create(ctx, v2Client, options) err = client.SetTokenAndAuthResult(result) if err != nil { @@ -148,18 +142,19 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc return err } - if options.AllowReauth { + if options.CanReauth() { // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client tac.SetThrowaway(true) tac.ReauthFunc = nil - tac.SetTokenAndAuthResult(nil) - tao := options - tao.AllowReauth = false - client.ReauthFunc = func() error { - err := v2auth(&tac, endpoint, tao, eo) + err := tac.SetTokenAndAuthResult(nil) + if err != nil { + return err + } + client.ReauthFunc = func(ctx context.Context) error { + err := v2auth(ctx, &tac, endpoint, &v2TokenNoReauth{options}, eo) if err != nil { return err } @@ -168,18 +163,18 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { - return V2EndpointURL(catalog, opts) + return V2Endpoint(context.TODO(), client, catalog, opts) } return nil } // AuthenticateV3 explicitly authenticates against the identity v3 service. -func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - return v3auth(client, "", options, eo) +func AuthenticateV3(ctx context.Context, client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { + return v3auth(ctx, client, "", options, eo) } -func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { +func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { // Override the generated service endpoint with the one returned by the version endpoint. v3Client, err := NewIdentityV3(client, eo) if err != nil { @@ -211,7 +206,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au } v3Client.SetToken(tokenID) - result := tokens3.Get(v3Client, tokenID) + result := tokens3.Get(ctx, v3Client, tokenID) if result.Err != nil { return result.Err } @@ -229,11 +224,11 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au var result tokens3.CreateResult switch opts.(type) { case *ec2tokens.AuthOptions: - result = ec2tokens.Create(v3Client, opts) + result = ec2tokens.Create(ctx, v3Client, opts) case *oauth1.AuthOptions: - result = oauth1.Create(v3Client, opts) + result = oauth1.Create(ctx, v3Client, opts) default: - result = tokens3.Create(v3Client, opts) + result = tokens3.Create(ctx, v3Client, opts) } err = client.SetTokenAndAuthResult(result) @@ -254,7 +249,10 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au tac := *client tac.SetThrowaway(true) tac.ReauthFunc = nil - tac.SetTokenAndAuthResult(nil) + err = tac.SetTokenAndAuthResult(nil) + if err != nil { + return err + } var tao tokens3.AuthOptionsBuilder switch ot := opts.(type) { case *gophercloud.AuthOptions: @@ -276,8 +274,8 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au default: tao = opts } - client.ReauthFunc = func() error { - err := v3auth(&tac, endpoint, tao, eo) + client.ReauthFunc = func(ctx context.Context) error { + err := v3auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err } @@ -286,7 +284,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { - return V3EndpointURL(catalog, opts) + return V3Endpoint(context.TODO(), client, catalog, opts) } return nil @@ -347,13 +345,21 @@ func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp }, nil } -func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { +// TODO(stephenfin): Allow passing aliases to all New${SERVICE}V${VERSION} methods in v3 +func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string, version int) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) + eo.ApplyDefaults(clientType) + if eo.Version != 0 && eo.Version != version { + return sc, errors.New("conflict between requested service major version and manually set version") + } + eo.Version = version + url, err := client.EndpointLocator(eo) if err != nil { return sc, err } + sc.ProviderClient = client sc.Endpoint = url sc.Type = clientType @@ -363,86 +369,85 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO // 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") + sc, err := initClientOpts(client, eo, "baremetal", 1) + if !strings.HasSuffix(strings.TrimSuffix(sc.Endpoint, "/"), "v1") { + sc.ResourceBase = sc.Endpoint + "v1/" + } + return sc, err } // 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-introspection") + return initClientOpts(client, eo, "baremetal-introspection", 1) } // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 // object storage package. func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "object-store") + return initClientOpts(client, eo, "object-store", 1) } // NewComputeV2 creates a ServiceClient that may be used with the v2 compute // package. func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "compute") + return initClientOpts(client, eo, "compute", 2) } // NewNetworkV2 creates a ServiceClient that may be used with the v2 network // package. func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "network") + sc, err := initClientOpts(client, eo, "network", 2) sc.ResourceBase = sc.Endpoint + "v2.0/" return sc, err } +// TODO(stephenfin): Remove this in v3. We no longer support the V1 Block Storage service. // NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 // block storage service. func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volume") + return initClientOpts(client, eo, "volume", 1) } // NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 // block storage service. func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volumev2") + return initClientOpts(client, eo, "block-storage", 2) } // NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service. func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volumev3") + return initClientOpts(client, eo, "block-storage", 3) } // NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "sharev2") -} - -// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1 -// CDN service. -func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "cdn") + return initClientOpts(client, eo, "shared-file-system", 2) } // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 // orchestration service. func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "orchestration") + return initClientOpts(client, eo, "orchestration", 1) } // NewDBV1 creates a ServiceClient that may be used to access the v1 DB service. func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "database") + return initClientOpts(client, eo, "database", 1) } // NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS // service. func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "dns") + sc, err := initClientOpts(client, eo, "dns", 2) sc.ResourceBase = sc.Endpoint + "v2/" return sc, err } -// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 -// image service. -func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "image") +// NewImageV2 creates a ServiceClient that may be used to access the v2 image +// service. +func NewImageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "image", 2) sc.ResourceBase = sc.Endpoint + "v2/" return sc, err } @@ -450,38 +455,40 @@ func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi // NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2 // load balancer service. func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "load-balancer") + sc, err := initClientOpts(client, eo, "load-balancer", 2) // Fixes edge case having an OpenStack lb endpoint with trailing version number. - endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1) + endpoint := strings.ReplaceAll(sc.Endpoint, "v2.0/", "") sc.ResourceBase = endpoint + "v2.0/" return sc, err } -// NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering -// package. -func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "clustering") +// NewMetricV1 creates a ServiceClient that may be used with the v1 metric-storage +// service (Aetos). +func NewMetricV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "metric-storage", 1) + sc.ResourceBase = sc.Endpoint + "api/v1/" + return sc, err } // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging // service. func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "messaging") + sc, err := initClientOpts(client, eo, "message", 2) sc.MoreHeaders = map[string]string{"Client-ID": clientID} return sc, err } // NewContainerV1 creates a ServiceClient that may be used with v1 container package func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container") + return initClientOpts(client, eo, "application-container", 1) } // NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key // manager service. func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "key-manager") + sc, err := initClientOpts(client, eo, "key-manager", 1) sc.ResourceBase = sc.Endpoint + "v1/" return sc, err } @@ -489,15 +496,15 @@ func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoint // NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management // package. func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container-infra") + return initClientOpts(client, eo, "container-infrastructure-management", 1) } // NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package. func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "workflowv2") + return initClientOpts(client, eo, "workflow", 2) } // 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") + return initClientOpts(client, eo, "placement", 1) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/doc.go similarity index 75% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/doc.go index 747966d8d..28a5870c2 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/doc.go @@ -12,7 +12,7 @@ Example to List Flavors AccessType: flavors.PublicAccess, } - allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages() + allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to Create a Flavor RxTxFactor: 1.0, } - flavor, err := flavors.Create(computeClient, createOpts).Extract() + flavor, err := flavors.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Flavor Description: "This is a good description" } - flavor, err := flavors.Update(computeClient, flavorID, updateOpts).Extract() + flavor, err := flavors.Update(context.TODO(), computeClient, flavorID, updateOpts).Extract() if err != nil { panic(err) } @@ -59,7 +59,7 @@ Example to List Flavor Access flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages() + allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages(context.TODO()) if err != nil { panic(err) } @@ -81,7 +81,7 @@ Example to Grant Access to a Flavor Tenant: "15153a0979884b59b0592248ef947921", } - accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract() + accessList, err := flavors.AddAccess(context.TODO(), computeClient, flavor.ID, accessOpts).Extract() if err != nil { panic(err) } @@ -94,7 +94,7 @@ Example to Remove/Revoke Access to a Flavor Tenant: "15153a0979884b59b0592248ef947921", } - accessList, err := flavors.RemoveAccess(computeClient, flavor.ID, accessOpts).Extract() + accessList, err := flavors.RemoveAccess(context.TODO(), computeClient, flavor.ID, accessOpts).Extract() if err != nil { panic(err) } @@ -107,7 +107,7 @@ Example to Create Extra Specs for a Flavor "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } - createdExtraSpecs, err := flavors.CreateExtraSpecs(computeClient, flavorID, createOpts).Extract() + createdExtraSpecs, err := flavors.CreateExtraSpecs(context.TODO(), computeClient, flavorID, createOpts).Extract() if err != nil { panic(err) } @@ -118,7 +118,7 @@ Example to Get Extra Specs for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract() + extraSpecs, err := flavors.ListExtraSpecs(context.TODO(), computeClient, flavorID).Extract() if err != nil { panic(err) } @@ -132,7 +132,7 @@ Example to Update Extra Specs for a Flavor updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_thread_policy": "CPU-THREAD-POLICY-UPDATED", } - updatedExtraSpec, err := flavors.UpdateExtraSpec(computeClient, flavorID, updateOpts).Extract() + updatedExtraSpec, err := flavors.UpdateExtraSpec(context.TODO(), computeClient, flavorID, updateOpts).Extract() if err != nil { panic(err) } @@ -142,7 +142,7 @@ Example to Update Extra Specs for a Flavor Example to Delete an Extra Spec for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - err := flavors.DeleteExtraSpec(computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() + err := flavors.DeleteExtraSpec(context.TODO(), computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/requests.go similarity index 75% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/requests.go index 3887cdfdc..119f5e78e 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/requests.go @@ -1,8 +1,10 @@ package flavors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -97,7 +99,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } type CreateOptsBuilder interface { - ToFlavorCreateMap() (map[string]interface{}, error) + ToFlavorCreateMap() (map[string]any, error) } // CreateOpts specifies parameters used for creating a flavor. @@ -136,18 +138,18 @@ type CreateOpts struct { } // ToFlavorCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFlavorCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "flavor") } // Create requests the creation of a new flavor. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFlavorCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -155,7 +157,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } type UpdateOptsBuilder interface { - ToFlavorUpdateMap() (map[string]interface{}, error) + ToFlavorUpdateMap() (map[string]any, error) } // UpdateOpts specifies parameters used for updating a flavor. @@ -167,18 +169,18 @@ type UpdateOpts struct { } // ToFlavorUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "flavor") } // Update requests the update of a new flavor. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFlavorUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -187,15 +189,15 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder // Get retrieves details of a single flavor. Use Extract to convert its // result into a Flavor. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified flavor ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -212,7 +214,7 @@ func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess requests. type AddAccessOptsBuilder interface { - ToFlavorAddAccessMap() (map[string]interface{}, error) + ToFlavorAddAccessMap() (map[string]any, error) } // AddAccessOpts represents options for adding access to a flavor. @@ -222,18 +224,18 @@ type AddAccessOpts struct { } // ToFlavorAddAccessMap constructs a request body from AddAccessOpts. -func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) { +func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "addTenantAccess") } // AddAccess grants a tenant/project access to a flavor. -func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { +func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToFlavorAddAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -243,7 +245,7 @@ func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsB // RemoveAccessOptsBuilder allows extensions to add additional parameters to the // RemoveAccess requests. type RemoveAccessOptsBuilder interface { - ToFlavorRemoveAccessMap() (map[string]interface{}, error) + ToFlavorRemoveAccessMap() (map[string]any, error) } // RemoveAccessOpts represents options for removing access to a flavor. @@ -253,18 +255,18 @@ type RemoveAccessOpts struct { } // ToFlavorRemoveAccessMap constructs a request body from RemoveAccessOpts. -func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, error) { +func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "removeTenantAccess") } // RemoveAccess removes/revokes a tenant/project access to a flavor. -func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { +func RemoveAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToFlavorRemoveAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -272,14 +274,14 @@ func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAcces } // ExtraSpecs requests all the extra-specs for the given flavor ID. -func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { - resp, err := client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) +func ListExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { + resp, err := client.Get(ctx, extraSpecsListURL(client, flavorID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { - resp, err := client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) +func GetExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { + resp, err := client.Get(ctx, extraSpecsGetURL(client, flavorID, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -287,7 +289,7 @@ func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string // CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the // CreateExtraSpecs requests. type CreateExtraSpecsOptsBuilder interface { - ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) + ToFlavorExtraSpecsCreateMap() (map[string]any, error) } // ExtraSpecsOpts is a map that contains key-value pairs. @@ -295,19 +297,19 @@ type ExtraSpecsOpts map[string]string // ToFlavorExtraSpecsCreateMap assembles a body for a Create request based on // the contents of ExtraSpecsOpts. -func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"extra_specs": opts}, nil +func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]any, error) { + return map[string]any{"extra_specs": opts}, nil } // CreateExtraSpecs will create or update the extra-specs key-value pairs for // the specified Flavor. -func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { +func CreateExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { b, err := opts.ToFlavorExtraSpecsCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -340,13 +342,13 @@ func (opts ExtraSpecsOpts) ToFlavorExtraSpecUpdateMap() (map[string]string, stri // UpdateExtraSpec will updates the value of the specified flavor's extra spec // for the key in opts. -func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { +func UpdateExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { b, key, err := opts.ToFlavorExtraSpecUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -355,8 +357,8 @@ func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts Up // DeleteExtraSpec will delete the key-value pair with the given key for the given // flavor ID. -func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { - resp, err := client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ +func DeleteExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(ctx, extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/results.go similarity index 94% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/results.go index 4da14118a..387268af8 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { @@ -80,13 +80,19 @@ type Flavor struct { // 65535 characters in length. Only printable characters are allowed. // New in version 2.55 Description string `json:"description"` + + // Properties is a dictionary of the flavor’s extra-specs key-and-value + // pairs. This will only be included if the user is allowed by policy to + // index flavor extra_specs + // New in version 2.61 + ExtraSpecs map[string]string `json:"extra_specs"` } func (r *Flavor) UnmarshalJSON(b []byte) error { type tmp Flavor var s struct { tmp - Swap interface{} `json:"swap"` + Swap any `json:"swap"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/urls.go similarity index 97% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/urls.go index 65bbb6540..27ca0571f 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors/urls.go @@ -1,7 +1,7 @@ package flavors import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) func getURL(client *gophercloud.ServiceClient, id string) string { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/doc.go similarity index 75% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/doc.go index 9fa914ec7..0e52dfc9a 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/doc.go @@ -4,7 +4,7 @@ servers with a specified key pair. Example to List Key Pairs - allPages, err := keypairs.List(computeClient, nil).AllPages() + allPages, err := keypairs.List(computeClient, nil).AllPages(context.TODO()) if err != nil { panic(err) } @@ -26,7 +26,7 @@ Example to List Key Pairs using microversion 2.10 or greater UserID: "user-id", } - allPages, err := keypairs.List(computeClient, listOpts).AllPages() + allPages, err := keypairs.List(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -46,7 +46,7 @@ Example to Create a Key Pair Name: "keypair-name", } - keypair, err := keypairs.Create(computeClient, createOpts).Extract() + keypair, err := keypairs.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -60,14 +60,14 @@ Example to Import a Key Pair PublicKey: "public-key", } - keypair, err := keypairs.Create(computeClient, createOpts).Extract() + keypair, err := keypairs.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Key Pair - err := keypairs.Delete(computeClient, "keypair-name", nil).ExtractErr() + err := keypairs.Delete(context.TODO(), computeClient, "keypair-name", nil).ExtractErr() if err != nil { panic(err) } @@ -80,7 +80,7 @@ Example to Delete a Key Pair owned by a certain user using microversion 2.10 or UserID: "user-id", } - err := keypairs.Delete(client, "keypair-name", deleteOpts).ExtractErr() + err := keypairs.Delete(context.TODO(), client, "keypair-name", deleteOpts).ExtractErr() if err != nil { panic(err) } @@ -98,7 +98,7 @@ Example to Create a Server With a Key Pair KeyName: "keypair-name", } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -111,7 +111,7 @@ Example to Get a Key Pair owned by a certain user using microversion 2.10 or gre UserID: "user-id", } - keypair, err := keypairs.Get(computeClient, "keypair-name", getOpts).Extract() + keypair, err := keypairs.Get(context.TODO(), computeClient, "keypair-name", getOpts).Extract() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/requests.go similarity index 83% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/requests.go index aba11fb43..bc4cc270f 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/requests.go @@ -1,9 +1,11 @@ package keypairs import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/pagination" + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsExt adds a KeyPair option to the base CreateOpts. @@ -15,7 +17,7 @@ type CreateOptsExt struct { } // ToServerCreateMap adds the key_name to the base server creation options. -func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToServerCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { return nil, err @@ -25,7 +27,7 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { return base, nil } - serverMap := base["server"].(map[string]interface{}) + serverMap := base["server"].(map[string]any) serverMap["key_name"] = opts.KeyName return base, nil @@ -68,7 +70,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToKeyPairCreateMap() (map[string]interface{}, error) + ToKeyPairCreateMap() (map[string]any, error) } // CreateOpts specifies KeyPair creation or import parameters. @@ -91,19 +93,19 @@ type CreateOpts struct { } // ToKeyPairCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToKeyPairCreateMap() (map[string]any, 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) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToKeyPairCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -130,7 +132,7 @@ func (opts GetOpts) ToKeyPairGetQuery() (string, error) { } // Get returns public data about a previously uploaded KeyPair. -func Get(client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r GetResult) { url := getURL(client, name) if opts != nil { query, err := opts.ToKeyPairGetQuery() @@ -141,7 +143,7 @@ func Get(client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r url += query } - resp, err := client.Get(url, &r.Body, nil) + resp, err := client.Get(ctx, url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -166,7 +168,7 @@ func (opts DeleteOpts) ToKeyPairDeleteQuery() (string, error) { } // Delete requests the deletion of a previous stored KeyPair from the server. -func Delete(client *gophercloud.ServiceClient, name string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, name string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, name) if opts != nil { query, err := opts.ToKeyPairDeleteQuery() @@ -177,7 +179,7 @@ func Delete(client *gophercloud.ServiceClient, name string, opts DeleteOptsBuild url += query } - resp, err := client.Delete(url, nil) + resp, err := client.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/results.go similarity index 96% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/results.go index 0ac05c361..0c91fee69 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/results.go @@ -1,8 +1,8 @@ package keypairs import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // KeyPair is an SSH key known to the OpenStack Cloud that is available to be diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/urls.go similarity index 91% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/urls.go index fec38f367..f5be5f5e3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs/urls.go @@ -1,6 +1,6 @@ package keypairs -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-keypairs" diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/doc.go new file mode 100644 index 000000000..72381f074 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/doc.go @@ -0,0 +1,313 @@ +/* +Package servers provides information and interaction with the server API +resource in the OpenStack Compute service. + +A server is a virtual machine instance in the compute system. In order for +one to be provisioned, a valid flavor and image are required. + +Example to List Servers + + listOpts := servers.ListOpts{ + AllTenants: true, + } + + allPages, err := servers.ListSimple(computeClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allServers, err := servers.ExtractServers(allPages) + if err != nil { + panic(err) + } + + for _, server := range allServers { + fmt.Printf("%+v\n", server) + } + +Example to List Detail Servers + + listOpts := servers.ListOpts{ + AllTenants: true, + } + + allPages, err := servers.List(computeClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allServers, err := servers.ExtractServers(allPages) + if err != nil { + panic(err) + } + + for _, server := range allServers { + fmt.Printf("%+v\n", server) + } + +Example to Create a Server + + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts, nil).Extract() + if err != nil { + panic(err) + } + +Example to Add a Server to a Server Group + + schedulerHintOpts := servers.SchedulerHintOpts{ + Group: "servergroup-uuid", + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() + if err != nil { + panic(err) + } + +Example to Place Server B on a Different Host than Server A + + schedulerHintOpts := servers.SchedulerHintOpts{ + DifferentHost: []string{ + "server-a-uuid", + } + } + + createOpts := servers.CreateOpts{ + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() + if err != nil { + panic(err) + } + +Example to Place Server B on the Same Host as Server A + + schedulerHintOpts := servers.SchedulerHintOpts{ + SameHost: []string{ + "server-a-uuid", + } + } + + createOpts := servers.CreateOpts{ + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() + if err != nil { + panic(err) + } + +# Example to Create 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 := []servers.BlockDevice{ + servers.BlockDevice{ + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "image-uuid", + }, + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + ImageRef: "image-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() + if err != nil { + panic(err) + } + +# Example to Create 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 := []servers.BlockDevice{ + servers.BlockDevice{ + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceImage, + UUID: "image-uuid", + VolumeSize: 2, + }, + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() + if err != nil { + panic(err) + } + +# Example to Create a Server From an Existing Volume + +This example will create a server with an existing volume as its root disk. + + blockDevices := []servers.BlockDevice{ + servers.BlockDevice{ + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, + UUID: "volume-uuid", + }, + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() + if err != nil { + panic(err) + } + +# Example to Create 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 := []servers.BlockDevice{ + servers.BlockDevice{ + BootIndex: 0, + DestinationType: servers.DestinationLocal, + DeleteOnTermination: true, + SourceType: servers.SourceImage, + UUID: "image-uuid", + VolumeSize: 5, + }, + servers.BlockDevice{ + BootIndex: -1, + DestinationType: servers.DestinationLocal, + DeleteOnTermination: true, + GuestFormat: "ext4", + SourceType: servers.SourceBlank, + VolumeSize: 1, + }, + servers.BlockDevice{ + BootIndex: -1, + DestinationType: servers.DestinationLocal, + DeleteOnTermination: true, + GuestFormat: "ext4", + SourceType: servers.SourceBlank, + VolumeSize: 1, + }, + } + + CreateOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + ImageRef: "image-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Server + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + err := servers.Delete(context.TODO(), computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Force Delete a Server + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + err := servers.ForceDelete(context.TODO(), computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Reboot a Server + + rebootOpts := servers.RebootOpts{ + Type: servers.SoftReboot, + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + err := servers.Reboot(context.TODO(), computeClient, serverID, rebootOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Rebuild a Server + + rebuildOpts := servers.RebuildOpts{ + Name: "new_name", + ImageID: "image-uuid", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract() + if err != nil { + panic(err) + } + +Example to Resize a Server + + resizeOpts := servers.ResizeOpts{ + FlavorRef: "flavor-uuid", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + err := servers.Resize(context.TODO(), computeClient, serverID, resizeOpts).ExtractErr() + if err != nil { + panic(err) + } + + err = servers.ConfirmResize(context.TODO(), computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Snapshot a Server + + snapshotOpts := servers.CreateImageOpts{ + Name: "snapshot_name", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + image, err := servers.CreateImage(context.TODO(), computeClient, serverID, snapshotOpts).ExtractImageID() + if err != nil { + panic(err) + } +*/ +package servers diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/errors.go similarity index 98% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/errors.go index c9f0e3c20..a6eda38f4 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/errors.go @@ -3,7 +3,7 @@ package servers import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ErrNeitherImageIDNorImageNameProvided is the error when neither the image diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/requests.go new file mode 100644 index 000000000..c0ccebfa4 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/requests.go @@ -0,0 +1,1405 @@ +package servers + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "maps" + "net" + "regexp" + "strings" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToServerListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListOpts struct { + // ChangesSince is a time/date stamp for when the server last changed status. + ChangesSince string `q:"changes-since"` + + // Image is the name of the image in URL format. + Image string `q:"image"` + + // Flavor is the name of the flavor in URL format. + Flavor string `q:"flavor"` + + // IP is a regular expression to match the IPv4 address of the server. + IP string `q:"ip"` + + // This requires the client to be set to microversion 2.5 or later, unless + // the user is an admin. + // IP is a regular expression to match the IPv6 address of the server. + IP6 string `q:"ip6"` + + // Name of the server as a string; can be queried with regular expressions. + // Realize that ?name=bob returns both bob and bobb. If you need to match bob + // only, you can use a regular expression matching the syntax of the + // underlying database server implemented for Compute. + Name string `q:"name"` + + // Status is the value of the status of the server so that you can filter on + // "ACTIVE" for example. + Status string `q:"status"` + + // Host is the name of the host as a string. + Host string `q:"host"` + + // Marker is a UUID of the server at which you want to set a marker. + Marker string `q:"marker"` + + // Limit is an integer value for the limit of values to return. + Limit int `q:"limit"` + + // AllTenants is a bool to show all tenants. + AllTenants bool `q:"all_tenants"` + + // TenantID lists servers for a particular tenant. + // Setting "AllTenants = true" is required. + TenantID string `q:"tenant_id"` + + // This requires the client to be set to microversion 2.83 or later, unless + // the user is an admin. + // UserID lists servers for a particular user. + UserID string `q:"user_id"` + + // This requires the client to be set to microversion 2.26 or later. + // Tags filters on specific server tags. All tags must be present for the server. + Tags string `q:"tags"` + + // This requires the client to be set to microversion 2.26 or later. + // TagsAny filters on specific server tags. At least one of the tags must be present for the server. + TagsAny string `q:"tags-any"` + + // This requires the client to be set to microversion 2.26 or later. + // NotTags filters on specific server tags. All tags must be absent for the server. + NotTags string `q:"not-tags"` + + // This requires the client to be set to microversion 2.26 or later. + // NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server. + NotTagsAny string `q:"not-tags-any"` + + // Display servers based on their availability zone (Admin only until microversion 2.82). + AvailabilityZone string `q:"availability_zone"` +} + +// ToServerListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToServerListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListSimple makes a request against the API to list servers accessible to you. +func ListSimple(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToServerListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ServerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// List makes a request against the API to list servers details accessible to you. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToServerListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ServerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintOptsBuilder interface { + ToSchedulerHintsMap() (map[string]any, error) +} + +// SchedulerHintOpts represents a set of scheduling hints that are passed to the +// OpenStack scheduler. +type SchedulerHintOpts 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 []any + + // TargetCell specifies a cell name where the instance will be placed. + TargetCell string `json:"target_cell,omitempty"` + + // DifferentCell specifies cells names where an instance should not be placed. + DifferentCell []string `json:"different_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]any +} + +// ToSchedulerHintsMap assembles a request body for scheduler hints. +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]any, error) { + sh := make(map[string]any) + + 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 = "servers.schedulerhints.SchedulerHintOpts.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 = "servers.schedulerhints.SchedulerHintOpts.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 = "servers.schedulerhints.SchedulerHintOpts.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 = "servers.schedulerhints.SchedulerHintOpts.Query" + err.Value = opts.Query + err.Info = "Must be a conditional statement in the format of [op,variable,value]" + return nil, err + } + + // The query needs to be sent as a marshalled string. + b, err := json.Marshal(opts.Query) + if err != nil { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.schedulerhints.SchedulerHintOpts.Query" + err.Value = opts.Query + err.Info = "Must be a conditional statement in the format of [op,variable,value]" + return nil, err + } + + sh["query"] = string(b) + } + + if opts.TargetCell != "" { + sh["target_cell"] = opts.TargetCell + } + + if len(opts.DifferentCell) > 0 { + sh["different_cell"] = opts.DifferentCell + } + + if opts.BuildNearHostIP != "" { + if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.schedulerhints.SchedulerHintOpts.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 + } + } + + if len(sh) == 0 { + return sh, nil + } + + return map[string]any{"os:scheduler_hints": sh}, nil +} + +// Network is used within CreateOpts to control a new server's network +// attachments. +type Network struct { + // UUID of a network to attach to the newly provisioned server. + // Required unless Port is provided. + UUID string + + // Port of a neutron network to attach to the newly provisioned server. + // Required unless UUID is provided. + Port string + + // FixedIP specifies a fixed IPv4 address to be used on this network. + FixedIP string + + // Tag may contain an optional device role tag for the server's virtual + // network interface. This can be used to identify network interfaces when + // multiple networks are connected to one server. + // + // Requires microversion 2.32 through 2.36 or 2.42 or later. + Tag string +} + +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. + // Not specifying this will cause the device to be formatted to the default in Nova + // which is currently vfat. + // https://opendev.org/openstack/nova/src/commit/d0b459423dd81644e8d9382b6c87fabaa4f03ad4/nova/privsep/fs.py#L257 + 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"` + + // VolumeType is the volume type of the block device. + // This requires Compute API microversion 2.67 or later. + VolumeType string `json:"volume_type,omitempty"` + + // Tag is an arbitrary string that can be applied to a block device. + // Information about the device tags can be obtained from the metadata API + // and the config drive, allowing devices to be easily identified. + // This requires Compute API microversion 2.42 or later. + Tag string `json:"tag,omitempty"` +} + +// Personality is an array of files that are injected into the server at launch. +type Personality []*File + +// File is used within CreateOpts and RebuildOpts to inject a file into the +// server at launch. +// File implements the json.Marshaler interface, so when a Create or Rebuild +// operation is requested, json.Marshal will call File's MarshalJSON method. +type File struct { + // Path of the file. + Path string + + // Contents of the file. Maximum content size is 255 bytes. + Contents []byte +} + +// MarshalJSON marshals the escaped file, base64 encoding the contents. +func (f *File) MarshalJSON() ([]byte, error) { + file := struct { + Path string `json:"path"` + Contents string `json:"contents"` + }{ + Path: f.Path, + Contents: base64.StdEncoding.EncodeToString(f.Contents), + } + return json.Marshal(file) +} + +// DiskConfig represents one of the two possible settings for the DiskConfig +// option when creating, rebuilding, or resizing servers: Auto or Manual. +type DiskConfig string + +const ( + // Auto builds a server with a single partition the size of the target flavor + // disk and automatically adjusts the filesystem to fit the entire partition. + // Auto may only be used with images and servers that use a single EXT3 + // partition. + Auto DiskConfig = "AUTO" + + // Manual builds a server using whatever partition scheme and filesystem are + // present in the source image. If the target flavor disk is larger, the + // remaining space is left unpartitioned. This enables images to have non-EXT3 + // filesystems, multiple partitions, and so on, and enables you to manage the + // disk configuration. It also results in slightly shorter boot times. + Manual DiskConfig = "MANUAL" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToServerCreateMap() (map[string]any, error) +} + +// CreateOpts specifies server creation parameters. +type CreateOpts struct { + // Name is the name to assign to the newly launched server. + Name string `json:"name" required:"true"` + + // ImageRef is the ID or full URL to the image that contains the + // server's OS and initial state. + // Also optional if using the boot-from-volume extension. + ImageRef string `json:"imageRef"` + + // FlavorRef is the ID or full URL to the flavor that describes the server's specs. + FlavorRef string `json:"flavorRef"` + + // SecurityGroups lists the names of the security groups to which this server + // should belong. + SecurityGroups []string `json:"-"` + + // UserData contains configuration information or scripts to use upon launch. + // Create will base64-encode it for you, if it isn't already. + UserData []byte `json:"-"` + + // AvailabilityZone in which to launch the server. + AvailabilityZone string `json:"availability_zone,omitempty"` + + // Networks dictates how this server will be attached to available networks. + // By default, the server will be attached to all isolated networks for the + // tenant. + // Starting with microversion 2.37 networks can also be an "auto" or "none" + // string. + Networks any `json:"-"` + + // Metadata contains key-value pairs (up to 255 bytes each) to attach to the + // server. + Metadata map[string]string `json:"metadata,omitempty"` + + // Personality includes files to inject into the server at launch. + // Create will base64-encode file contents for you. + Personality Personality `json:"personality,omitempty"` + + // ConfigDrive enables metadata injection through a configuration drive. + ConfigDrive *bool `json:"config_drive,omitempty"` + + // AdminPass sets the root user password. If not set, a randomly-generated + // password will be created and returned in the response. + AdminPass string `json:"adminPass,omitempty"` + + // AccessIPv4 specifies an IPv4 address for the instance. + AccessIPv4 string `json:"accessIPv4,omitempty"` + + // AccessIPv6 specifies an IPv6 address for the instance. + AccessIPv6 string `json:"accessIPv6,omitempty"` + + // Min specifies Minimum number of servers to launch. + Min int `json:"min_count,omitempty"` + + // Max specifies Maximum number of servers to launch. + Max int `json:"max_count,omitempty"` + + // Tags allows a server to be tagged with single-word metadata. + // Requires microversion 2.52 or later. + Tags []string `json:"tags,omitempty"` + + // (Available from 2.90) Hostname specifies the hostname to configure for the + // instance in the metadata service. Starting with microversion 2.94, this can + // be a Fully Qualified Domain Name (FQDN) of up to 255 characters in length. + // If not set, OpenStack will derive the server's hostname from the Name field. + Hostname string `json:"hostname,omitempty"` + + // BlockDevice describes the mapping of various block devices. + BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"` + + // DiskConfig [optional] controls how the created server's disk is partitioned. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` + + // HypervisorHostname is the name of the hypervisor to which the server is scheduled. + HypervisorHostname string `json:"hypervisor_hostname,omitempty"` +} + +// ToServerCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToServerCreateMap() (map[string]any, error) { + // We intentionally don't envelope the body here since we want to strip + // some fields out and modify others + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.UserData != nil { + var userData string + if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil { + userData = base64.StdEncoding.EncodeToString(opts.UserData) + } else { + userData = string(opts.UserData) + } + b["user_data"] = &userData + } + + if len(opts.SecurityGroups) > 0 { + securityGroups := make([]map[string]any, len(opts.SecurityGroups)) + for i, groupName := range opts.SecurityGroups { + securityGroups[i] = map[string]any{"name": groupName} + } + b["security_groups"] = securityGroups + } + + switch v := opts.Networks.(type) { + case []Network: + if len(v) > 0 { + networks := make([]map[string]any, len(v)) + for i, net := range v { + networks[i] = make(map[string]any) + if net.UUID != "" { + networks[i]["uuid"] = net.UUID + } + if net.Port != "" { + networks[i]["port"] = net.Port + } + if net.FixedIP != "" { + networks[i]["fixed_ip"] = net.FixedIP + } + if net.Tag != "" { + networks[i]["tag"] = net.Tag + } + } + b["networks"] = networks + } + case string: + if v == "auto" || v == "none" { + b["networks"] = v + } else { + return nil, fmt.Errorf(`networks must be a slice of Network struct or a string with "auto" or "none" values, current value is %q`, v) + } + } + + if opts.Min != 0 { + b["min_count"] = opts.Min + } + + if opts.Max != 0 { + b["max_count"] = opts.Max + } + + // Now we do our enveloping + b = map[string]any{"server": b} + + return b, nil +} + +// Create requests a server to be provisioned to the user in the current tenant. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) { + b, err := opts.ToServerCreateMap() + if err != nil { + r.Err = err + return + } + + if hintOpts != nil { + sh, err := hintOpts.ToSchedulerHintsMap() + if err != nil { + r.Err = err + return + } + maps.Copy(b, sh) + } + + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete requests that a server previously provisioned be removed from your +// account. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete forces the deletion of a server. +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"forceDelete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get requests details on a single server, by ID. +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. +type UpdateOptsBuilder interface { + ToServerUpdateMap() (map[string]any, error) +} + +// UpdateOpts specifies the base attributes that may be updated on an existing +// server. +type UpdateOpts struct { + // Name changes the displayed name of the server. + // The server host name will *not* change. + // Server names are not constrained to be unique, even within the same tenant. + Name string `json:"name,omitempty"` + + // AccessIPv4 provides a new IPv4 address for the instance. + AccessIPv4 string `json:"accessIPv4,omitempty"` + + // AccessIPv6 provides a new IPv6 address for the instance. + AccessIPv6 string `json:"accessIPv6,omitempty"` + + // Hostname changes the hostname of the server. + // Requires microversion 2.90 or later. + // Note: This information is published via the metadata service and requires + // application such as cloud-init to propagate it through to the instance. + Hostname *string `json:"hostname,omitempty"` +} + +// ToServerUpdateMap formats an UpdateOpts structure into a request body. +func (opts UpdateOpts) ToServerUpdateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "server") +} + +// Update requests that various attributes of the indicated server be changed. +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToServerUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ChangeAdminPassword alters the administrator or root password for a specified +// server. +func ChangeAdminPassword(ctx context.Context, client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { + b := map[string]any{ + "changePassword": map[string]string{ + "adminPass": newPassword, + }, + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RebootMethod describes the mechanisms by which a server reboot can be requested. +type RebootMethod string + +// These constants determine how a server should be rebooted. +// See the Reboot() function for further details. +const ( + SoftReboot RebootMethod = "SOFT" + HardReboot RebootMethod = "HARD" + OSReboot = SoftReboot + PowerCycle = HardReboot +) + +// RebootOptsBuilder allows extensions to add additional parameters to the +// reboot request. +type RebootOptsBuilder interface { + ToServerRebootMap() (map[string]any, error) +} + +// RebootOpts provides options to the reboot request. +type RebootOpts struct { + // Type is the type of reboot to perform on the server. + Type RebootMethod `json:"type" required:"true"` +} + +// ToServerRebootMap builds a body for the reboot request. +func (opts RebootOpts) ToServerRebootMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "reboot") +} + +/* +Reboot requests that a given server reboot. + +Two methods exist for rebooting a server: + +HardReboot (aka PowerCycle) starts the server instance by physically cutting +power to the machine, or if a VM, terminating it at the hypervisor level. +It's done. Caput. Full stop. +Then, after a brief while, power is restored or the VM instance restarted. + +SoftReboot (aka OSReboot) simply tells the OS to restart under its own +procedure. +E.g., in Linux, asking it to enter runlevel 6, or executing +"sudo shutdown -r now", or by asking Windows to rtart the machine. +*/ +func Reboot(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { + b, err := opts.ToServerRebootMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RebuildOptsBuilder allows extensions to provide additional parameters to the +// rebuild request. +type RebuildOptsBuilder interface { + ToServerRebuildMap() (map[string]any, error) +} + +// RebuildOpts represents the configuration options used in a server rebuild +// operation. +type RebuildOpts struct { + // AdminPass is the server's admin password + AdminPass string `json:"adminPass,omitempty"` + + // ImageRef is the ID of the image you want your server to be provisioned on. + ImageRef string `json:"imageRef"` + + // Name to set the server to + Name string `json:"name,omitempty"` + + // AccessIPv4 [optional] provides a new IPv4 address for the instance. + AccessIPv4 string `json:"accessIPv4,omitempty"` + + // AccessIPv6 [optional] provides a new IPv6 address for the instance. + AccessIPv6 string `json:"accessIPv6,omitempty"` + + // Metadata [optional] contains key-value pairs (up to 255 bytes each) + // to attach to the server. + Metadata map[string]string `json:"metadata,omitempty"` + + // Personality [optional] includes files to inject into the server at launch. + // Rebuild will base64-encode file contents for you. + Personality Personality `json:"personality,omitempty"` + + // DiskConfig controls how the rebuilt server's disk is partitioned. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` +} + +// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON +func (opts RebuildOpts) ToServerRebuildMap() (map[string]any, error) { + if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.RebuildOpts.DiskConfig" + err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" + return nil, err + } + + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return map[string]any{"rebuild": b}, nil +} + +// Rebuild will reprovision the server according to the configuration options +// provided in the RebuildOpts struct. +func Rebuild(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { + b, err := opts.ToServerRebuildMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResizeOptsBuilder allows extensions to add additional parameters to the +// resize request. +type ResizeOptsBuilder interface { + ToServerResizeMap() (map[string]any, error) +} + +// ResizeOpts represents the configuration options used to control a Resize +// operation. +type ResizeOpts struct { + // FlavorRef is the ID of the flavor you wish your server to become. + FlavorRef string `json:"flavorRef" required:"true"` + + // DiskConfig [optional] controls how the resized server's disk is partitioned. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` +} + +// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON +// request body for the Resize request. +func (opts ResizeOpts) ToServerResizeMap() (map[string]any, error) { + if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.ResizeOpts.DiskConfig" + err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" + return nil, err + } + + return gophercloud.BuildRequestBody(opts, "resize") +} + +// Resize instructs the provider to change the flavor of the server. +// +// Note that this implies rebuilding it. +// +// Unfortunately, one cannot pass rebuild parameters to the resize function. +// When the resize completes, the server will be in VERIFY_RESIZE state. +// While in this state, you can explore the use of the new server's +// configuration. If you like it, call ConfirmResize() to commit the resize +// permanently. Otherwise, call RevertResize() to restore the old configuration. +func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { + b, err := opts.ToServerResizeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ConfirmResize confirms a previous resize operation on a server. +// See Resize() for more details. +func ConfirmResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201, 202, 204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RevertResize cancels a previous resize operation on a server. +// See Resize() for more details. +func RevertResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"revertResize": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetMetadataOptsBuilder allows extensions to add additional parameters to +// the Reset request. +type ResetMetadataOptsBuilder interface { + ToMetadataResetMap() (map[string]any, error) +} + +// MetadataOpts is a map that contains key-value pairs. +type MetadataOpts map[string]string + +// ToMetadataResetMap assembles a body for a Reset request based on the contents +// of a MetadataOpts. +func (opts MetadataOpts) ToMetadataResetMap() (map[string]any, error) { + return map[string]any{"metadata": opts}, nil +} + +// ToMetadataUpdateMap assembles a body for an Update request based on the +// contents of a MetadataOpts. +func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]any, error) { + return map[string]any{"metadata": opts}, nil +} + +// ResetMetadata will create multiple new key-value pairs for the given server +// ID. +// Note: Using this operation will erase any already-existing metadata and +// create the new metadata provided. To keep any already-existing metadata, +// use the UpdateMetadatas or UpdateMetadata function. +func ResetMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { + b, err := opts.ToMetadataResetMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Metadata requests all the metadata for the given server ID. +func Metadata(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { + resp, err := client.Get(ctx, metadataURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to +// the Create request. +type UpdateMetadataOptsBuilder interface { + ToMetadataUpdateMap() (map[string]any, error) +} + +// UpdateMetadata updates (or creates) all the metadata specified by opts for +// the given server ID. This operation does not affect already-existing metadata +// that is not specified by opts. +func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { + b, err := opts.ToMetadataUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// MetadatumOptsBuilder allows extensions to add additional parameters to the +// Create request. +type MetadatumOptsBuilder interface { + ToMetadatumCreateMap() (map[string]any, string, error) +} + +// MetadatumOpts is a map of length one that contains a key-value pair. +type MetadatumOpts map[string]string + +// ToMetadatumCreateMap assembles a body for a Create request based on the +// contents of a MetadataumOpts. +func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]any, string, error) { + if len(opts) != 1 { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.MetadatumOpts" + err.Info = "Must have 1 and only 1 key-value pair" + return nil, "", err + } + metadatum := map[string]any{"meta": opts} + var key string + for k := range metadatum["meta"].(MetadatumOpts) { + key = k + } + return metadatum, key, nil +} + +// CreateMetadatum will create or update the key-value pair with the given key +// for the given server ID. +func CreateMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { + b, key, err := opts.ToMetadatumCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Metadatum requests the key-value pair with the given key for the given +// server ID. +func Metadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { + resp, err := client.Get(ctx, metadatumURL(client, id, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteMetadatum will delete the key-value pair with the given key for the +// given server ID. +func DeleteMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { + resp, err := client.Delete(ctx, metadatumURL(client, id, key), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListAddresses makes a request against the API to list the servers IP +// addresses. +func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { + return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { + return AddressPage{pagination.SinglePageBase(r)} + }) +} + +// ListAddressesByNetwork makes a request against the API to list the servers IP +// addresses for the given network. +func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { + return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { + return NetworkAddressPage{pagination.SinglePageBase(r)} + }) +} + +// CreateImageOptsBuilder allows extensions to add additional parameters to the +// CreateImage request. +type CreateImageOptsBuilder interface { + ToServerCreateImageMap() (map[string]any, error) +} + +// CreateImageOpts provides options to pass to the CreateImage request. +type CreateImageOpts struct { + // Name of the image/snapshot. + Name string `json:"name" required:"true"` + + // Metadata contains key-value pairs (up to 255 bytes each) to attach to + // the created image. + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToServerCreateImageMap formats a CreateImageOpts structure into a request +// body. +func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "createImage") +} + +// CreateImage makes a request against the nova API to schedule an image to be +// created of the server +func CreateImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { + b, err := opts.ToServerCreateImageMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + KeepResponseBody: true, + }) + + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err != nil { + return + } + defer resp.Body.Close() + + if v := r.Header.Get("Content-Type"); v != "application/json" { + return + } + + // The response body is expected to be a small JSON object containing only "image_id". + // Read it fully into memory so the response body can be closed immediately. + // If the caller doesn't read from the buffer, it can still be safely garbage collected. + + var buf bytes.Buffer + + _, r.Err = io.Copy(&buf, resp.Body) + if r.Err != nil { + return + } + + r.Body = &buf + + return +} + +// GetPassword makes a request against the nova API to get the encrypted +// administrative password. +func GetPassword(ctx context.Context, client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { + resp, err := client.Get(ctx, passwordURL(client, serverId), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be +// used as ShowConsoleOutput options +type ShowConsoleOutputOptsBuilder interface { + ToServerShowConsoleOutputMap() (map[string]any, error) +} + +// ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder +type ShowConsoleOutputOpts struct { + // The number of lines to fetch from the end of console log. + // All lines will be returned if this is not specified. + Length int `json:"length,omitempty"` +} + +// ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. +func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") +} + +// ShowConsoleOutput makes a request against the nova API to get console log from the server +func ShowConsoleOutput(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { + b, err := opts.ToServerShowConsoleOutputMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// EvacuateOptsBuilder allows extensions to add additional parameters to the +// the Evacuate request. +type EvacuateOptsBuilder interface { + ToEvacuateMap() (map[string]any, error) +} + +// EvacuateOpts specifies Evacuate action parameters. +type EvacuateOpts struct { + // The name of the host to which the server is evacuated + Host string `json:"host,omitempty"` + + // Indicates whether server is on shared storage + OnSharedStorage bool `json:"onSharedStorage"` + + // An administrative password to access the evacuated server + AdminPass string `json:"adminPass,omitempty"` +} + +// ToServerGroupCreateMap constructs a request body from CreateOpts. +func (opts EvacuateOpts) ToEvacuateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "evacuate") +} + +// Evacuate will Evacuate a failed instance to another host. +func Evacuate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { + b, err := opts.ToEvacuateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// InjectNetworkInfo will inject the network info into a server +func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { + b := map[string]any{ + "injectNetworkInfo": nil, + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Lock is the operation responsible for locking a Compute server. +func Lock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r LockResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"lock": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unlock is the operation responsible for unlocking a Compute server. +func Unlock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnlockResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unlock": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Migrate will initiate a migration of the instance to another host. +func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MigrateResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"migrate": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// LiveMigrateOptsBuilder allows extensions to add additional parameters to the +// LiveMigrate request. +type LiveMigrateOptsBuilder interface { + ToLiveMigrateMap() (map[string]any, error) +} + +// LiveMigrateOpts specifies parameters of live migrate action. +type LiveMigrateOpts struct { + // The host to which to migrate the server. + // If this parameter is None, the scheduler chooses a host. + Host *string `json:"host"` + + // Set to True to migrate local disks by using block migration. + // If the source or destination host uses shared storage and you set + // this value to True, the live migration fails. + BlockMigration *bool `json:"block_migration,omitempty"` + + // Set to True to enable over commit when the destination host is checked + // for available disk space. Set to False to disable over commit. This setting + // affects only the libvirt virt driver. + DiskOverCommit *bool `json:"disk_over_commit,omitempty"` +} + +// ToLiveMigrateMap constructs a request body from LiveMigrateOpts. +func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-migrateLive") +} + +// LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. +func LiveMigrate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { + b, err := opts.ToLiveMigrateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Pause is the operation responsible for pausing a Compute server. +func Pause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r PauseResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"pause": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unpause is the operation responsible for unpausing a Compute server. +func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnpauseResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unpause": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RescueOptsBuilder is an interface that allows extensions to override the +// default structure of a Rescue request. +type RescueOptsBuilder interface { + ToServerRescueMap() (map[string]any, error) +} + +// RescueOpts represents the configuration options used to control a Rescue +// option. +type RescueOpts struct { + // AdminPass is the desired administrative password for the instance in + // RESCUE mode. + // If it's left blank, the server will generate a password. + AdminPass string `json:"adminPass,omitempty"` + + // RescueImageRef contains reference on an image that needs to be used as + // rescue image. + // If it's left blank, the server will be rescued with the default image. + RescueImageRef string `json:"rescue_image_ref,omitempty"` +} + +// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON +// request body for the Rescue request. +func (opts RescueOpts) ToServerRescueMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "rescue") +} + +// Rescue instructs the provider to place the server into RESCUE mode. +func Rescue(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { + b, err := opts.ToServerRescueMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unrescue instructs the provider to return the server from RESCUE mode. +func Unrescue(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnrescueResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unrescue": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetNetwork will reset the network of a server +func ResetNetwork(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResetNetworkResult) { + b := map[string]any{ + "resetNetwork": nil, + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ServerState refers to the states usable in ResetState Action +type ServerState string + +const ( + // StateActive returns the state of the server as active + StateActive ServerState = "active" + + // StateError returns the state of the server as error + StateError ServerState = "error" +) + +// ResetState will reset the state of a server +func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id string, state ServerState) (r ResetStateResult) { + stateMap := map[string]any{"state": state} + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-resetState": stateMap}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Shelve is the operation responsible for shelving a Compute server. +func Shelve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"shelve": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ShelveOffload is the operation responsible for Shelve-Offload a Compute server. +func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"shelveOffload": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UnshelveOptsBuilder allows extensions to add additional parameters to the +// Unshelve request. +type UnshelveOptsBuilder interface { + ToUnshelveMap() (map[string]any, error) +} + +// UnshelveOpts specifies parameters of shelve-offload action. +type UnshelveOpts struct { + // Sets the availability zone to unshelve a server + // Available only after nova 2.77 + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +func (opts UnshelveOpts) ToUnshelveMap() (map[string]any, error) { + // Key 'availabilty_zone' is required if the unshelve action is an object + // i.e {"unshelve": {}} will be rejected + b, err := gophercloud.BuildRequestBody(opts, "unshelve") + if err != nil { + return nil, err + } + + if _, ok := b["unshelve"].(map[string]any)["availability_zone"]; !ok { + b["unshelve"] = nil + } + + return b, err +} + +// Unshelve is the operation responsible for unshelve a Compute server. +func Unshelve(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { + b, err := opts.ToUnshelveMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Start is the operation responsible for starting a Compute server. +func Start(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StartResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-start": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Stop is the operation responsible for stopping a Compute server. +func Stop(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StopResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-stop": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Suspend is the operation responsible for suspending a Compute server. +func Suspend(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SuspendResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"suspend": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Resume is the operation responsible for resuming a Compute server. +func Resume(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResumeResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"resume": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/results.go similarity index 57% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/results.go index 2c22a3c4d..b11b9b236 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/results.go @@ -7,10 +7,12 @@ import ( "fmt" "net/url" "path" + "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" + "github.com/gophercloud/gophercloud/v2/pagination" ) type serverResult struct { @@ -24,12 +26,12 @@ func (r serverResult) Extract() (*Server, error) { return &s, err } -func (r serverResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "server") +func (r serverResult) ExtractInto(v any) error { + return r.ExtractIntoStructPtr(v, "server") } -func ExtractServersInto(r pagination.Page, v interface{}) error { - return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") +func ExtractServersInto(r pagination.Page, v any) error { + return r.(ServerPage).ExtractIntoSlicePtr(v, "servers") } // CreateResult is the response from a Create operation. Call its Extract @@ -117,11 +119,11 @@ func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (stri n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword)) if err != nil { - return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err) + return "", fmt.Errorf("failed to base64 decode encrypted password: %s", err) } password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n]) if err != nil { - return "", fmt.Errorf("Failed to decrypt password: %s", err) + return "", fmt.Errorf("failed to decrypt password: %s", err) } return string(password), nil @@ -132,18 +134,49 @@ func (r CreateImageResult) ExtractImageID() (string, error) { if r.Err != nil { return "", r.Err } - // Get the image id from the header + + microversion := r.Header.Get("X-OpenStack-Nova-API-Version") + + major, minor, err := utils.ParseMicroversion(microversion) + if err != nil { + return "", fmt.Errorf("failed to parse X-OpenStack-Nova-API-Version header: %s", err) + } + + // In microversions prior to 2.45, the image ID was provided in the Location header. + if major < 2 || (major == 2 && minor < 45) { + return r.extractImageIDFromLocationHeader() + } + + // Starting from 2.45, it is included in the response body. + return r.extractImageIDFromResponseBody() +} + +func (r CreateImageResult) extractImageIDFromLocationHeader() (string, error) { u, err := url.ParseRequestURI(r.Header.Get("Location")) if err != nil { return "", err } + imageID := path.Base(u.Path) if imageID == "." || imageID == "/" { - return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u) + return "", fmt.Errorf("failed to parse the ID of newly created image: %s", u) } + return imageID, nil } +func (r CreateImageResult) extractImageIDFromResponseBody() (string, error) { + var response struct { + ImageID string `json:"image_id"` + } + + if err := r.ExtractInto(&response); err != nil { + return "", err + } + + return response.ImageID, nil +} + // Server represents a server/instance in the OpenStack cloud. type Server struct { // ID uniquely identifies this server amongst all other servers, @@ -182,15 +215,15 @@ type Server struct { // Image refers to a JSON object, which itself indicates the OS image used to // deploy the server. - Image map[string]interface{} `json:"-"` + Image map[string]any `json:"-"` // Flavor refers to a JSON object, which itself indicates the hardware // configuration of the deployed server. - Flavor map[string]interface{} `json:"flavor"` + Flavor map[string]any `json:"flavor"` // Addresses includes a list of all IP addresses assigned to the server, // keyed by pool. - Addresses map[string]interface{} `json:"addresses"` + Addresses map[string]any `json:"addresses"` // Metadata includes a list of all user-specified key-value pairs attached // to the server. @@ -198,7 +231,7 @@ type Server struct { // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a server reference. - Links []interface{} `json:"links"` + Links []any `json:"links"` // KeyName indicates which public key was injected into the server on launch. KeyName string `json:"key_name"` @@ -211,7 +244,7 @@ type Server struct { // SecurityGroups includes the security groups that this instance has applied // to it. - SecurityGroups []map[string]interface{} `json:"security_groups"` + SecurityGroups []map[string]any `json:"security_groups"` // AttachedVolumes includes the volume attachments of this instance AttachedVolumes []AttachedVolume `json:"os-extended-volumes:volumes_attached"` @@ -228,6 +261,64 @@ type Server struct { // contain at most one entry. // New in microversion 2.71 ServerGroups *[]string `json:"server_groups"` + + // Host is the host/hypervisor that the instance is hosted on. + Host string `json:"OS-EXT-SRV-ATTR:host"` + + // InstanceName is the name of the instance. + InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` + + // HypervisorHostname is the hostname of the host/hypervisor that the + // instance is hosted on. + HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` + + // ReservationID is the reservation ID of the instance. + // This requires microversion 2.3 or later. + ReservationID *string `json:"OS-EXT-SRV-ATTR:reservation_id"` + + // LaunchIndex is the launch index of the instance. + // This requires microversion 2.3 or later. + LaunchIndex *int `json:"OS-EXT-SRV-ATTR:launch_index"` + + // RAMDiskID is the ID of the RAM disk image of the instance. + // This requires microversion 2.3 or later. + RAMDiskID *string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` + + // KernelID is the ID of the kernel image of the instance. + // This requires microversion 2.3 or later. + KernelID *string `json:"OS-EXT-SRV-ATTR:kernel_id"` + + // Hostname is the hostname of the instance. + // This requires microversion 2.3 or later. + Hostname *string `json:"OS-EXT-SRV-ATTR:hostname"` + + // RootDeviceName is the name of the root device of the instance. + // This requires microversion 2.3 or later. + RootDeviceName *string `json:"OS-EXT-SRV-ATTR:root_device_name"` + + // Userdata is the userdata of the instance. + // This requires microversion 2.3 or later. + Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` + + TaskState string `json:"OS-EXT-STS:task_state"` + VmState string `json:"OS-EXT-STS:vm_state"` + PowerState PowerState `json:"OS-EXT-STS:power_state"` + + LaunchedAt time.Time `json:"-"` + TerminatedAt time.Time `json:"-"` + + // DiskConfig is the disk configuration of the server. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` + + // AvailabilityZone is the availabilty zone the server is in. + AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` + + // Locked indicates the lock status of the server + // This requires microversion 2.9 or later + Locked *bool `json:"locked"` + + // ConfigDrive enables metadata injection through a configuration drive. + ConfigDrive bool `json:"-"` } type AttachedVolume struct { @@ -241,11 +332,54 @@ type Fault struct { Message string `json:"message"` } +type PowerState int + +type ServerExtendedStatusExt struct { + TaskState string `json:"OS-EXT-STS:task_state"` + VmState string `json:"OS-EXT-STS:vm_state"` + PowerState PowerState `json:"OS-EXT-STS:power_state"` +} + +const ( + NOSTATE = iota + RUNNING + _UNUSED1 + PAUSED + SHUTDOWN + _UNUSED2 + CRASHED + SUSPENDED +) + +func (r PowerState) String() string { + switch r { + case NOSTATE: + return "NOSTATE" + case RUNNING: + return "RUNNING" + case PAUSED: + return "PAUSED" + case SHUTDOWN: + return "SHUTDOWN" + case CRASHED: + return "CRASHED" + case SUSPENDED: + return "SUSPENDED" + case _UNUSED1, _UNUSED2: + return "_UNUSED" + default: + return "N/A" + } +} + func (r *Server) UnmarshalJSON(b []byte) error { type tmp Server var s struct { tmp - Image interface{} `json:"image"` + Image any `json:"image"` + LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` + TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` + ConfigDrive any `json:"config_drive"` } err := json.Unmarshal(b, &s) if err != nil { @@ -255,7 +389,7 @@ func (r *Server) UnmarshalJSON(b []byte) error { *r = Server(s.tmp) switch t := s.Image.(type) { - case map[string]interface{}: + case map[string]any: r.Image = t case string: switch t { @@ -264,6 +398,27 @@ func (r *Server) UnmarshalJSON(b []byte) error { } } + r.LaunchedAt = time.Time(s.LaunchedAt) + r.TerminatedAt = time.Time(s.TerminatedAt) + + switch t := s.ConfigDrive.(type) { + case nil: + r.ConfigDrive = false + case bool: + r.ConfigDrive = t + case string: + if t == "" { + r.ConfigDrive = false + } else { + r.ConfigDrive, err = strconv.ParseBool(t) + if err != nil { + return fmt.Errorf("failed to parse ConfigDrive %q: %v", t, err) + } + } + default: + return fmt.Errorf("unknown type for ConfigDrive: %T (value: %v)", t, t) + } + return err } @@ -442,3 +597,135 @@ func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { return s[key], err } + +// EvacuateResult is the response from an Evacuate operation. +// Call its ExtractAdminPass method to retrieve the admin password of the instance. +// The admin password will be an empty string if the cloud is not configured to inject admin passwords.. +type EvacuateResult struct { + gophercloud.Result +} + +func (r EvacuateResult) ExtractAdminPass() (string, error) { + var s struct { + AdminPass string `json:"adminPass"` + } + err := r.ExtractInto(&s) + if err != nil && err.Error() == "EOF" { + return "", nil + } + return s.AdminPass, err +} + +// InjectNetworkResult is the response of a InjectNetworkInfo operation. Call +// its ExtractErr method to determine if the request suceeded or failed. +type InjectNetworkResult struct { + gophercloud.ErrResult +} + +// LockResult and UnlockResult are the responses from a Lock and Unlock +// operations respectively. Call their ExtractErr methods to determine if the +// requests suceeded or failed. +type LockResult struct { + gophercloud.ErrResult +} + +type UnlockResult struct { + gophercloud.ErrResult +} + +// MigrateResult is the response from a Migrate operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type MigrateResult struct { + gophercloud.ErrResult +} + +// PauseResult is the response from a Pause operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type PauseResult struct { + gophercloud.ErrResult +} + +// UnpauseResult is the response from an Unpause operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type UnpauseResult struct { + gophercloud.ErrResult +} + +type commonResult struct { + gophercloud.Result +} + +// RescueResult is the response from a Rescue operation. Call its Extract +// method to retrieve adminPass for a rescued server. +type RescueResult struct { + commonResult +} + +// UnrescueResult is the response from an UnRescue operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type UnrescueResult struct { + gophercloud.ErrResult +} + +// Extract interprets any RescueResult as an AdminPass, if possible. +func (r RescueResult) Extract() (string, error) { + var s struct { + AdminPass string `json:"adminPass"` + } + err := r.ExtractInto(&s) + return s.AdminPass, err +} + +// ResetResult is the response of a ResetNetwork operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type ResetNetworkResult struct { + gophercloud.ErrResult +} + +// ResetResult is the response of a ResetState operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type ResetStateResult struct { + gophercloud.ErrResult +} + +// ShelveResult is the response from a Shelve operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type ShelveResult struct { + gophercloud.ErrResult +} + +// ShelveOffloadResult is the response from a Shelve operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type ShelveOffloadResult struct { + gophercloud.ErrResult +} + +// UnshelveResult is the response from Stop operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type UnshelveResult struct { + gophercloud.ErrResult +} + +// 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 +} + +// SuspendResult is the response from a Suspend operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type SuspendResult struct { + gophercloud.ErrResult +} + +// ResumeResult is the response from an Unsuspend operation. Call +// its ExtractErr method to determine if the request succeeded or failed. +type ResumeResult struct { + gophercloud.ErrResult +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/urls.go similarity index 96% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/urls.go index e892e8d92..36c8c90a1 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/urls.go @@ -1,6 +1,6 @@ package servers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("servers") diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/util.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/util.go new file mode 100644 index 000000000..4f611750d --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/util.go @@ -0,0 +1,24 @@ +package servers + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// WaitForStatus will continually poll a server until it successfully +// transitions to a specified status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { + current, err := Get(ctx, c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go similarity index 86% rename from vendor/github.com/gophercloud/gophercloud/openstack/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go index af4bd512b..4d89fb5d0 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go @@ -6,7 +6,7 @@ OpenStack cloud and for provisioning various service-level clients. Example of Creating a Service Client ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) + provider, err := openstack.AuthenticatedClient(context.TODO(), ao) client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint.go new file mode 100644 index 000000000..617843442 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint.go @@ -0,0 +1,190 @@ +package openstack + +import ( + "context" + "regexp" + "slices" + "strconv" + + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/utils" +) + +var versionedServiceTypeAliasRegexp = regexp.MustCompile(`^.*v(\d)$`) + +func extractServiceTypeVersion(serviceType string) int { + matches := versionedServiceTypeAliasRegexp.FindAllStringSubmatch(serviceType, 1) + if matches != nil { + // no point converting to an int + ret, err := strconv.Atoi(matches[0][1]) + if err != nil { + return 0 + } + return ret + } + return 0 +} + +func endpointSupportsVersion(ctx context.Context, client *gophercloud.ProviderClient, serviceType, endpointURL string, expectedVersion int) (bool, error) { + // Swift doesn't support version discovery :( + if expectedVersion == 0 || serviceType == "object-store" { + return true, nil + } + + // Repeating verbatim from keystoneauth1 [1]: + // + // > The sins of our fathers become the blood on our hands. + // > If a user requests an old-style service type such as volumev2, then they + // > are inherently requesting the major API version 2. It's not a good + // > interface, but it's the one that was imposed on the world years ago + // > because the client libraries all hid the version discovery document. + // > In order to be able to ensure that a user who requests volumev2 does not + // > get a block-storage endpoint that only provides v3 of the block-storage + // > service, we need to pull the version out of the service_type. The + // > service-types-authority will prevent the growth of new monstrosities such + // > as this, but in order to move forward without breaking people, we have + // > to just cry in the corner while striking ourselves with thorned branches. + // > That said, for sure only do this hack for officially known service_types. + // + // So yeah, what mordred said. + // + // https://github.com/openstack/keystoneauth/blob/5.10.0/keystoneauth1/discover.py#L270-L290 + impliedVersion := extractServiceTypeVersion(serviceType) + if impliedVersion != 0 && impliedVersion != expectedVersion { + return false, nil + } + + // NOTE(stephenfin) In addition to the above, keystoneauth also supports a URL + // hack whereby it will extract the version from the URL. We may wish to + // implement this too. + + endpointURL, err := utils.BaseVersionedEndpoint(endpointURL) + if err != nil { + return false, err + } + + supportedVersions, err := utils.GetServiceVersions(ctx, client, endpointURL, false) + if err != nil { + return false, err + } + + for _, supportedVersion := range supportedVersions { + if supportedVersion.Major == expectedVersion { + return true, nil + } + } + + return false, nil +} + +/* +V2Endpoint discovers the endpoint URL for a specific service from a +ServiceCatalog acquired during the v2 identity service. + +The specified EndpointOpts are used to identify a unique, unambiguous endpoint +to return. It's an error both when multiple endpoints match the provided +criteria and when none do. The minimum that can be specified is a Type, but you +will also often need to specify a Name and/or a Region depending on what's +available on your OpenStack deployment. +*/ +func V2Endpoint(ctx context.Context, client *gophercloud.ProviderClient, catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { + // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. + // + // If multiple endpoints are found, we return the first result and disregard the rest. + // This behavior matches the Python library. See GH-1764. + for _, entry := range catalog.Entries { + if (slices.Contains(opts.Types(), entry.Type)) && (opts.Name == "" || entry.Name == opts.Name) { + for _, endpoint := range entry.Endpoints { + if opts.Region != "" && endpoint.Region != opts.Region { + continue + } + + var endpointURL string + switch opts.Availability { + case gophercloud.AvailabilityPublic: + endpointURL = gophercloud.NormalizeURL(endpoint.PublicURL) + case gophercloud.AvailabilityInternal: + endpointURL = gophercloud.NormalizeURL(endpoint.InternalURL) + case gophercloud.AvailabilityAdmin: + endpointURL = gophercloud.NormalizeURL(endpoint.AdminURL) + default: + err := &ErrInvalidAvailabilityProvided{} + err.Argument = "Availability" + err.Value = opts.Availability + return "", err + } + + endpointSupportsVersion, err := endpointSupportsVersion(ctx, client, entry.Type, endpointURL, opts.Version) + if err != nil { + return "", err + } + if !endpointSupportsVersion { + continue + } + + return endpointURL, nil + } + } + } + + // Report an error if there were no matching endpoints. + err := &gophercloud.ErrEndpointNotFound{} + return "", err +} + +/* +V3Endpoint discovers the endpoint URL for a specific service from a Catalog +acquired during the v3 identity service. + +The specified EndpointOpts are used to identify a unique, unambiguous endpoint +to return. It's an error both when multiple endpoints match the provided +criteria and when none do. The minimum that can be specified is a Type, but you +will also often need to specify a Name and/or a Region depending on what's +available on your OpenStack deployment. +*/ +func V3Endpoint(ctx context.Context, client *gophercloud.ProviderClient, catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { + if opts.Availability != gophercloud.AvailabilityAdmin && + opts.Availability != gophercloud.AvailabilityPublic && + opts.Availability != gophercloud.AvailabilityInternal { + err := &ErrInvalidAvailabilityProvided{} + err.Argument = "Availability" + err.Value = opts.Availability + return "", err + } + + // Extract Endpoints from the catalog entries that match the requested Type, Interface, + // Name if provided, and Region if provided. + // + // If multiple endpoints are found, we return the first result and disregard the rest. + // This behavior matches the Python library. See GH-1764. + for _, entry := range catalog.Entries { + if (slices.Contains(opts.Types(), entry.Type)) && (opts.Name == "" || entry.Name == opts.Name) { + for _, endpoint := range entry.Endpoints { + if opts.Availability != gophercloud.Availability(endpoint.Interface) { + continue + } + if opts.Region != "" && endpoint.Region != opts.Region && endpoint.RegionID != opts.Region { + continue + } + + endpointURL := gophercloud.NormalizeURL(endpoint.URL) + + endpointSupportsVersion, err := endpointSupportsVersion(ctx, client, entry.Type, endpointURL, opts.Version) + if err != nil { + return "", err + } + if !endpointSupportsVersion { + continue + } + + return endpointURL, nil + } + } + } + + // Report an error if there were no matching endpoints. + err := &gophercloud.ErrEndpointNotFound{} + return "", err +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go similarity index 52% rename from vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go index 509700790..573c1f06f 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go @@ -1,11 +1,15 @@ package openstack import ( - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "slices" + + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" ) +// TODO(stephenfin): Remove this module in v3. The functions below are no longer used. + /* V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired during the v2 identity service. @@ -18,39 +22,33 @@ available on your OpenStack deployment. */ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. - var endpoints = make([]tokens2.Endpoint, 0, 1) + // + // If multiple endpoints are found, we return the first result and disregard the rest. + // This behavior matches the Python library. See GH-1764. for _, entry := range catalog.Entries { - if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { + if (slices.Contains(opts.Types(), entry.Type)) && (opts.Name == "" || entry.Name == opts.Name) { for _, endpoint := range entry.Endpoints { - if opts.Region == "" || endpoint.Region == opts.Region { - endpoints = append(endpoints, endpoint) + if opts.Region != "" && endpoint.Region != opts.Region { + continue } - } - } - } - // If multiple endpoints were found, use the first result - // and disregard the other endpoints. - // - // This behavior matches the Python library. See GH-1764. - if len(endpoints) > 1 { - endpoints = endpoints[0:1] - } + var endpointURL string + switch opts.Availability { + case gophercloud.AvailabilityPublic: + endpointURL = gophercloud.NormalizeURL(endpoint.PublicURL) + case gophercloud.AvailabilityInternal: + endpointURL = gophercloud.NormalizeURL(endpoint.InternalURL) + case gophercloud.AvailabilityAdmin: + endpointURL = gophercloud.NormalizeURL(endpoint.AdminURL) + default: + err := &ErrInvalidAvailabilityProvided{} + err.Argument = "Availability" + err.Value = opts.Availability + return "", err + } - // Extract the appropriate URL from the matching Endpoint. - for _, endpoint := range endpoints { - switch opts.Availability { - case gophercloud.AvailabilityPublic: - return gophercloud.NormalizeURL(endpoint.PublicURL), nil - case gophercloud.AvailabilityInternal: - return gophercloud.NormalizeURL(endpoint.InternalURL), nil - case gophercloud.AvailabilityAdmin: - return gophercloud.NormalizeURL(endpoint.AdminURL), nil - default: - err := &ErrInvalidAvailabilityProvided{} - err.Argument = "Availability" - err.Value = opts.Availability - return "", err + return endpointURL, nil + } } } @@ -70,41 +68,35 @@ will also often need to specify a Name and/or a Region depending on what's available on your OpenStack deployment. */ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { + if opts.Availability != gophercloud.AvailabilityAdmin && + opts.Availability != gophercloud.AvailabilityPublic && + opts.Availability != gophercloud.AvailabilityInternal { + err := &ErrInvalidAvailabilityProvided{} + err.Argument = "Availability" + err.Value = opts.Availability + return "", err + } + // Extract Endpoints from the catalog entries that match the requested Type, Interface, // Name if provided, and Region if provided. - var endpoints = make([]tokens3.Endpoint, 0, 1) + // + // If multiple endpoints are found, we return the first result and disregard the rest. + // This behavior matches the Python library. See GH-1764. for _, entry := range catalog.Entries { - if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { + if (slices.Contains(opts.Types(), entry.Type)) && (opts.Name == "" || entry.Name == opts.Name) { for _, endpoint := range entry.Endpoints { - if opts.Availability != gophercloud.AvailabilityAdmin && - opts.Availability != gophercloud.AvailabilityPublic && - opts.Availability != gophercloud.AvailabilityInternal { - err := &ErrInvalidAvailabilityProvided{} - err.Argument = "Availability" - err.Value = opts.Availability - return "", err + if opts.Availability != gophercloud.Availability(endpoint.Interface) { + continue } - if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && - (opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { - endpoints = append(endpoints, endpoint) + if opts.Region != "" && endpoint.Region != opts.Region && endpoint.RegionID != opts.Region { + continue } + + return gophercloud.NormalizeURL(endpoint.URL), nil } } } - // If multiple endpoints were found, use the first result - // and disregard the other endpoints. - // - // This behavior matches the Python library. See GH-1764. - if len(endpoints) > 1 { - endpoints = endpoints[0:1] - } - - // Extract the URL from the matching Endpoint. - for _, endpoint := range endpoints { - return gophercloud.NormalizeURL(endpoint.URL), nil - } - // Report an error if there were no matching endpoints. err := &gophercloud.ErrEndpointNotFound{} return "", err diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/errors.go similarity index 97% rename from vendor/github.com/gophercloud/gophercloud/openstack/errors.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/errors.go index cba6ae5f0..f5273483e 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/errors.go @@ -3,7 +3,7 @@ package openstack import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ErrEndpointNotFound is the error when no suitable endpoint can be found diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go similarity index 81% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go index 348dd2083..b14d69a56 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go @@ -12,7 +12,7 @@ Example to List Tenants Limit: 2, } - allPages, err := tenants.List(identityClient, listOpts).AllPages() + allPages, err := tenants.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -34,7 +34,7 @@ Example to Create a Tenant Enabled: gophercloud.Enabled, } - tenant, err := tenants.Create(identityClient, createOpts).Extract() + tenant, err := tenants.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -48,7 +48,7 @@ Example to Update a Tenant Enabled: gophercloud.Disabled, } - tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() + tenant, err := tenants.Update(context.TODO(), identityClient, tenantID, updateOpts).Extract() if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to Delete a Tenant tenantID := "e6db6ed6277c461a853458589063b295" - err := tenants.Delete(identitYClient, tenantID).ExtractErr() + err := tenants.Delete(context.TODO(), identitYClient, tenantID).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go similarity index 63% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go index f16df38e5..84a8b9df1 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go @@ -1,10 +1,18 @@ package tenants import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToTenantListQuery() (string, error) +} + // ListOpts filters the Tenants that are returned by the List call. type ListOpts struct { // Marker is the ID of the last Tenant on the previous page. @@ -14,15 +22,21 @@ type ListOpts struct { Limit int `q:"limit"` } +// ToTenantListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTenantListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // List enumerates the Tenants to which the current token has access. -func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { - q, err := gophercloud.BuildQueryString(opts) + query, err := opts.ToTenantListQuery() if err != nil { return pagination.Pager{Err: err} } - url += q.String() + url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return TenantPage{pagination.LinkedPageBase{PageResult: r}} @@ -44,23 +58,23 @@ type CreateOpts struct { // CreateOptsBuilder enables extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToTenantCreateMap() (map[string]interface{}, error) + ToTenantCreateMap() (map[string]any, error) } // ToTenantCreateMap assembles a request body based on the contents of // a CreateOpts. -func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTenantCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "tenant") } // Create is the operation responsible for creating new tenant. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTenantCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -68,8 +82,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get requests details on a single tenant by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -77,7 +91,7 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToTenantUpdateMap() (map[string]interface{}, error) + ToTenantUpdateMap() (map[string]any, error) } // UpdateOpts specifies the base attributes that may be updated on an existing @@ -94,18 +108,18 @@ type UpdateOpts struct { } // ToTenantUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToTenantUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "tenant") } // Update is the operation responsible for updating exist tenants by their TenantID. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToTenantUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -113,8 +127,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete is the operation responsible for permanently deleting a tenant. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/results.go similarity index 96% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/results.go index 2daff9840..2569ffec0 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/results.go @@ -1,8 +1,8 @@ package tenants import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Tenant is a grouping of users in the identity service. diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go similarity index 92% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go index 0f0266907..4c2aaf384 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go @@ -1,6 +1,6 @@ package tenants -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("tenants") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go similarity index 76% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go index 5375eea87..7cfc36730 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go @@ -12,7 +12,7 @@ Example to Create an Unscoped Token from a Password Password: "pass" } - token, err := tokens.Create(identityClient, authOpts).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() if err != nil { panic(err) } @@ -25,7 +25,7 @@ Example to Create a Token from a Tenant ID and Password TenantID: "fc394f2ab2df4114bde39905f800dc57" } - token, err := tokens.Create(identityClient, authOpts).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() if err != nil { panic(err) } @@ -38,7 +38,7 @@ Example to Create a Token from a Tenant Name and Password TenantName: "tenantname" } - token, err := tokens.Create(identityClient, authOpts).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go similarity index 82% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go index 84f16c3fc..5afa8fd26 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go @@ -1,6 +1,10 @@ package tokens -import "github.com/gophercloud/gophercloud" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // PasswordCredentialsV2 represents the required options to authenticate // with a username and password. @@ -37,7 +41,8 @@ type AuthOptionsV2 struct { type AuthOptionsBuilder interface { // ToTokenCreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. - ToTokenV2CreateMap() (map[string]interface{}, error) + ToTokenV2CreateMap() (map[string]any, error) + CanReauth() bool } // AuthOptions are the valid options for Openstack Identity v2 authentication. @@ -53,7 +58,7 @@ type AuthOptions struct { } // ToTokenV2CreateMap builds a token request body from the given AuthOptions. -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]any, error) { v2Opts := AuthOptionsV2{ TenantID: opts.TenantID, TenantName: opts.TenantName, @@ -77,17 +82,21 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return b, nil } +func (opts AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + // Create authenticates to the identity service and attempts to acquire a Token. // Generally, rather than interact with this call directly, end users should // call openstack.AuthenticatedClient(), which abstracts all of the gory details // about navigating service catalogs and such. -func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { b, err := auth.ToTokenV2CreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, OmitHeaders: []string{"X-Auth-Token"}, }) @@ -96,8 +105,8 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r Creat } // Get validates and retrieves information for user's token. -func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { - resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := client.Get(ctx, GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go similarity index 97% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go index ee5da37f4..516371ee8 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go @@ -3,8 +3,8 @@ package tokens import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" ) // Token provides only the most basic information related to an authentication diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go similarity index 87% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go index ee0a28f20..845cdb58b 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go @@ -1,6 +1,6 @@ package tokens -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // CreateURL generates the URL used to create new Tokens. func CreateURL(client *gophercloud.ServiceClient) string { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go similarity index 82% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go index a30d0faf3..bd74473da 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go @@ -13,7 +13,7 @@ Example to Create a Token From an EC2 access and secret keys Secret: "18f4f6761ada4e3795fa5273c30349b9", } - token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken() + token, err := ec2tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -32,7 +32,7 @@ Example to auth a client using EC2 access and secret keys AllowReauth: true, } - err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + err = openstack.AuthenticateV3(context.TODO(), client, authOptions, gophercloud.EndpointOpts{}) if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go similarity index 87% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go index 32ba0e621..1d4cb5492 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go @@ -1,19 +1,20 @@ package ec2tokens import ( + "context" "crypto/hmac" + "crypto/rand" "crypto/sha1" "crypto/sha256" "encoding/hex" "fmt" - "math/rand" "net/url" "sort" "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" ) const ( @@ -66,7 +67,7 @@ type AuthOptions struct { // Signature can be either a []byte (encoded to base64 automatically) or // a string. You can set the singature explicitly, when you already know // it. In this case default Params won't be automatically set. Optional. - Signature interface{} `json:"signature"` + Signature any `json:"signature"` // BodyHash is a HTTP request body sha256 hash. When nil and Signature // is not set, a random hash is generated. Optional. BodyHash *string `json:"body_hash"` @@ -204,13 +205,13 @@ func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders st // ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder // interface. -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { return nil, nil } // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. -func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { return nil, nil } @@ -220,7 +221,7 @@ func (opts *AuthOptions) CanReauth() bool { } // ToTokenV3CreateMap formats an AuthOptions into a create request. -func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3CreateMap(map[string]any) (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "credentials") if err != nil { return nil, err @@ -231,18 +232,14 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] } // calculate signature, when it is not set - c, _ := b["credentials"].(map[string]interface{}) + c, _ := b["credentials"].(map[string]any) h := interfaceToMap(c, "headers") p := interfaceToMap(c, "params") // detect and process a signature v2 if v, ok := p["SignatureVersion"]; ok && v == "2" { - if _, ok := c["body_hash"]; ok { - delete(c, "body_hash") - } - if _, ok := c["headers"]; ok { - delete(c, "headers") - } + delete(c, "body_hash") + delete(c, "headers") if v, ok := p["SignatureMethod"]; ok { // params is a map of strings strToSign := EC2CredentialsBuildStringToSignV2(*opts) @@ -268,12 +265,16 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] if opts.Timestamp != nil { date = *opts.Timestamp } - if v, _ := c["body_hash"]; v == nil { + if v := c["body_hash"]; v == nil { // when body_hash is not set, generate a random one - c["body_hash"] = randomBodyHash() + bodyHash, err := randomBodyHash() + if err != nil { + return nil, fmt.Errorf("failed to generate random hash") + } + c["body_hash"] = bodyHash } - signedHeaders, _ := h["X-Amz-SignedHeaders"] + signedHeaders := h["X-Amz-SignedHeaders"] stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date) key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date) @@ -288,7 +289,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] } // Create authenticates and either generates a new token from EC2 credentials -func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -298,9 +299,8 @@ func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tok // delete "token" element, since it is used in s3tokens deleteBodyElements(b, "token") - resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{"X-Auth-Token": ""}, - OkCodes: []int{200}, + resp, err := c.Post(ctx, ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -308,7 +308,7 @@ func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tok // ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't // generate a new token ID, but returns a tokens.CreateResult. -func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +func ValidateS3Token(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -318,9 +318,8 @@ func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilde // delete unused element, since it is used in ec2tokens only deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") - resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{"X-Auth-Token": ""}, - OkCodes: []int{200}, + resp, err := c.Post(ctx, s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -343,18 +342,20 @@ func sumHMAC256(key []byte, data []byte) []byte { } // randomBodyHash is a func to generate a random sha256 hexdigest. -func randomBodyHash() string { +func randomBodyHash() (string, error) { h := make([]byte, 64) - rand.Read(h) - return hex.EncodeToString(h) + if _, err := rand.Read(h); err != nil { + return "", err + } + return hex.EncodeToString(h), nil } // interfaceToMap is a func used to represent a "credentials" map element as a // "map[string]string" -func interfaceToMap(c map[string]interface{}, key string) map[string]string { - // convert map[string]interface{} to map[string]string +func interfaceToMap(c map[string]any, key string) map[string]string { + // convert map[string]any to map[string]string m := make(map[string]string) - if v, _ := c[key].(map[string]interface{}); v != nil { + if v, _ := c[key].(map[string]any); v != nil { for k, v := range v { m[k] = v.(string) } @@ -366,12 +367,10 @@ func interfaceToMap(c map[string]interface{}, key string) map[string]string { } // deleteBodyElements deletes map body elements -func deleteBodyElements(b map[string]interface{}, elements ...string) { - if c, ok := b["credentials"].(map[string]interface{}); ok { +func deleteBodyElements(b map[string]any, elements ...string) { + if c, ok := b["credentials"].(map[string]any); ok { for _, k := range elements { - if _, ok := c[k]; ok { - delete(c, k) - } + delete(c, k) } } } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go similarity index 81% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go index 84b33b282..91add91eb 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go @@ -1,6 +1,6 @@ package ec2tokens -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func ec2tokensURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("ec2tokens") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go similarity index 81% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go index 4294ef6c8..c0cfa924a 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go @@ -6,7 +6,7 @@ Example to Create an OAuth1 Consumer createConsumerOpts := oauth1.CreateConsumerOpts{ Description: "My consumer", } - consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract() + consumer, err := oauth1.CreateConsumer(context.TODO(), identityClient, createConsumerOpts).Extract() if err != nil { panic(err) } @@ -22,7 +22,7 @@ Example to Request an unauthorized OAuth1 token OAuthSignatureMethod: oauth1.HMACSHA1, RequestedProjectID: projectID, } - requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract() + requestToken, err := oauth1.RequestToken(context.TODO(), identityClient, requestTokenOpts).Extract() if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to Authorize an unauthorized OAuth1 token {Name: "member"}, }, } - authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() + authToken, err := oauth1.AuthorizeToken(context.TODO(), identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() if err != nil { panic(err) } @@ -54,7 +54,7 @@ Example to Create an OAuth1 Access Token OAuthVerifier: authToken.OAuthVerifier, OAuthSignatureMethod: oauth1.HMACSHA1, } - accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract() + accessToken, err := oauth1.CreateAccessToken(context.TODO(), identityClient, accessTokenOpts).Extract() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example to Create an OAuth1 Access Token Example to List User's OAuth1 Access Tokens - allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages() + allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages(context.TODO()) if err != nil { panic(err) } @@ -93,7 +93,7 @@ Example to Authenticate a client using OAuth1 method OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, } - err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + err = openstack.AuthenticateV3(context.TODO(), client, authOptions, gophercloud.EndpointOpts{}) if err != nil { panic(err) } @@ -114,7 +114,7 @@ Example to Create a Token using OAuth1 method OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, } - err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token) + err := tokens.Create(context.TODO(), identityClient, createOpts).ExtractInto(&oauth1Token) if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go similarity index 82% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go index 028b5a45b..0b23269ff 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go @@ -1,11 +1,12 @@ package oauth1 import ( + "context" "crypto/hmac" "crypto/sha1" "encoding/base64" "fmt" - "io/ioutil" + "io" "math/rand" "net/url" "sort" @@ -13,9 +14,9 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Type SignatureMethod is a OAuth1 SignatureMethod type. @@ -71,7 +72,7 @@ type AuthOptions struct { // ToTokenV3HeadersMap builds the headers required for an OAuth1-based create // request. -func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) { +func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]any) (map[string]string, error) { q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") if err != nil { return nil, err @@ -96,7 +97,7 @@ func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) ( // ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder // interface. -func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { return nil, nil } @@ -107,7 +108,7 @@ func (opts AuthOptions) CanReauth() bool { } // ToTokenV3CreateMap builds a create request body. -func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV3CreateMap(map[string]any) (map[string]any, error) { // identityReq defines the "identity" portion of an OAuth1-based authentication // create request body. type identityReq struct { @@ -133,16 +134,16 @@ func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]i return gophercloud.BuildRequestBody(req, "") } -// Create authenticates and either generates a new OpenStack token from an -// OAuth1 token. -func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { +// Create authenticates and either generates a new OpenStack token +// from an OAuth1 token. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err return } - headerOpts := map[string]interface{}{ + headerOpts := map[string]any{ "method": "POST", "url": authURL(client), } @@ -153,7 +154,7 @@ func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) ( return } - resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, authURL(client), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) @@ -164,7 +165,7 @@ func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) ( // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the CreateConsumer request. type CreateConsumerOptsBuilder interface { - ToOAuth1CreateConsumerMap() (map[string]interface{}, error) + ToOAuth1CreateConsumerMap() (map[string]any, error) } // CreateConsumerOpts provides options used to create a new Consumer. @@ -174,27 +175,27 @@ type CreateConsumerOpts struct { } // ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request. -func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) { +func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "consumer") } -// Create creates a new Consumer. -func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { +// CreateConsumer creates a new Consumer. +func CreateConsumer(ctx context.Context, client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToOAuth1CreateConsumerMap() if err != nil { r.Err = err return } - resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// Delete deletes a Consumer. -func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { - resp, err := client.Delete(consumerURL(client, id), nil) +// DeleteConsumer deletes a Consumer. +func DeleteConsumer(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { + resp, err := client.Delete(ctx, consumerURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -207,12 +208,18 @@ func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { } // GetConsumer retrieves details on a single Consumer by ID. -func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { - resp, err := client.Get(consumerURL(client, id), &r.Body, nil) +func GetConsumer(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { + resp, err := client.Get(ctx, consumerURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// UpdateConsumerOptsBuilder allows extensions to add additional parameters to the +// UpdateConsumer request. +type UpdateConsumerOptsBuilder interface { + ToOAuth1UpdateConsumerMap() (map[string]any, error) +} + // UpdateConsumerOpts provides options used to update a consumer. type UpdateConsumerOpts struct { // Description is the consumer description. @@ -221,18 +228,18 @@ type UpdateConsumerOpts struct { // ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update // request. -func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) { +func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "consumer") } // UpdateConsumer updates an existing Consumer. -func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { +func UpdateConsumer(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateConsumerOptsBuilder) (r UpdateConsumerResult) { b, err := opts.ToOAuth1UpdateConsumerMap() if err != nil { r.Err = err return } - resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -298,14 +305,14 @@ func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[ } // RequestToken requests an unauthorized OAuth1 Token. -func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { +func RequestToken(ctx context.Context, client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client)) if err != nil { r.Err = err return } - resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -319,14 +326,14 @@ func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilde r.Err = fmt.Errorf("unsupported Content-Type: %q", v) return } - r.Body, r.Err = ioutil.ReadAll(resp.Body) + r.Body, r.Err = io.ReadAll(resp.Body) return } // AuthorizeTokenOptsBuilder allows extensions to add additional parameters to // the AuthorizeToken request. type AuthorizeTokenOptsBuilder interface { - ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) + ToOAuth1AuthorizeTokenMap() (map[string]any, error) } // AuthorizeTokenOpts provides options used to authorize a request token. @@ -342,7 +349,7 @@ type Role struct { // ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token // request. -func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) { +func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]any, error) { for _, r := range opts.Roles { if r == (Role{}) { return nil, fmt.Errorf("role must not be empty") @@ -352,13 +359,13 @@ func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface } // AuthorizeToken authorizes an unauthorized consumer token. -func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { +func AuthorizeToken(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { b, err := opts.ToOAuth1AuthorizeTokenMap() if err != nil { r.Err = err return } - resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -426,14 +433,14 @@ func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u str } // CreateAccessToken creates a new OAuth1 Access Token -func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { +func CreateAccessToken(ctx context.Context, client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client)) if err != nil { r.Err = err return } - resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -447,20 +454,20 @@ func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessToken r.Err = fmt.Errorf("unsupported Content-Type: %q", v) return } - r.Body, r.Err = ioutil.ReadAll(resp.Body) + r.Body, r.Err = io.ReadAll(resp.Body) return } // GetAccessToken retrieves details on a single OAuth1 access token by an ID. -func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { - resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil) +func GetAccessToken(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { + resp, err := client.Get(ctx, userAccessTokenURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RevokeAccessToken revokes an OAuth1 access token. -func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { - resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil) +func RevokeAccessToken(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { + resp, err := client.Delete(ctx, userAccessTokenURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -483,8 +490,8 @@ func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id s // GetAccessTokenRole retrieves details on a single OAuth1 access token role by // an ID. -func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { - resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) +func GetAccessTokenRole(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { + resp, err := client.Get(ctx, userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -493,7 +500,7 @@ func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id str // buildOAuth1QueryString builds a URLEncoded parameters string specific for // OAuth1-based requests. -func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) { +func buildOAuth1QueryString(opts any, timestamp *time.Time, callback string) (*url.URL, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, err diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go similarity index 98% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go index 2a3706162..2ed75bd74 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go @@ -5,8 +5,8 @@ import ( "net/url" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Consumer represents a delegated authorization request between two diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go similarity index 96% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go index 9b51d53b3..c8dc02e5d 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go @@ -1,6 +1,6 @@ package oauth1 -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func consumersURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("OS-OAUTH1", "consumers") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go similarity index 74% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go index de74c82ec..c711d0c28 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go @@ -12,7 +12,7 @@ Example to Create a Token From a Username and Password Password: "password", } - token, err := tokens.Create(identityClient, authOptions).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -25,7 +25,7 @@ Example to Create a Token From a Username, Password, and Domain DomainID: "default", } - token, err := tokens.Create(identityClient, authOptions).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -36,7 +36,7 @@ Example to Create a Token From a Username, Password, and Domain DomainName: "default", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -47,7 +47,7 @@ Example to Create a Token From a Token TokenID: "token_id", } - token, err := tokens.Create(identityClient, authOptions).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example to Create a Token from a Username and Password with Project ID Scope Password: "password", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -81,7 +81,7 @@ Example to Create a Token from a Username and Password with Domain ID Scope Password: "password", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -99,7 +99,7 @@ Example to Create a Token from a Username and Password with Project Name Scope Password: "password", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go similarity index 80% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go index 1af55d813..fa8b925d0 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go @@ -1,6 +1,10 @@ package tokens -import "github.com/gophercloud/gophercloud" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // Scope allows a created token to be limited to a specific domain or project. type Scope struct { @@ -9,6 +13,7 @@ type Scope struct { DomainID string DomainName string System bool + TrustID string } // AuthOptionsBuilder provides the ability for extensions to add additional @@ -16,9 +21,9 @@ type Scope struct { type AuthOptionsBuilder interface { // ToTokenV3CreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. - ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) - ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) - ToTokenV3ScopeMap() (map[string]interface{}, error) + ToTokenV3CreateMap(map[string]any) (map[string]any, error) + ToTokenV3HeadersMap(map[string]any) (map[string]string, error) + ToTokenV3ScopeMap() (map[string]any, error) CanReauth() bool } @@ -67,7 +72,7 @@ type AuthOptions struct { } // ToTokenV3CreateMap builds a request body from AuthOptions. -func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]any) (map[string]any, error) { gophercloudAuthOpts := gophercloud.AuthOptions{ Username: opts.Username, UserID: opts.UserID, @@ -86,7 +91,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } // ToTokenV3ScopeMap builds a scope request body from AuthOptions. -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { scope := gophercloud.AuthScope(opts.Scope) gophercloudAuthOpts := gophercloud.AuthOptions{ @@ -109,7 +114,7 @@ func (opts *AuthOptions) CanReauth() bool { // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. -func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { return nil, nil } @@ -121,7 +126,7 @@ func subjectTokenHeaders(subjectToken string) map[string]string { // Create authenticates and either generates a new token, or changes the Scope // of an existing token. -func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { scope, err := opts.ToTokenV3ScopeMap() if err != nil { r.Err = err @@ -134,7 +139,7 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu return } - resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ OmitHeaders: []string{"X-Auth-Token"}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -142,8 +147,8 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu } // Get validates and retrieves information about another token. -func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { - resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, c *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := c.Get(ctx, tokenURL(c), &r.Body, &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) @@ -152,8 +157,8 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { } // Validate determines if a specified token is valid or not. -func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ +func Validate(ctx context.Context, c *gophercloud.ServiceClient, token string) (bool, error) { + resp, err := c.Head(ctx, tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 204, 404}, }) @@ -165,8 +170,8 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { } // Revoke immediately makes specified token invalid. -func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { - resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{ +func Revoke(ctx context.Context, c *gophercloud.ServiceClient, token string) (r RevokeResult) { + resp, err := c.Delete(ctx, tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go similarity index 89% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go index f1e17e9f7..bd61a9a67 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go @@ -3,7 +3,7 @@ package tokens import ( "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Endpoint represents a single API endpoint offered by a service. @@ -76,6 +76,18 @@ type Project struct { Name string `json:"name"` } +type TrustUser struct { + ID string `json:"id"` +} + +// Trust provides information about trust with which User is authorized. +type Trust struct { + ID string `json:"id"` + Impersonation bool `json:"impersonation"` + TrusteeUserID TrustUser `json:"trustee_user"` + TrustorUserID TrustUser `json:"trustor_user"` +} + // commonResult is the response from a request. A commonResult has various // methods which can be used to extract different details about the result. type commonResult struct { @@ -160,6 +172,15 @@ func (r commonResult) ExtractDomain() (*Domain, error) { return s.Domain, err } +// ExtractTrust returns Trust to which User is authorized. +func (r commonResult) ExtractTrust() (*Trust, error) { + var s struct { + Trust *Trust `json:"OS-TRUST:trust"` + } + err := r.ExtractInto(&s) + return s.Trust, err +} + // CreateResult is the response from a Create request. Use ExtractToken() // to interpret it as a Token, or ExtractServiceCatalog() to interpret it // as a service catalog. @@ -189,6 +210,6 @@ type Token struct { ExpiresAt time.Time `json:"expires_at"` } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.ExtractIntoStructPtr(v, "token") } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go similarity index 70% rename from vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go index 2f864a31c..2218c107f 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go @@ -1,6 +1,6 @@ package tokens -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func tokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("auth", "tokens") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/doc.go similarity index 72% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/doc.go index 20a510839..f99cf743f 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/doc.go @@ -11,7 +11,7 @@ Example to Upload Image Data } defer imageData.Close() - err = imagedata.Upload(imageClient, imageID, imageData).ExtractErr() + err = imagedata.Upload(context.TODO(), imageClient, imageID, imageData).ExtractErr() if err != nil { panic(err) } @@ -26,7 +26,7 @@ Example to Stage Image Data } defer imageData.Close() - err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr() + err = imagedata.Stage(context.TODO(), imageClient, imageID, imageData).ExtractErr() if err != nil { panic(err) } @@ -35,7 +35,7 @@ Example to Download Image Data imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" - image, err := imagedata.Download(imageClient, imageID).Extract() + image, err := imagedata.Download(context.TODO(), imageClient, imageID).Extract() if err != nil { panic(err) } @@ -43,7 +43,7 @@ Example to Download Image Data // close the reader, when reading has finished defer image.Close() - imageData, err := ioutil.ReadAll(image) + imageData, err := io.ReadAll(image) if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/requests.go similarity index 51% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/requests.go index 820a0c699..fd0f3e7ff 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/requests.go @@ -1,14 +1,15 @@ package imagedata import ( + "context" "io" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Upload uploads an image file. -func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { - resp, err := client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ +func Upload(ctx context.Context, client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { + resp, err := client.Put(ctx, uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) @@ -16,11 +17,11 @@ func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r Upl return } -// Stage performs PUT call on the existing image object in the Imageservice with +// Stage performs PUT call on the existing image object in the Image service with // the provided file. // Existing image object must be in the "queued" status. -func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { - resp, err := client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ +func Stage(ctx context.Context, client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { + resp, err := client.Put(ctx, stageURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) @@ -29,8 +30,8 @@ func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r Stag } // Download retrieves an image. -func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { - resp, err := client.Get(downloadURL(client, id), nil, &gophercloud.RequestOpts{ +func Download(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DownloadResult) { + resp, err := client.Get(ctx, downloadURL(client, id), nil, &gophercloud.RequestOpts{ KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/results.go similarity index 95% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/results.go index d9632f27b..27f41b7df 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/results.go @@ -3,7 +3,7 @@ package imagedata import ( "io" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // UploadResult is the result of an upload image operation. Call its ExtractErr diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/urls.go similarity index 92% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/urls.go index d9615ba7f..21b7cceb3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata/urls.go @@ -1,6 +1,6 @@ package imagedata -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "images" diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/doc.go similarity index 71% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/doc.go index 775a3630b..8a36af94b 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/doc.go @@ -1,10 +1,10 @@ /* Package imageimport enables management of images import and retrieval of the -Imageservice Import API information. +Image service Import API information. Example to Get an information about the Import API - importInfo, err := imageimport.Get(imagesClient).Extract() + importInfo, err := imageimport.Get(context.TODO(), imagesClient).Extract() if err != nil { panic(err) } @@ -19,7 +19,7 @@ Example to Create a new image import } imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" - err := imageimport.Create(imagesClient, imageID, createOpts).ExtractErr() + err := imageimport.Create(context.TODO(), imagesClient, imageID, createOpts).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/requests.go similarity index 66% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/requests.go index 118d36ea8..186e5bab3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/requests.go @@ -1,6 +1,10 @@ package imageimport -import "github.com/gophercloud/gophercloud" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // ImportMethod represents valid Import API method. type ImportMethod string @@ -14,15 +18,15 @@ const ( ) // Get retrieves Import API information data. -func Get(c *gophercloud.ServiceClient) (r GetResult) { - resp, err := c.Get(infoURL(c), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient) (r GetResult) { + resp, err := c.Get(ctx, infoURL(c), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { - ToImportCreateMap() (map[string]interface{}, error) + ToImportCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new image import. @@ -32,22 +36,22 @@ type CreateOpts struct { } // ToImportCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToImportCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToImportCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } - return map[string]interface{}{"method": b}, nil + return map[string]any{"method": b}, nil } // Create requests the creation of a new image import on the server. -func Create(client *gophercloud.ServiceClient, imageID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, imageID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImportCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/results.go similarity index 95% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/results.go index 2158c20da..d5f1dbab8 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/results.go @@ -1,6 +1,6 @@ package imageimport -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" type commonResult struct { gophercloud.Result diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/urls.go similarity index 87% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/urls.go index 20310eb09..797b00549 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport/urls.go @@ -1,6 +1,6 @@ package imageimport -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "images" diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/doc.go similarity index 73% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/doc.go index 14da9ac90..bf1a6cd5b 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/doc.go @@ -8,7 +8,7 @@ Example to List Images Owner: "a7509e1ae65945fda83f3e52c6296017", } - allPages, err := images.List(imagesClient, listOpts).AllPages() + allPages, err := images.List(imagesClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -29,7 +29,7 @@ Example to Create an Image Visibility: images.ImageVisibilityPrivate, } - image, err := images.Create(imageClient, createOpts) + image, err := images.Create(context.TODO(), imageClient, createOpts) if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Update an Image }, } - image, err := images.Update(imageClient, imageID, updateOpts).Extract() + image, err := images.Update(context.TODO(), imageClient, imageID, updateOpts).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update an Image Example to Delete an Image imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" - err := images.Delete(imageClient, imageID).ExtractErr() + err := images.Delete(context.TODO(), imageClient, imageID).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/requests.go similarity index 83% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/requests.go index 2ab609cbc..a5f61dd61 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/requests.go @@ -1,12 +1,13 @@ package images import ( + "context" "fmt" "net/url" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -144,7 +145,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { // Returns value that can be passed to json.Marshal - ToImageCreateMap() (map[string]interface{}, error) + ToImageCreateMap() (map[string]any, error) } // CreateOpts represents options used to create an image. @@ -191,7 +192,7 @@ type CreateOpts struct { // ToImageCreateMap assembles a request body based on the contents of // a CreateOpts. -func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToImageCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -206,39 +207,39 @@ func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { } // Create implements create image request. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImageCreateMap() if err != nil { r.Err = err return r } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements image delete request. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get implements image get request. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Update implements image updated request. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageUpdateMap() if err != nil { r.Err = err return r } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, MoreHeaders: map[string]string{"Content-Type": "application/openstack-images-v2.1-json-patch"}, }) @@ -252,7 +253,7 @@ type UpdateOptsBuilder interface { // returns value implementing json.Marshaler which when marshaled matches // the patch schema: // http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html - ToImageUpdateMap() ([]interface{}, error) + ToImageUpdateMap() ([]any, error) } // UpdateOpts implements UpdateOpts @@ -260,8 +261,8 @@ type UpdateOpts []Patch // ToImageUpdateMap assembles a request body based on the contents of // UpdateOpts. -func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { - m := make([]interface{}, len(opts)) +func (opts UpdateOpts) ToImageUpdateMap() ([]any, error) { + m := make([]any, len(opts)) for i, patch := range opts { patchJSON := patch.ToImagePatchMap() m[i] = patchJSON @@ -272,7 +273,7 @@ func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { // Patch represents a single update to an existing image. Multiple updates // to an image can be submitted at the same time. type Patch interface { - ToImagePatchMap() map[string]interface{} + ToImagePatchMap() map[string]any } // UpdateVisibility represents an updated visibility property request. @@ -281,8 +282,8 @@ type UpdateVisibility struct { } // ToImagePatchMap assembles a request body based on UpdateVisibility. -func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r UpdateVisibility) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/visibility", "value": r.Visibility, @@ -295,8 +296,8 @@ type ReplaceImageHidden struct { } // ToImagePatchMap assembles a request body based on ReplaceImageHidden. -func (r ReplaceImageHidden) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageHidden) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/os_hidden", "value": r.NewHidden, @@ -309,8 +310,8 @@ type ReplaceImageName struct { } // ToImagePatchMap assembles a request body based on ReplaceImageName. -func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageName) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/name", "value": r.NewName, @@ -323,8 +324,8 @@ type ReplaceImageChecksum struct { } // ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum. -func (r ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageChecksum) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/checksum", "value": r.Checksum, @@ -337,8 +338,8 @@ type ReplaceImageTags struct { } // ToImagePatchMap assembles a request body based on ReplaceImageTags. -func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageTags) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/tags", "value": r.NewTags, @@ -351,8 +352,8 @@ type ReplaceImageMinDisk struct { } // ToImagePatchMap assembles a request body based on ReplaceImageTags. -func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/min_disk", "value": r.NewMinDisk, @@ -365,8 +366,8 @@ type ReplaceImageMinRam struct { } // ToImagePatchMap assembles a request body based on ReplaceImageTags. -func (r ReplaceImageMinRam) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageMinRam) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/min_ram", "value": r.NewMinRam, @@ -379,8 +380,8 @@ type ReplaceImageProtected struct { } // ToImagePatchMap assembles a request body based on ReplaceImageProtected -func (r ReplaceImageProtected) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageProtected) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/protected", "value": r.NewProtected, @@ -404,8 +405,8 @@ type UpdateImageProperty struct { } // ToImagePatchMap assembles a request body based on UpdateImageProperty. -func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { - updateMap := map[string]interface{}{ +func (r UpdateImageProperty) ToImagePatchMap() map[string]any { + updateMap := map[string]any{ "op": r.Op, "path": fmt.Sprintf("/%s", r.Name), } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/results.go similarity index 87% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/results.go index 96fd91a2c..0e0eac21a 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/results.go @@ -7,8 +7,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Image represents an image found in the OpenStack Image service. @@ -20,7 +20,7 @@ type Image struct { Name string `json:"name"` // Status is the image status. It can be "queued" or "active" - // See imageservice/v2/images/type.go + // See image/v2/images/type.go Status ImageStatus `json:"status"` // Tags is a list of image tags. Tags are arbitrarily defined strings @@ -70,7 +70,7 @@ type Image struct { // Properties is a set of key-value pairs, if any, that are associated with // the image. - Properties map[string]interface{} + Properties map[string]any `json:"-"` // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` @@ -102,9 +102,10 @@ func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp - SizeBytes interface{} `json:"size"` - OpenStackImageImportMethods string `json:"openstack-image-import-methods"` - OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` + Properties string `json:"properties"` + SizeBytes any `json:"size"` + OpenStackImageImportMethods string `json:"openstack-image-import-methods"` + OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` } err := json.Unmarshal(b, &s) if err != nil { @@ -120,16 +121,16 @@ func (r *Image) UnmarshalJSON(b []byte) error { case float64: r.SizeBytes = int64(t) default: - return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t) + return fmt.Errorf("unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t) } - // Bundle all other fields into Properties - var result interface{} + // Bundle all other fields into Properties, except for the field named "properties" + var result any err = json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { delete(resultMap, "self") delete(resultMap, "size") delete(resultMap, "openstack-image-import-methods") @@ -137,6 +138,11 @@ func (r *Image) UnmarshalJSON(b []byte) error { r.Properties = gophercloud.RemainingKeys(Image{}, resultMap) } + // Add the "properties" field to the image since it's not included in above step (i.e. in remaining keys) + if s.Properties != "" { + r.Properties["properties"] = s.Properties + } + if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { r.OpenStackImageImportMethods = v } @@ -154,7 +160,7 @@ type commonResult struct { // Extract interprets any commonResult as an Image. func (r commonResult) Extract() (*Image, error) { var s *Image - if v, ok := r.Body.(map[string]interface{}); ok { + if v, ok := r.Body.(map[string]any); ok { for k, h := range r.Header { if strings.ToLower(k) == "openstack-image-import-methods" { for _, s := range h { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/types.go similarity index 93% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/types.go index 147be1992..eedc13a33 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/types.go @@ -13,10 +13,14 @@ const ( // been reserved for an image in the image registry. ImageStatusQueued ImageStatus = "queued" - // ImageStatusSaving denotes that an image’s raw data is currently being + // ImageStatusSaving denotes that an image's raw data is currently being // uploaded to Glance ImageStatusSaving ImageStatus = "saving" + // ImageStatusUploading denotes that an image's raw data is currently being + // uploaded to Glance through the upload process + ImageStatusUploading ImageStatus = "uploading" + // ImageStatusActive denotes an image that is fully available in Glance. ImageStatusActive ImageStatus = "active" diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/urls.go similarity index 94% rename from vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/urls.go index 1780c3c6c..c3007a612 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/image/v2/images/urls.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) // `listURL` is a pure function. `listURL(c)` is a URL for which a GET diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/constants.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/constants.go new file mode 100644 index 000000000..85dff7818 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/constants.go @@ -0,0 +1,7 @@ +package floatingips + +const ( + StatusActive = "ACTIVE" + StatusDown = "DOWN" + StatusError = "ERROR" +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/doc.go new file mode 100644 index 000000000..ab54042a4 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/doc.go @@ -0,0 +1,71 @@ +/* +package floatingips enables management and retrieval of Floating IPs from the +OpenStack Networking service. + +Example to List Floating IPs + + listOpts := floatingips.ListOpts{ + FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", + } + + allPages, err := floatingips.List(networkClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allFIPs, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + panic(err) + } + + for _, fip := range allFIPs { + fmt.Printf("%+v\n", fip) + } + +Example to Create a Floating IP + + createOpts := floatingips.CreateOpts{ + FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", + } + + fip, err := floatingips.Create(context.TODO(), networkingClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Floating IP + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + portID := "76d0a61b-b8e5-490c-9892-4cf674f2bec8" + + updateOpts := floatingips.UpdateOpts{ + PortID: &portID, + } + + fip, err := floatingips.Update(context.TODO(), networkingClient, fipID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Disassociate a Floating IP with a Port + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + + updateOpts := floatingips.UpdateOpts{ + PortID: new(string), + } + + fip, err := floatingips.Update(context.TODO(), networkingClient, fipID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Floating IP + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + err := floatingips.Delete(context.TODO(), networkClient, fipID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package floatingips diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/requests.go new file mode 100644 index 000000000..be8949d69 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -0,0 +1,206 @@ +package floatingips + +import ( + "context" + "fmt" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToFloatingIPListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the floating IP attributes you want to see returned. SortKey allows you to +// sort by a particular network attribute. SortDir sets the direction, and is +// either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + ID string `q:"id"` + Description string `q:"description"` + FloatingNetworkID string `q:"floating_network_id"` + PortID string `q:"port_id"` + FixedIP string `q:"fixed_ip_address"` + FloatingIP string `q:"floating_ip_address"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + RouterID string `q:"router_id"` + Status string `q:"status"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` + RevisionNumber *int `q:"revision_number"` +} + +// ToNetworkListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToFloatingIPListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// floating IP resources. It accepts a ListOpts struct, which allows you to +// filter and sort the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToFloatingIPListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToFloatingIPCreateMap() (map[string]any, error) +} + +// CreateOpts contains all the values needed to create a new floating IP +// resource. The only required fields are FloatingNetworkID and PortID which +// refer to the external network and internal port respectively. +type CreateOpts struct { + Description string `json:"description,omitempty"` + FloatingNetworkID string `json:"floating_network_id" required:"true"` + FloatingIP string `json:"floating_ip_address,omitempty"` + PortID string `json:"port_id,omitempty"` + FixedIP string `json:"fixed_ip_address,omitempty"` + SubnetID string `json:"subnet_id,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` +} + +// ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder +// interface +func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "floatingip") +} + +// Create accepts a CreateOpts struct and uses the values provided to create a +// new floating IP resource. You can create floating IPs on external networks +// only. If you provide a FloatingNetworkID which refers to a network that is +// not external (i.e. its `router:external' attribute is False), the operation +// will fail and return a 400 error. +// +// If you do not specify a FloatingIP address value, the operation will +// automatically allocate an available address for the new resource. If you do +// choose to specify one, it must fall within the subnet range for the external +// network - otherwise the operation returns a 400 error. If the FloatingIP +// address is already in use, the operation returns a 409 error code. +// +// You can associate the new resource with an internal port by using the PortID +// field. If you specify a PortID that is not valid, the operation will fail and +// return 404 error code. +// +// You must also configure an IP address for the port associated with the PortID +// you have provided - this is what the FixedIP refers to: an IP fixed to a +// port. Because a port might be associated with multiple IP addresses, you can +// use the FixedIP field to associate a particular IP address rather than have +// the API assume for you. If you specify an IP address that is not valid, the +// operation will fail and return a 400 error code. If the PortID and FixedIP +// are already associated with another resource, the operation will fail and +// returns a 409 error code. +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToFloatingIPCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves a particular floating IP resource based on its unique ID. +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToFloatingIPUpdateMap() (map[string]any, error) +} + +// UpdateOpts contains the values used when updating a floating IP resource. The +// only value that can be updated is which internal port the floating IP is +// linked to. To associate the floating IP with a new internal port, provide its +// ID. To disassociate the floating IP from all ports, provide an empty string. +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + PortID *string `json:"port_id,omitempty"` + FixedIP string `json:"fixed_ip_address,omitempty"` + + // RevisionNumber implements extension:standard-attr-revisions. If != "" it + // will set revision_number=%s. If the revision number does not match, the + // update will fail. + RevisionNumber *int `json:"-" h:"If-Match"` +} + +// ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder +// interface +func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]any, error) { + b, err := gophercloud.BuildRequestBody(opts, "floatingip") + if err != nil { + return nil, err + } + + if m := b["floatingip"].(map[string]any); m["port_id"] == "" { + m["port_id"] = nil + } + + return b, nil +} + +// Update allows floating IP resources to be updated. Currently, the only way to +// "update" a floating IP is to associate it with a new internal port, or +// disassociated it from all ports. See UpdateOpts for instructions of how to +// do this. +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToFloatingIPUpdateMap() + if err != nil { + r.Err = err + return + } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + r.Err = err + return + } + for k := range h { + if k == "If-Match" { + h[k] = fmt.Sprintf("revision_number=%s", h[k]) + } + } + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will permanently delete a particular floating IP resource. Please +// ensure this is what you want - you can also disassociate the IP from existing +// internal ports. +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/results.go new file mode 100644 index 000000000..89fd5cf9c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -0,0 +1,184 @@ +package floatingips + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// FloatingIP represents a floating IP resource. A floating IP is an external +// IP address that is mapped to an internal port and, optionally, a specific +// IP address on a private network. In other words, it enables access to an +// instance on a private network from an external network. For this reason, +// floating IPs can only be defined on networks where the `router:external' +// attribute (provided by the external network extension) is set to True. +type FloatingIP struct { + // ID is the unique identifier for the floating IP instance. + ID string `json:"id"` + + // Description for the floating IP instance. + Description string `json:"description"` + + // FloatingNetworkID is the UUID of the external network where the floating + // IP is to be created. + FloatingNetworkID string `json:"floating_network_id"` + + // FloatingIP is the address of the floating IP on the external network. + FloatingIP string `json:"floating_ip_address"` + + // PortID is the UUID of the port on an internal network that is associated + // with the floating IP. + PortID string `json:"port_id"` + + // FixedIP is the specific IP address of the internal port which should be + // associated with the floating IP. + FixedIP string `json:"fixed_ip_address"` + + // TenantID is the project owner of the floating IP. Only admin users can + // specify a project identifier other than its own. + TenantID string `json:"tenant_id"` + + // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of + // the floating ip last changed, and when it was created. + UpdatedAt time.Time `json:"-"` + CreatedAt time.Time `json:"-"` + + // ProjectID is the project owner of the floating IP. + ProjectID string `json:"project_id"` + + // Status is the condition of the API resource. + Status string `json:"status"` + + // RouterID is the ID of the router used for this floating IP. + RouterID string `json:"router_id"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` +} + +func (r *FloatingIP) UnmarshalJSON(b []byte) error { + type tmp FloatingIP + + // Support for older neutron time format + var s1 struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = FloatingIP(s1.tmp) + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for newer neutron time format + var s2 struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) + if err != nil { + return err + } + + *r = FloatingIP(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will extract a FloatingIP resource from a result. +func (r commonResult) Extract() (*FloatingIP, error) { + var s FloatingIP + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v any) error { + return r.ExtractIntoStructPtr(v, "floatingip") +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a FloatingIP. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a FloatingIP. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a FloatingIP. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of an update operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// FloatingIPPage is the page returned by a pager when traversing over a +// collection of floating IPs. +type FloatingIPPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of floating IPs has +// reached the end of a page and the pager seeks to traverse over a new one. +// In order to do this, it needs to construct the next page's URL. +func (r FloatingIPPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"floatingips_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a FloatingIPPage struct is empty. +func (r FloatingIPPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + is, err := ExtractFloatingIPs(r) + return len(is) == 0, err +} + +// ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage +// struct, and extracts the elements into a slice of FloatingIP structs. In +// other words, a generic collection is mapped into a relevant slice. +func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { + var s struct { + FloatingIPs []FloatingIP `json:"floatingips"` + } + err := (r.(FloatingIPPage)).ExtractInto(&s) + return s.FloatingIPs, err +} + +func ExtractFloatingIPsInto(r pagination.Page, v any) error { + return r.(FloatingIPPage).ExtractIntoSlicePtr(v, "floatingips") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/urls.go new file mode 100644 index 000000000..4352321a3 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/urls.go @@ -0,0 +1,13 @@ +package floatingips + +import "github.com/gophercloud/gophercloud/v2" + +const resourcePath = "floatingips" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/doc.go similarity index 78% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/doc.go index 7d8bbcaac..298d61712 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/doc.go @@ -8,7 +8,7 @@ Example to List Security Groups TenantID: "966b3c7d36a24facaf20b7e458bf2192", } - allPages, err := groups.List(networkClient, listOpts).AllPages() + allPages, err := groups.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -29,7 +29,7 @@ Example to Create a Security Group Description: "A Security Group", } - group, err := groups.Create(networkClient, createOpts).Extract() + group, err := groups.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Update a Security Group Name: "new_name", } - group, err := groups.Update(networkClient, groupID, updateOpts).Extract() + group, err := groups.Update(context.TODO(), networkClient, groupID, updateOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Security Group Example to Delete a Security Group groupID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := groups.Delete(networkClient, groupID).ExtractErr() + err := groups.Delete(context.TODO(), networkClient, groupID).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/requests.go similarity index 63% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/requests.go index 645f87cfa..95f7bfabd 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/requests.go @@ -1,8 +1,11 @@ package groups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "context" + "fmt" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through @@ -11,20 +14,21 @@ import ( // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - Description string `q:"description"` - Stateful *bool `q:"stateful"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tags string `q:"tags"` - TagsAny string `q:"tags-any"` - NotTags string `q:"not-tags"` - NotTagsAny string `q:"not-tags-any"` + ID string `q:"id"` + Name string `q:"name"` + Description string `q:"description"` + Stateful *bool `q:"stateful"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` + RevisionNumber *int `q:"revision_number"` } // List returns a Pager which allows you to iterate over a collection of @@ -44,7 +48,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSecGroupCreateMap() (map[string]interface{}, error) + ToSecGroupCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new security group. @@ -68,19 +72,19 @@ type CreateOpts struct { } // ToSecGroupCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group") } // Create is an operation which provisions a new security group with default // security group rules for the IPv4 and IPv6 ether types. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -88,7 +92,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSecGroupUpdateMap() (map[string]interface{}, error) + ToSecGroupUpdateMap() (map[string]any, error) } // UpdateOpts contains all the values needed to update an existing security @@ -102,39 +106,55 @@ type UpdateOpts struct { // Stateful indicates if the security group is stateful or stateless. Stateful *bool `json:"stateful,omitempty"` + + // RevisionNumber implements extension:standard-attr-revisions. If != "" it + // will set revision_number=%s. If the revision number does not match, the + // update will fail. + RevisionNumber *int `json:"-" h:"If-Match"` } // ToSecGroupUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group") } // Update is an operation which updates an existing security group. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecGroupUpdateMap() if err != nil { r.Err = err return } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + r.Err = err + return + } + for k := range h { + if k == "If-Match" { + h[k] = fmt.Sprintf("revision_number=%s", h[k]) + } + } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/results.go similarity index 93% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/results.go index f892bcb5c..6a8a16fa8 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/results.go @@ -4,9 +4,9 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SecGroup represents a container for security group rules. @@ -41,6 +41,9 @@ type SecGroup struct { // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` } func (r *SecGroup) UnmarshalJSON(b []byte) error { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/urls.go similarity index 83% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/urls.go index 104cbcc55..8c445868a 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups/urls.go @@ -1,6 +1,6 @@ package groups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const rootPath = "security-groups" diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/doc.go similarity index 79% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/doc.go index bf66dc8b4..fde5e1305 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/doc.go @@ -8,7 +8,7 @@ Example to List Security Groups Rules Protocol: "tcp", } - allPages, err := rules.List(networkClient, listOpts).AllPages() + allPages, err := rules.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -34,7 +34,7 @@ Example to Create a Security Group Rule SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", } - rule, err := rules.Create(networkClient, createOpts).Extract() + rule, err := rules.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Create a Security Group Rule Example to Delete a Security Group Rule ruleID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := rules.Delete(networkClient, ruleID).ExtractErr() + err := rules.Delete(context.TODO(), networkClient, ruleID).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/requests.go similarity index 59% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/requests.go index 480b1510c..edd253f03 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/requests.go @@ -1,44 +1,66 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToSecGroupListQuery() (string, error) +} + // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the security group rule attributes you want to see returned. SortKey allows // you to sort by a particular network attribute. SortDir sets the direction, // and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Direction string `q:"direction"` - EtherType string `q:"ethertype"` - ID string `q:"id"` - Description string `q:"description"` - PortRangeMax int `q:"port_range_max"` - PortRangeMin int `q:"port_range_min"` - Protocol string `q:"protocol"` - RemoteGroupID string `q:"remote_group_id"` - RemoteIPPrefix string `q:"remote_ip_prefix"` - SecGroupID string `q:"security_group_id"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + Direction string `q:"direction"` + EtherType string `q:"ethertype"` + ID string `q:"id"` + Description string `q:"description"` + PortRangeMax int `q:"port_range_max"` + PortRangeMin int `q:"port_range_min"` + Protocol string `q:"protocol"` + RemoteAddressGroupID string `q:"remote_address_group_id"` + RemoteGroupID string `q:"remote_group_id"` + RemoteIPPrefix string `q:"remote_ip_prefix"` + SecGroupID string `q:"security_group_id"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + RevisionNumber *int `q:"revision_number"` +} + +// ToSecGroupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSecGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(&opts) + if err != nil { + return "", err + } + return q.String(), nil } // List returns a Pager which allows you to iterate over a collection of // security group rules. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToSecGroupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return SecGroupRulePage{pagination.LinkedPageBase{PageResult: r}} }) } @@ -81,7 +103,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSecGroupRuleCreateMap() (map[string]interface{}, error) + ToSecGroupRuleCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new security group @@ -103,7 +125,7 @@ type CreateOpts struct { // The maximum port number in the range that is matched by the security group // rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If - // the protocol is ICMP, this value must be an ICMP type. + // the protocol is ICMP, this value must be an ICMP code. PortRangeMax int `json:"port_range_max,omitempty"` // The minimum port number in the range that is matched by the security group @@ -116,12 +138,16 @@ type CreateOpts struct { // "tcp", "udp", "icmp" or an empty string. Protocol RuleProtocol `json:"protocol,omitempty"` + // The remote address group ID to be associated with this security group rule. + // You can specify either RemoteAddressGroupID, RemoteGroupID, or RemoteIPPrefix + RemoteAddressGroupID string `json:"remote_address_group_id,omitempty"` + // The remote group ID to be associated with this security group rule. You can - // specify either RemoteGroupID or RemoteIPPrefix. + // specify either RemoteAddressGroupID,RemoteGroupID or RemoteIPPrefix. RemoteGroupID string `json:"remote_group_id,omitempty"` // The remote IP prefix to be associated with this security group rule. You can - // specify either RemoteGroupID or RemoteIPPrefix. This attribute matches the + // specify either RemoteAddressGroupID,RemoteGroupID or RemoteIPPrefix. This attribute matches the // specified IP prefix as the source IP address of the IP packet. RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"` @@ -131,34 +157,51 @@ type CreateOpts struct { } // ToSecGroupRuleCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } // Create is an operation which adds a new security group rule and associates it // with an existing security group (whose ID is specified in CreateOpts). -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupRuleCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateBulk is an operation which adds new security group rules and associates them +// with an existing security group (whose ID is specified in CreateOpts). +// As of Dalmatian (2024.2) neutron only allows bulk creation of rules when +// they all belong to the same tenant and security group. +// https://github.com/openstack/neutron/blob/6183792/neutron/db/securitygroups_db.py#L814-L828 +func CreateBulk(ctx context.Context, c *gophercloud.ServiceClient, opts []CreateOpts) (r CreateBulkResult) { + body, err := gophercloud.BuildRequestBody(opts, "security_group_rules") + if err != nil { + r.Err = err + return + } + + resp, err := c.Post(ctx, rootURL(c), body, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group rule based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group rule based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/results.go similarity index 69% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/results.go index 1e65f0627..03696ac20 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/results.go @@ -1,8 +1,11 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SecGroupRule represents a rule to dictate the behaviour of incoming or @@ -42,6 +45,10 @@ type SecGroupRule struct { // "tcp", "udp", "icmp" or an empty string. Protocol string + // The remote address group ID to be associated with this security group rule. + // You can specify either RemoteAddressGroupID, RemoteGroupID, or RemoteIPPrefix + RemoteAddressGroupID string `json:"remote_address_group_id"` + // The remote group ID to be associated with this security group rule. You // can specify either RemoteGroupID or RemoteIPPrefix. RemoteGroupID string `json:"remote_group_id"` @@ -56,6 +63,53 @@ type SecGroupRule struct { // ProjectID is the project owner of this security group rule. ProjectID string `json:"project_id"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` + + // Timestamp when the rule was created + CreatedAt time.Time `json:"-"` + + // Timestamp when the rule was last updated + UpdatedAt time.Time `json:"-"` +} + +func (r *SecGroupRule) UnmarshalJSON(b []byte) error { + type tmp SecGroupRule + + // Support for older neutron time format + var s1 struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = SecGroupRule(s1.tmp) + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for newer neutron time format + var s2 struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) + if err != nil { + return err + } + + *r = SecGroupRule(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil } // SecGroupRulePage is the page returned by a pager when traversing over a @@ -103,6 +157,10 @@ type commonResult struct { gophercloud.Result } +type bulkResult struct { + gophercloud.Result +} + // Extract is a function that accepts a result and extracts a security rule. func (r commonResult) Extract() (*SecGroupRule, error) { var s struct { @@ -112,12 +170,27 @@ func (r commonResult) Extract() (*SecGroupRule, error) { return s.SecGroupRule, err } +// Extract is a function that accepts a result and extracts security rules. +func (r bulkResult) Extract() ([]SecGroupRule, error) { + var s struct { + SecGroupRules []SecGroupRule `json:"security_group_rules"` + } + err := r.ExtractInto(&s) + return s.SecGroupRules, err +} + // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a SecGroupRule. type CreateResult struct { commonResult } +// CreateBulkResult represents the result of a bulk create operation. Call its +// Extract method to interpret it as a slice of SecGroupRules. +type CreateBulkResult struct { + bulkResult +} + // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a SecGroupRule. type GetResult struct { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/urls.go similarity index 84% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/urls.go index a5ede0e89..98c9ea7c2 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules/urls.go @@ -1,6 +1,6 @@ package rules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const rootPath = "security-group-rules" diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/constants.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/constants.go new file mode 100644 index 000000000..1214ce9de --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/constants.go @@ -0,0 +1,8 @@ +package networks + +const ( + StatusActive = "ACTIVE" + StatusBuild = "BUILD" + StatusDown = "DOWN" + StatusError = "ERROR" +) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/doc.go similarity index 82% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/doc.go index 9d1dd5a7e..4f8ce23ae 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/doc.go @@ -14,7 +14,7 @@ Example to List Networks TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", } - allPages, err := networks.List(networkClient, listOpts).AllPages() + allPages, err := networks.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -36,7 +36,7 @@ Example to Create a Network AdminStateUp: &iTrue, } - network, err := networks.Create(networkClient, createOpts).Extract() + network, err := networks.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Network Name: &name, } - network, err := networks.Update(networkClient, networkID, updateOpts).Extract() + network, err := networks.Update(context.TODO(), networkClient, networkID, updateOpts).Extract() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Update a Network Example to Delete a Network networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" - err := networks.Delete(networkClient, networkID).ExtractErr() + err := networks.Delete(context.TODO(), networkClient, networkID).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/requests.go similarity index 72% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/requests.go index 00c2eae77..d4dd64ff9 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/requests.go @@ -1,10 +1,11 @@ package networks import ( + "context" "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -19,22 +20,23 @@ type ListOptsBuilder interface { // by a particular network attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Status string `q:"status"` - Name string `q:"name"` - Description string `q:"description"` - AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - Shared *bool `q:"shared"` - ID string `q:"id"` - Marker string `q:"marker"` - Limit int `q:"limit"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tags string `q:"tags"` - TagsAny string `q:"tags-any"` - NotTags string `q:"not-tags"` - NotTagsAny string `q:"not-tags-any"` + Status string `q:"status"` + Name string `q:"name"` + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Shared *bool `q:"shared"` + ID string `q:"id"` + Marker string `q:"marker"` + Limit int `q:"limit"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` + RevisionNumber *int `q:"revision_number"` } // ToNetworkListQuery formats a ListOpts into a query string. @@ -61,8 +63,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific network based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -70,7 +72,7 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToNetworkCreateMap() (map[string]interface{}, error) + ToNetworkCreateMap() (map[string]any, error) } // CreateOpts represents options used to create a network. @@ -85,7 +87,7 @@ type CreateOpts struct { } // ToNetworkCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToNetworkCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "network") } @@ -96,13 +98,13 @@ func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { // The tenant ID that is contained in the URI is the tenant that creates the // network. An admin user, however, has the option of specifying another tenant // ID in the CreateOpts struct. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNetworkCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -110,7 +112,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToNetworkUpdateMap() (map[string]interface{}, error) + ToNetworkUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a network. @@ -127,13 +129,13 @@ type UpdateOpts struct { } // ToNetworkUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "network") } // Update accepts a UpdateOpts struct and updates an existing network using the // values provided. For more information, see the Create function. -func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToNetworkUpdateMap() if err != nil { r.Err = err @@ -149,7 +151,7 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild h[k] = fmt.Sprintf("revision_number=%s", h[k]) } } - resp, err := c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 201}, }) @@ -158,8 +160,8 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild } // Delete accepts a unique ID and deletes the network associated with it. -func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, networkID), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, networkID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/results.go similarity index 93% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/results.go index 2a020a13f..d4b97c1a2 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { @@ -19,8 +19,8 @@ func (r commonResult) Extract() (*Network, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "network") +func (r commonResult) ExtractInto(v any) error { + return r.ExtractIntoStructPtr(v, "network") } // CreateResult represents the result of a create operation. Call its Extract @@ -172,6 +172,6 @@ func ExtractNetworks(r pagination.Page) ([]Network, error) { return s, err } -func ExtractNetworksInto(r pagination.Page, v interface{}) error { - return r.(NetworkPage).Result.ExtractIntoSlicePtr(v, "networks") +func ExtractNetworksInto(r pagination.Page, v any) error { + return r.(NetworkPage).ExtractIntoSlicePtr(v, "networks") } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/urls.go similarity index 93% rename from vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/urls.go index 4a8fb1dc7..14c352e39 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/urls.go @@ -1,6 +1,6 @@ package networks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("networks", id) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/constants.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/constants.go new file mode 100644 index 000000000..6275839bf --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/constants.go @@ -0,0 +1,8 @@ +package ports + +const ( + StatusActive = "ACTIVE" + StatusBuild = "BUILD" + StatusDown = "DOWN" + StatusError = "ERROR" +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/doc.go new file mode 100644 index 000000000..025ad4fa9 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/doc.go @@ -0,0 +1,73 @@ +/* +Package ports contains functionality for working with Neutron port resources. + +A port represents a virtual switch port on a logical network switch. Virtual +instances attach their interfaces into ports. The logical port also defines +the MAC address and the IP address(es) to be assigned to the interfaces +plugged into them. When IP addresses are associated to a port, this also +implies the port is associated with a subnet, as the IP address was taken +from the allocation pool for a specific subnet. + +Example to List Ports + + listOpts := ports.ListOpts{ + DeviceID: "b0b89efe-82f8-461d-958b-adbf80f50c7d", + } + + allPages, err := ports.List(networkClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allPorts, err := ports.ExtractPorts(allPages) + if err != nil { + panic(err) + } + + for _, port := range allPorts { + fmt.Printf("%+v\n", port) + } + +Example to Create a Port + + createOtps := ports.CreateOpts{ + Name: "private-port", + AdminStateUp: &asu, + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }, + SecurityGroups: &[]string{"foo"}, + AllowedAddressPairs: []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + } + + port, err := ports.Create(context.TODO(), networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Port + + portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" + + updateOpts := ports.UpdateOpts{ + Name: "new_name", + SecurityGroups: &[]string{}, + } + + port, err := ports.Update(context.TODO(), networkClient, portID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Port + + portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" + err := ports.Delete(context.TODO(), networkClient, portID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package ports diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/requests.go new file mode 100644 index 000000000..a9c5c3753 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/requests.go @@ -0,0 +1,248 @@ +package ports + +import ( + "context" + "fmt" + "net/url" + "slices" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToPortListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the port attributes you want to see returned. SortKey allows you to sort +// by a particular port attribute. SortDir sets the direction, and is either +// `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + Status string `q:"status"` + Name string `q:"name"` + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + NetworkID string `q:"network_id"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + DeviceOwner string `q:"device_owner"` + MACAddress string `q:"mac_address"` + ID string `q:"id"` + DeviceID string `q:"device_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` + RevisionNumber *int `q:"revision_number"` + SecurityGroups []string `q:"security_groups"` + FixedIPs []FixedIPOpts +} + +type FixedIPOpts struct { + IPAddress string + IPAddressSubstr string + SubnetID string +} + +func (f FixedIPOpts) toParams() []string { + var res []string + if f.IPAddress != "" { + res = append(res, fmt.Sprintf("ip_address=%s", f.IPAddress)) + } + if f.IPAddressSubstr != "" { + res = append(res, fmt.Sprintf("ip_address_substr=%s", f.IPAddressSubstr)) + } + if f.SubnetID != "" { + res = append(res, fmt.Sprintf("subnet_id=%s", f.SubnetID)) + } + return res +} + +// ToPortListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPortListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + params := q.Query() + for _, fixedIP := range opts.FixedIPs { + for _, fixedIPParam := range fixedIP.toParams() { + params.Add("fixed_ips", fixedIPParam) + } + } + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// ports. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those ports that are owned by the tenant +// who submits the request, unless the request is submitted by a user with +// administrative rights. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToPortListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PortPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves a specific port based on its unique ID. +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPortCreateMap() (map[string]any, error) +} + +// CreateOpts represents the attributes used when creating a new port. +type CreateOpts struct { + NetworkID string `json:"network_id" required:"true"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + MACAddress string `json:"mac_address,omitempty"` + FixedIPs any `json:"fixed_ips,omitempty"` + DeviceID string `json:"device_id,omitempty"` + DeviceOwner string `json:"device_owner,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` + AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` + PropagateUplinkStatus *bool `json:"propagate_uplink_status,omitempty"` + ValueSpecs *map[string]string `json:"value_specs,omitempty"` +} + +// ToPortCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToPortCreateMap() (map[string]any, error) { + body, err := gophercloud.BuildRequestBody(opts, "port") + if err != nil { + return nil, err + } + + return AddValueSpecs(body) +} + +// AddValueSpecs expands the 'value_specs' object and removes 'value_specs' +// from the request body. It will return error if the value specs would overwrite +// an existing field or contains forbidden keys. +func AddValueSpecs(body map[string]any) (map[string]any, error) { + // Banned the same as in heat. See https://github.com/openstack/heat/blob/dd7319e373b88812cb18897f742b5196a07227ea/heat/engine/resources/openstack/neutron/neutron.py#L59 + bannedKeys := []string{"shared", "tenant_id"} + port := body["port"].(map[string]any) + + if port["value_specs"] != nil { + for k, v := range port["value_specs"].(map[string]any) { + if slices.Contains(bannedKeys, k) { + return nil, fmt.Errorf("forbidden key in value_specs: %s", k) + } + if _, ok := port[k]; ok { + return nil, fmt.Errorf("value_specs would overwrite key: %s", k) + } + port[k] = v + } + delete(port, "value_specs") + } + body["port"] = port + + return body, nil +} + +// Create accepts a CreateOpts struct and creates a new network using the values +// provided. You must remember to provide a NetworkID value. +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPortCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPortUpdateMap() (map[string]any, error) +} + +// UpdateOpts represents the attributes used when updating an existing port. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + FixedIPs any `json:"fixed_ips,omitempty"` + DeviceID *string `json:"device_id,omitempty"` + DeviceOwner *string `json:"device_owner,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` + AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` + PropagateUplinkStatus *bool `json:"propagate_uplink_status,omitempty"` + ValueSpecs *map[string]string `json:"value_specs,omitempty"` + MACAddress *string `json:"mac_address,omitempty"` + + // RevisionNumber implements extension:standard-attr-revisions. If != "" it + // will set revision_number=%s. If the revision number does not match, the + // update will fail. + RevisionNumber *int `json:"-" h:"If-Match"` +} + +// ToPortUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToPortUpdateMap() (map[string]any, error) { + body, err := gophercloud.BuildRequestBody(opts, "port") + if err != nil { + return nil, err + } + return AddValueSpecs(body) +} + +// Update accepts a UpdateOpts struct and updates an existing port using the +// values provided. +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPortUpdateMap() + if err != nil { + r.Err = err + return + } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + r.Err = err + return + } + for k := range h { + if k == "If-Match" { + h[k] = fmt.Sprintf("revision_number=%s", h[k]) + } + } + resp, err := c.Put(ctx, updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{200, 201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete accepts a unique ID and deletes the port associated with it. +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/results.go new file mode 100644 index 000000000..83cc94138 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/results.go @@ -0,0 +1,206 @@ +package ports + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a port resource. +func (r commonResult) Extract() (*Port, error) { + var s Port + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v any) error { + return r.ExtractIntoStructPtr(v, "port") +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Port. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Port. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Port. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// IP is a sub-struct that represents an individual IP. +type IP struct { + SubnetID string `json:"subnet_id,omitempty"` + IPAddress string `json:"ip_address,omitempty"` +} + +// AddressPair contains the IP Address and the MAC address. +type AddressPair struct { + IPAddress string `json:"ip_address,omitempty"` + MACAddress string `json:"mac_address,omitempty"` +} + +// Port represents a Neutron port. See package documentation for a top-level +// description of what this is. +type Port struct { + // UUID for the port. + ID string `json:"id"` + + // Network that this port is associated with. + NetworkID string `json:"network_id"` + + // Human-readable name for the port. Might not be unique. + Name string `json:"name"` + + // Describes the port. + Description string `json:"description"` + + // Administrative state of port. If false (down), port does not forward + // packets. + AdminStateUp bool `json:"admin_state_up"` + + // Indicates whether network is currently operational. Possible values include + // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional + // values. + Status string `json:"status"` + + // Mac address to use on this port. + MACAddress string `json:"mac_address"` + + // Specifies IP addresses for the port thus associating the port itself with + // the subnets where the IP addresses are picked from + FixedIPs []IP `json:"fixed_ips"` + + // TenantID is the project owner of the port. + TenantID string `json:"tenant_id"` + + // ProjectID is the project owner of the port. + ProjectID string `json:"project_id"` + + // Identifies the entity (e.g.: dhcp agent) using this port. + DeviceOwner string `json:"device_owner"` + + // Specifies the IDs of any security groups associated with a port. + SecurityGroups []string `json:"security_groups"` + + // Identifies the device (e.g., virtual server) using this port. + DeviceID string `json:"device_id"` + + // Identifies the list of IP addresses the port will recognize/accept + AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` + + // PropagateUplinkStatus enables/disables propagate uplink status on the port. + PropagateUplinkStatus bool `json:"propagate_uplink_status"` + + // RevisionNumber optionally set via extensions/standard-attr-revisions + RevisionNumber int `json:"revision_number"` + + // Timestamp when the port was created + CreatedAt time.Time `json:"created_at"` + + // Timestamp when the port was last updated + UpdatedAt time.Time `json:"updated_at"` +} + +func (r *Port) UnmarshalJSON(b []byte) error { + type tmp Port + + // Support for older neutron time format + var s1 struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = Port(s1.tmp) + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for newer neutron time format + var s2 struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) + if err != nil { + return err + } + + *r = Port(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil +} + +// PortPage is the page returned by a pager when traversing over a collection +// of network ports. +type PortPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of ports has reached +// the end of a page and the pager seeks to traverse over a new one. In order +// to do this, it needs to construct the next page's URL. +func (r PortPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"ports_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PortPage struct is empty. +func (r PortPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + is, err := ExtractPorts(r) + return len(is) == 0, err +} + +// ExtractPorts accepts a Page struct, specifically a PortPage struct, +// and extracts the elements into a slice of Port structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractPorts(r pagination.Page) ([]Port, error) { + var s []Port + err := ExtractPortsInto(r, &s) + return s, err +} + +func ExtractPortsInto(r pagination.Page, v any) error { + return r.(PortPage).ExtractIntoSlicePtr(v, "ports") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/urls.go new file mode 100644 index 000000000..e52f44f65 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/urls.go @@ -0,0 +1,31 @@ +package ports + +import "github.com/gophercloud/gophercloud/v2" + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("ports", id) +} + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("ports") +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go similarity index 56% rename from vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go index 40080f7af..f219c0bf4 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go @@ -6,9 +6,7 @@ import ( "strings" ) -// BaseEndpoint will return a URL without the /vX.Y -// portion of the URL. -func BaseEndpoint(endpoint string) (string, error) { +func parseEndpoint(endpoint string, includeVersion bool) (string, error) { u, err := url.Parse(endpoint) if err != nil { return "", err @@ -21,8 +19,23 @@ func BaseEndpoint(endpoint string) (string, error) { if version := versionRe.FindString(path); version != "" { versionIndex := strings.Index(path, version) + if includeVersion { + versionIndex += len(version) + } u.Path = path[:versionIndex] } return u.String(), nil } + +// BaseEndpoint will return a URL without the /vX.Y +// portion of the URL. +func BaseEndpoint(endpoint string) (string, error) { + return parseEndpoint(endpoint, false) +} + +// BaseVersionedEndpoint will return a URL with the /vX.Y portion of the URL, +// if present, but without a project ID or similar +func BaseVersionedEndpoint(endpoint string) (string, error) { + return parseEndpoint(endpoint, true) +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go similarity index 68% rename from vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go index 27da19f91..b9298ad84 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go @@ -1,10 +1,11 @@ package utils import ( + "context" "fmt" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Version is a supported API version, corresponding to a vN package within the appropriate service. @@ -20,10 +21,14 @@ var goodStatus = map[string]bool{ "stable": true, } -// ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's -// published versions. -// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint. -func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { +// ChooseVersion queries the base endpoint of an API to choose the identity service version. +// It will pick a version among the recognized, taking into account the priority and avoiding +// experimental alternatives from the published versions. However, if the client specifies a full +// endpoint that is among the recognized versions, it will be used regardless of priority. +// It returns the highest-Priority Version, OR exact match with client endpoint, +// among the alternatives that are provided, as well as its corresponding endpoint. +func ChooseVersion(ctx context.Context, client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { + // TODO(stephenfin): This could be removed since we can accomplish this with GetServiceVersions now. type linkResp struct { Href string `json:"href"` Rel string `json:"rel"` @@ -59,7 +64,7 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* } var resp response - _, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{ + _, err := client.Request(ctx, "GET", client.IdentityBase, &gophercloud.RequestOpts{ JSONResponse: &resp, OkCodes: []int{200, 300}, }) @@ -84,7 +89,7 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* // Prefer a version that exactly matches the provided endpoint. if href == identityEndpoint { if href == "" { - return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase) + return nil, "", fmt.Errorf("endpoint missing in version %s response from %s", value.ID, client.IdentityBase) } return version, href, nil } @@ -101,10 +106,10 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* } if highest == nil { - return nil, "", fmt.Errorf("No supported version available from endpoint %s", client.IdentityBase) + return nil, "", fmt.Errorf("no supported version available from endpoint %s", client.IdentityBase) } if endpoint == "" { - return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, client.IdentityBase) + return nil, "", fmt.Errorf("endpoint missing in version %s response from %s", highest.ID, client.IdentityBase) } return highest, endpoint, nil diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/discovery.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/discovery.go new file mode 100644 index 000000000..86d1d14c3 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/discovery.go @@ -0,0 +1,372 @@ +package utils + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/gophercloud/gophercloud/v2" +) + +type Status string + +const ( + StatusCurrent Status = "CURRENT" + StatusSupported Status = "SUPPORTED" + StatusDeprecated Status = "DEPRECATED" + StatusExperimental Status = "EXPERIMENTAL" + StatusUnknown Status = "" +) + +// SupportedVersion stores a normalized form of the API version data. It handles APIs that +// support microversions as well as those that do not. +type SupportedVersion struct { + // Major is the major version number of the API + Major int + // Minor is the minor version number of the API + Minor int + // Status is the status of the API + Status Status + SupportedMicroversions +} + +// SupportedMicroversions stores a normalized form of the maximum and minimum API microversions +// supported by a given service. +type SupportedMicroversions struct { + // MaxMajor is the major version number of the maximum supported API microversion + MaxMajor int + // MaxMinor is the minor version number of the maximum supported API microversion + MaxMinor int + // MinMajor is the major version number of the minimum supported API microversion + MinMajor int + // MinMinor is the minor version number of the minimum supported API microversion + MinMinor int +} + +type version struct { + ID string `json:"id"` + Status string `json:"status"` + Version string `json:"version,omitempty"` + MaxVersion string `json:"max_version,omitempty"` + MinVersion string `json:"min_version"` +} + +type response struct { + Versions []version `json:"-"` +} + +func (r *response) UnmarshalJSON(in []byte) error { + // intermediateResponse is an intermediate struct that allows us to offload the difference + // between a single version document and a multi-version document to the json parser and + // only focus on differences in the latter + type intermediateResponse struct { + ID string `json:"id"` + Version *version `json:"version"` + Versions *json.RawMessage `json:"versions"` + } + + data := intermediateResponse{} + if err := json.Unmarshal(in, &data); err != nil { + return err + } + + // case 1: we have a single enveloped version object + // + // this is the approach used by Manila for single version responses + if data.Version != nil { + r.Versions = []version{*data.Version} + return nil + } + + // case 2: we have an singly enveloped array of version objects + // + // this is the approach used by nova, cinder and glance, among others, for multi-version + // responses + if data.Versions != nil { + var versionArr []version + if err := json.Unmarshal(*data.Versions, &versionArr); err == nil { + r.Versions = versionArr + return nil + } + } + + // case 3: we have an doubly enveloped array of version objects + // + // this is the approach used by keystone and barbican, among others, for multi-version + // responses + if data.Versions != nil { + type values struct { + Values []version `json:"values"` + } + + var valuesObj values + if err := json.Unmarshal(*data.Versions, &valuesObj); err == nil { + r.Versions = valuesObj.Values + return nil + } + } + + // case 4: we have a single unenveloped version object + // + // this is the approach used by most other services for single version responses + if data.ID != "" { + r.Versions = []version{{ID: data.ID}} + return nil + } + + return fmt.Errorf("failed to unmarshal versions document: %s", in) +} + +func extractVersion(endpointURL string) (int, int, error) { + u, err := url.Parse(endpointURL) + if err != nil { + return 0, 0, err + } + + parts := strings.Split(strings.TrimRight(u.Path, "/"), "/") + if len(parts) == 0 { + return 0, 0, fmt.Errorf("expected path with version, got: %s", u.Path) + } + + // first, check the nth path element for a version string + if majorVersion, minorVersion, err := ParseVersion(parts[len(parts)-1]); err == nil { + return majorVersion, minorVersion, nil + } + + // if there are no more parts, quit + if len(parts) == 1 { + // we don't return the error message directly since it might be misleading: at this point + // we might have a *malformed* version identifier rather than *no* version identifier + return 0, 0, fmt.Errorf("failed to infer version from path: %s", u.Path) + } + + // the guidelines say we should use the currently scoped project_id from the token, but we + // don't necessarily have a token yet so we speculatively look at the (n-1)th path element + // (but only that) just as keystoneauth does + // + // https://github.com/openstack/keystoneauth/blob/master/keystoneauth1/discover.py#L1534-L1545 + if majorVersion, minorVersion, err := ParseVersion(parts[len(parts)-1]); err == nil { + return majorVersion, minorVersion, err + } + + // once again, we don't return the error message directly + return 0, 0, fmt.Errorf("failed to infer version from path: %s", u.Path) +} + +// GetServiceVersions returns the versions supported by the ServiceClient Endpoint. +// If the endpoint resolves to an unversioned discovery API, this should return one or more supported versions. +// If the endpoint resolves to a versioned discovery API, this should return exactly one supported version. +func GetServiceVersions(ctx context.Context, client *gophercloud.ProviderClient, endpointURL string, discoverVersions bool) ([]SupportedVersion, error) { + var supportedVersions []SupportedVersion + var endpointVersion *SupportedVersion + + if majorVersion, minorVersion, err := extractVersion(endpointURL); err == nil { + endpointVersion = &SupportedVersion{Major: majorVersion, Minor: minorVersion} + if !discoverVersions { + return append(supportedVersions, *endpointVersion), nil + } + } + + var resp response + _, err := client.Request(ctx, "GET", endpointURL, &gophercloud.RequestOpts{ + JSONResponse: &resp, + OkCodes: []int{200, 300}, + }) + if err != nil { + // we weren't able to find a discovery document but we have version information from the URL + if endpointVersion != nil { + return append(supportedVersions, *endpointVersion), nil + } + return supportedVersions, err + } + + versions := resp.Versions + + for _, version := range versions { + majorVersion, minorVersion, err := ParseVersion(version.ID) + if err != nil { + return supportedVersions, err + } + + status, err := ParseStatus(version.Status) + if err != nil { + return supportedVersions, err + } + + supportedVersion := SupportedVersion{ + Major: majorVersion, + Minor: minorVersion, + Status: status, + } + + // Only normalize the microversions if there are microversions to normalize + if (version.Version != "" || version.MaxVersion != "") && version.MinVersion != "" { + supportedVersion.MinMajor, supportedVersion.MinMinor, err = ParseMicroversion(version.MinVersion) + if err != nil { + return supportedVersions, err + } + + maxVersion := version.Version + if maxVersion == "" { + maxVersion = version.MaxVersion + } + supportedVersion.MaxMajor, supportedVersion.MaxMinor, err = ParseMicroversion(maxVersion) + if err != nil { + return supportedVersions, err + } + } + + supportedVersions = append(supportedVersions, supportedVersion) + } + + sort.Slice(supportedVersions, func(i, j int) bool { + return supportedVersions[i].Major > supportedVersions[j].Major || (supportedVersions[i].Major == supportedVersions[j].Major && + supportedVersions[i].Minor > supportedVersions[j].Minor) + }) + + return supportedVersions, nil +} + +// GetSupportedMicroversions returns the minimum and maximum microversion that is supported by the ServiceClient Endpoint. +func GetSupportedMicroversions(ctx context.Context, client *gophercloud.ServiceClient) (SupportedMicroversions, error) { + var supportedMicroversions SupportedMicroversions + + supportedVersions, err := GetServiceVersions(ctx, client.ProviderClient, client.Endpoint, true) + if err != nil { + return supportedMicroversions, err + } + + // If there are multiple versions then we were handed an unversioned endpoint. These don't + // provide microversion information, so we need to fail. Likewise, if there are no versions + // then something has gone wrong and we also need to fail. + if len(supportedVersions) > 1 { + return supportedMicroversions, fmt.Errorf("unversioned endpoint with multiple alternatives not supported") + } else if len(supportedVersions) == 0 { + return supportedMicroversions, fmt.Errorf("microversions not supported by endpoint") + } + + supportedMicroversions = supportedVersions[0].SupportedMicroversions + + if supportedMicroversions.MaxMajor == 0 && + supportedMicroversions.MaxMinor == 0 && + supportedMicroversions.MinMajor == 0 && + supportedMicroversions.MinMinor == 0 { + return supportedMicroversions, fmt.Errorf("microversions not supported by endpoint") + } + + return supportedMicroversions, err +} + +// RequireMicroversion checks that the required microversion is supported and +// returns a ServiceClient with the microversion set. +func RequireMicroversion(ctx context.Context, client gophercloud.ServiceClient, required string) (gophercloud.ServiceClient, error) { + supportedMicroversions, err := GetSupportedMicroversions(ctx, &client) + if err != nil { + return client, fmt.Errorf("unable to determine supported microversions: %w", err) + } + supported, err := supportedMicroversions.IsSupported(required) + if err != nil { + return client, err + } + if !supported { + return client, fmt.Errorf("microversion %s not supported. Supported versions: %v", required, supportedMicroversions) + } + client.Microversion = required + return client, nil +} + +// IsSupported checks if a microversion falls in the supported interval. +// It returns true if the version is within the interval and false otherwise. +func (supported SupportedMicroversions) IsSupported(version string) (bool, error) { + // Parse the version X.Y into X and Y integers that are easier to compare. + vMajor, vMinor, err := ParseMicroversion(version) + if err != nil { + return false, err + } + + // Check that the major version number is supported. + if (vMajor < supported.MinMajor) || (vMajor > supported.MaxMajor) { + return false, nil + } + + // Check that the minor version number is supported + if (vMinor <= supported.MaxMinor) && (vMinor >= supported.MinMinor) { + return true, nil + } + + return false, nil +} + +// ParseVersion parsed the version strings v{MAJOR} and v{MAJOR}.{MINOR} into separate integers +// major and minor. +// For example, "v2.1" becomes 2 and 1, "v3" becomes 3 and 0, and "1" becomes 1 and 0. +func ParseVersion(version string) (major, minor int, err error) { + if version == "" { + return 0, 0, fmt.Errorf("empty version provided") + } + + // We use the regex indicated by the version discovery guidelines. + // + // https://specs.openstack.org/openstack/api-sig/guidelines/consuming-catalog/version-discovery.html#inferring-version + // + // However, we diverge slightly since not all services include the 'v' prefix (glares at zaqar) + versionRe := regexp.MustCompile(`^v?(?P[0-9]+)(\.(?P[0-9]+))?$`) + + match := versionRe.FindStringSubmatch(version) + if len(match) == 0 { + return 0, 0, fmt.Errorf("invalid format: %q", version) + } + + major, err = strconv.Atoi(match[versionRe.SubexpIndex("major")]) + if err != nil { + return 0, 0, err + } + + minor = 0 + if match[versionRe.SubexpIndex("minor")] != "" { + minor, err = strconv.Atoi(match[versionRe.SubexpIndex("minor")]) + if err != nil { + return 0, 0, err + } + } + + return major, minor, nil +} + +// ParseMicroversion parses the version major.minor into separate integers major and minor. +// For example, "2.53" becomes 2 and 53. +func ParseMicroversion(version string) (major int, minor int, err error) { + parts := strings.Split(version, ".") + if len(parts) != 2 { + return 0, 0, fmt.Errorf("invalid microversion format: %q", version) + } + major, err = strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, err + } + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, err + } + return major, minor, nil +} + +func ParseStatus(status string) (Status, error) { + switch strings.ToUpper(status) { + case "CURRENT", "STABLE": // keystone uses STABLE instead of CURRENT + return StatusCurrent, nil + case "SUPPORTED": + return StatusSupported, nil + case "DEPRECATED": + return StatusDeprecated, nil + case "": + return StatusUnknown, nil + default: + return StatusUnknown, fmt.Errorf("invalid status: %q", status) + } +} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/http.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go similarity index 76% rename from vendor/github.com/gophercloud/gophercloud/pagination/http.go rename to vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go index 7845cda13..cf188b89b 100644 --- a/vendor/github.com/gophercloud/gophercloud/pagination/http.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go @@ -1,13 +1,14 @@ package pagination import ( + "context" "encoding/json" - "io/ioutil" + "io" "net/http" "net/url" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // PageResult stores the HTTP response that returned the current page of results. @@ -19,10 +20,10 @@ type PageResult struct { // PageResultFrom parses an HTTP response as JSON and returns a PageResult containing the // results, interpreting it as JSON if the content type indicates. func PageResultFrom(resp *http.Response) (PageResult, error) { - var parsedBody interface{} + var parsedBody any defer resp.Body.Close() - rawBody, err := ioutil.ReadAll(resp.Body) + rawBody, err := io.ReadAll(resp.Body) if err != nil { return PageResult{}, err } @@ -41,7 +42,7 @@ func PageResultFrom(resp *http.Response) (PageResult, error) { // PageResultFromParsed constructs a PageResult from an HTTP response that has already had its // body parsed as JSON (and closed). -func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { +func PageResultFromParsed(resp *http.Response, body any) PageResult { return PageResult{ Result: gophercloud.Result{ Body: body, @@ -53,8 +54,8 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { } // Request performs an HTTP request and extracts the http.Response from the result. -func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { - return client.Get(url, nil, &gophercloud.RequestOpts{ +func Request(ctx context.Context, client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { + return client.Get(ctx, url, nil, &gophercloud.RequestOpts{ MoreHeaders: headers, OkCodes: []int{200, 204, 300}, KeepResponseBody: true, diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/linked.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/linked.go similarity index 85% rename from vendor/github.com/gophercloud/gophercloud/pagination/linked.go rename to vendor/github.com/gophercloud/gophercloud/v2/pagination/linked.go index a664e0567..7e4de4f7a 100644 --- a/vendor/github.com/gophercloud/gophercloud/pagination/linked.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/linked.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. @@ -31,10 +31,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { path = current.LinkPath } - submap, ok := current.Body.(map[string]interface{}) + submap, ok := current.Body.(map[string]any) if !ok { err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}" + err.Expected = "map[string]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return "", err } @@ -48,10 +48,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { } if len(path) > 0 { - submap, ok = value.(map[string]interface{}) + submap, ok = value.(map[string]any) if !ok { err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}" + err.Expected = "map[string]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) return "", err } @@ -76,17 +76,17 @@ func (current LinkedPageBase) NextPageURL() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (current LinkedPageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { + if b, ok := current.Body.([]any); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" + err.Expected = "[]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. -func (current LinkedPageBase) GetBody() interface{} { +func (current LinkedPageBase) GetBody() any { return current.Body } diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/marker.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go similarity index 88% rename from vendor/github.com/gophercloud/gophercloud/pagination/marker.go rename to vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go index 52e53bae8..1d101fe2d 100644 --- a/vendor/github.com/gophercloud/gophercloud/pagination/marker.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. @@ -42,17 +42,17 @@ func (current MarkerPageBase) NextPageURL() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (current MarkerPageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { + if b, ok := current.Body.([]any); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" + err.Expected = "[]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. -func (current MarkerPageBase) GetBody() interface{} { +func (current MarkerPageBase) GetBody() any { return current.Body } diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/pager.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go similarity index 82% rename from vendor/github.com/gophercloud/gophercloud/pagination/pager.go rename to vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go index 1dec2703e..2eed649f7 100644 --- a/vendor/github.com/gophercloud/gophercloud/pagination/pager.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go @@ -1,18 +1,19 @@ package pagination import ( + "context" "errors" "fmt" "net/http" "reflect" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var ( // ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist. - ErrPageNotAvailable = errors.New("The requested page does not exist.") + ErrPageNotAvailable = errors.New("the requested page does not exist") ) // Page must be satisfied by the result type of any resource collection. @@ -30,7 +31,7 @@ type Page interface { IsEmpty() (bool, error) // GetBody returns the Page Body. This is used in the `AllPages` method. - GetBody() interface{} + GetBody() any } // Pager knows how to advance through a specific resource collection, one page at a time. @@ -69,8 +70,8 @@ func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { } } -func (p Pager) fetchNextPage(url string) (Page, error) { - resp, err := Request(p.client, p.Headers, url) +func (p Pager) fetchNextPage(ctx context.Context, url string) (Page, error) { + resp, err := Request(ctx, p.client, p.Headers, url) if err != nil { return nil, err } @@ -83,9 +84,10 @@ func (p Pager) fetchNextPage(url string) (Page, error) { return p.createPage(remembered), nil } -// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function. -// Return "false" from the handler to prematurely stop iterating. -func (p Pager) EachPage(handler func(Page) (bool, error)) error { +// EachPage iterates over each page returned by a Pager, yielding one at a time +// to a handler function. Return "false" from the handler to prematurely stop +// iterating. +func (p Pager) EachPage(ctx context.Context, handler func(context.Context, Page) (bool, error)) error { if p.Err != nil { return p.Err } @@ -99,7 +101,7 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { p.firstPage = nil } else { var err error - currentPage, err = p.fetchNextPage(currentURL) + currentPage, err = p.fetchNextPage(ctx, currentURL) if err != nil { return err } @@ -113,7 +115,7 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { return nil } - ok, err := handler(currentPage) + ok, err := handler(ctx, currentPage) if err != nil { return err } @@ -133,17 +135,17 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { // AllPages returns all the pages from a `List` operation in a single page, // allowing the user to retrieve all the pages at once. -func (p Pager) AllPages() (Page, error) { +func (p Pager) AllPages(ctx context.Context) (Page, error) { if p.Err != nil { return nil, p.Err } // pagesSlice holds all the pages until they get converted into as Page Body. - var pagesSlice []interface{} + var pagesSlice []any // body will contain the final concatenated Page body. var body reflect.Value // Grab a first page to ascertain the page body type. - firstPage, err := p.fetchNextPage(p.initialURL) + firstPage, err := p.fetchNextPage(ctx, p.initialURL) if err != nil { return nil, err } @@ -159,21 +161,21 @@ func (p Pager) AllPages() (Page, error) { // store the first page to avoid getting it twice p.firstPage = firstPage - // Switch on the page body type. Recognized types are `map[string]interface{}`, - // `[]byte`, and `[]interface{}`. + // Switch on the page body type. Recognized types are `map[string]any`, + // `[]byte`, and `[]any`. switch pb := firstPage.GetBody().(type) { - case map[string]interface{}: - // key is the map key for the page body if the body type is `map[string]interface{}`. + case map[string]any: + // key is the map key for the page body if the body type is `map[string]any`. var key string // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { - b := page.GetBody().(map[string]interface{}) + err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { + b := page.GetBody().(map[string]any) for k, v := range b { // If it's a linked page, we don't want the `links`, we want the other one. if !strings.HasSuffix(k, "links") { - // check the field's type. we only want []interface{} (which is really []map[string]interface{}) + // check the field's type. we only want []any (which is really []map[string]any) switch vt := v.(type) { - case []interface{}: + case []any: key = k pagesSlice = append(pagesSlice, vt...) } @@ -184,12 +186,12 @@ func (p Pager) AllPages() (Page, error) { if err != nil { return nil, err } - // Set body to value of type `map[string]interface{}` + // Set body to value of type `map[string]any` body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice))) body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) case []byte: // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { + err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { b := page.GetBody().([]byte) pagesSlice = append(pagesSlice, b) // seperate pages with a comma @@ -211,24 +213,24 @@ func (p Pager) AllPages() (Page, error) { // Set body to value of type `bytes`. body = reflect.New(reflect.TypeOf(b)).Elem() body.SetBytes(b) - case []interface{}: + case []any: // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { - b := page.GetBody().([]interface{}) + err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { + b := page.GetBody().([]any) pagesSlice = append(pagesSlice, b...) return true, nil }) if err != nil { return nil, err } - // Set body to value of type `[]interface{}` + // Set body to value of type `[]any` body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice)) for i, s := range pagesSlice { body.Index(i).Set(reflect.ValueOf(s)) } default: err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}/[]byte/[]interface{}" + err.Expected = "map[string]any/[]byte/[]any" err.Actual = fmt.Sprintf("%T", pb) return nil, err } diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/pkg.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pkg.go similarity index 100% rename from vendor/github.com/gophercloud/gophercloud/pagination/pkg.go rename to vendor/github.com/gophercloud/gophercloud/v2/pagination/pkg.go diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/single.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go similarity index 81% rename from vendor/github.com/gophercloud/gophercloud/pagination/single.go rename to vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go index 4251d6491..416621121 100644 --- a/vendor/github.com/gophercloud/gophercloud/pagination/single.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. @@ -17,17 +17,17 @@ func (current SinglePageBase) NextPageURL() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (current SinglePageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { + if b, ok := current.Body.([]any); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" + err.Expected = "[]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the single page's body. This method is needed to satisfy the // Page interface. -func (current SinglePageBase) GetBody() interface{} { +func (current SinglePageBase) GetBody() any { return current.Body } diff --git a/vendor/github.com/gophercloud/gophercloud/params.go b/vendor/github.com/gophercloud/gophercloud/v2/params.go similarity index 81% rename from vendor/github.com/gophercloud/gophercloud/params.go rename to vendor/github.com/gophercloud/gophercloud/v2/params.go index 5abc2c558..ddb61b3d0 100644 --- a/vendor/github.com/gophercloud/gophercloud/params.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/params.go @@ -11,9 +11,11 @@ import ( ) /* -BuildRequestBody builds a map[string]interface from the given `struct`. If -parent is not an empty string, the final map[string]interface returned will -encapsulate the built one. For example: +BuildRequestBody builds a map[string]interface from the given `struct`, or +collection of `structs`. If parent is not an empty string, the final +map[string]interface returned will encapsulate the built one. Parent is +required when passing a list of `structs`. +For example: disk := 1 createOpts := flavors.CreateOpts{ @@ -27,24 +29,47 @@ encapsulate the built one. For example: body, err := gophercloud.BuildRequestBody(createOpts, "flavor") -The above example can be run as-is, however it is recommended to look at how + + opts := []rules.CreateOpts{ + { + Direction: "ingress", + PortRangeMin: 80, + EtherType: rules.EtherType4, + PortRangeMax: 80, + Protocol: "tcp", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + }, + { + Direction: "ingress", + PortRangeMin: 443, + EtherType: rules.EtherType4, + PortRangeMax: 443, + Protocol: "tcp", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + }, + } + + body, err := gophercloud.BuildRequestBody(opts, "security_group_rules") + +The above examples can be run as-is, however it is recommended to look at how BuildRequestBody is used within Gophercloud to more fully understand how it fits within the request process as a whole rather than use it directly as shown above. */ -func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { +func BuildRequestBody(opts any, parent string) (map[string]any, error) { optsValue := reflect.ValueOf(opts) - if optsValue.Kind() == reflect.Ptr { + if optsValue.Kind() == reflect.Pointer { optsValue = optsValue.Elem() } optsType := reflect.TypeOf(opts) - if optsType.Kind() == reflect.Ptr { + if optsType.Kind() == reflect.Pointer { optsType = optsType.Elem() } - optsMap := make(map[string]interface{}) - if optsValue.Kind() == reflect.Struct { + optsMap := make(map[string]any) + switch optsValue.Kind() { + case reflect.Struct: //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) for i := 0; i < optsValue.NumField(); i++ { v := optsValue.Field(i) @@ -79,12 +104,12 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) { xorFieldIsZero = true } else { - if xorField.Kind() == reflect.Ptr { + if xorField.Kind() == reflect.Pointer { xorField = xorField.Elem() } xorFieldIsZero = isZero(xorField) } - if !(zero != xorFieldIsZero) { + if zero == xorFieldIsZero { err := ErrMissingInput{} err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag) err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag) @@ -101,7 +126,7 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) { orFieldIsZero = true } else { - if orField.Kind() == reflect.Ptr { + if orField.Kind() == reflect.Pointer { orField = orField.Elem() } orFieldIsZero = isZero(orField) @@ -120,15 +145,15 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, continue } - if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) { + if v.Kind() == reflect.Slice || (v.Kind() == reflect.Pointer && v.Elem().Kind() == reflect.Slice) { sliceValue := v - if sliceValue.Kind() == reflect.Ptr { + if sliceValue.Kind() == reflect.Pointer { sliceValue = sliceValue.Elem() } for i := 0; i < sliceValue.Len(); i++ { element := sliceValue.Index(i) - if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) { + if element.Kind() == reflect.Struct || (element.Kind() == reflect.Pointer && element.Elem().Kind() == reflect.Struct) { _, err := BuildRequestBody(element.Interface(), "") if err != nil { return nil, err @@ -136,7 +161,7 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, } } } - if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { + if v.Kind() == reflect.Struct || (v.Kind() == reflect.Pointer && v.Elem().Kind() == reflect.Struct) { if zero { //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) if jsonTag != "" { @@ -144,7 +169,7 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { if v.CanSet() { if !v.IsNil() { - if v.Kind() == reflect.Ptr { + if v.Kind() == reflect.Pointer { v.Set(reflect.Zero(v.Type())) } } @@ -180,13 +205,26 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, //fmt.Printf("optsMap: %+v\n", optsMap) if parent != "" { - optsMap = map[string]interface{}{parent: optsMap} + optsMap = map[string]any{parent: optsMap} } //fmt.Printf("optsMap after parent added: %+v\n", optsMap) return optsMap, nil + case reflect.Slice, reflect.Array: + optsMaps := make([]map[string]any, optsValue.Len()) + for i := 0; i < optsValue.Len(); i++ { + b, err := BuildRequestBody(optsValue.Index(i).Interface(), "") + if err != nil { + return nil, err + } + optsMaps[i] = b + } + if parent == "" { + return nil, fmt.Errorf("parent is required when passing an array or a slice") + } + return map[string]any{parent: optsMaps}, nil } - // Return an error if the underlying type of 'opts' isn't a struct. - return nil, fmt.Errorf("Options type is not a struct.") + // Return an error if we can't work with the underlying type of 'opts' + return nil, fmt.Errorf("options type is not a struct, a slice, or an array") } // EnabledState is a convenience type, mostly used in Create and Update @@ -254,7 +292,7 @@ func MaybeInt(original int) *int { /* func isUnderlyingStructZero(v reflect.Value) bool { switch v.Kind() { - case reflect.Ptr: + case reflect.Pointer: return isUnderlyingStructZero(v.Elem()) default: return isZero(v) @@ -267,7 +305,7 @@ var t time.Time func isZero(v reflect.Value) bool { //fmt.Printf("\n\nchecking isZero for value: %+v\n", v) switch v.Kind() { - case reflect.Ptr: + case reflect.Pointer: if v.IsNil() { return true } @@ -282,10 +320,7 @@ func isZero(v reflect.Value) bool { return z case reflect.Struct: if v.Type() == reflect.TypeOf(t) { - if v.Interface().(time.Time).IsZero() { - return true - } - return false + return v.Interface().(time.Time).IsZero() } z := true for i := 0; i < v.NumField(); i++ { @@ -328,14 +363,14 @@ Slice are handled in one of two ways: Baz []int `q:"baz" format="comma-separated"` // E.g. ?baz=1,2 } */ -func BuildQueryString(opts interface{}) (*url.URL, error) { +func BuildQueryString(opts any) (*url.URL, error) { optsValue := reflect.ValueOf(opts) - if optsValue.Kind() == reflect.Ptr { + if optsValue.Kind() == reflect.Pointer { optsValue = optsValue.Elem() } optsType := reflect.TypeOf(opts) - if optsType.Kind() == reflect.Ptr { + if optsType.Kind() == reflect.Pointer { optsType = optsType.Elem() } @@ -355,7 +390,7 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { if !isZero(v) { loop: switch v.Kind() { - case reflect.Ptr: + case reflect.Pointer: v = v.Elem() goto loop case reflect.String: @@ -394,7 +429,7 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { } else { // if the field has a 'required' tag, it can't have a zero-value if requiredTag := f.Tag.Get("required"); requiredTag == "true" { - return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name) + return &url.URL{}, fmt.Errorf("required query parameter [%s] not set", f.Name) } } } @@ -403,7 +438,7 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { return &url.URL{RawQuery: params.Encode()}, nil } // Return an error if the underlying type of 'opts' isn't a struct. - return nil, fmt.Errorf("Options type is not a struct.") + return nil, fmt.Errorf("options type is not a struct") } /* @@ -434,14 +469,14 @@ will be converted into: Untagged fields and fields left at their zero values are skipped. Integers, booleans and string values are supported. */ -func BuildHeaders(opts interface{}) (map[string]string, error) { +func BuildHeaders(opts any) (map[string]string, error) { optsValue := reflect.ValueOf(opts) - if optsValue.Kind() == reflect.Ptr { + if optsValue.Kind() == reflect.Pointer { optsValue = optsValue.Elem() } optsType := reflect.TypeOf(opts) - if optsType.Kind() == reflect.Ptr { + if optsType.Kind() == reflect.Pointer { optsType = optsType.Elem() } @@ -458,7 +493,7 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { // if the field is set, add it to the slice of query pieces if !isZero(v) { - if v.Kind() == reflect.Ptr { + if v.Kind() == reflect.Pointer { v = v.Elem() } switch v.Kind() { @@ -474,7 +509,7 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { } else { // if the field has a 'required' tag, it can't have a zero-value if requiredTag := f.Tag.Get("required"); requiredTag == "true" { - return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name) + return optsMap, fmt.Errorf("required header [%s] not set", f.Name) } } } @@ -483,7 +518,7 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { return optsMap, nil } // Return an error if the underlying type of 'opts' isn't a struct. - return optsMap, fmt.Errorf("Options type is not a struct.") + return optsMap, fmt.Errorf("options type is not a struct") } // IDSliceToQueryString takes a slice of elements and converts them into a query diff --git a/vendor/github.com/gophercloud/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/v2/provider_client.go similarity index 78% rename from vendor/github.com/gophercloud/gophercloud/provider_client.go rename to vendor/github.com/gophercloud/gophercloud/v2/provider_client.go index 7e25882c8..e618744d9 100644 --- a/vendor/github.com/gophercloud/gophercloud/provider_client.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/provider_client.go @@ -6,15 +6,15 @@ import ( "encoding/json" "errors" "io" - "io/ioutil" "net/http" + "slices" "strings" "sync" ) // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.14.1" + DefaultUserAgent = "gophercloud/v2.13.0" DefaultMaxBackoffRetries = 60 ) @@ -82,15 +82,12 @@ type ProviderClient struct { // ReauthFunc is the function used to re-authenticate the user if the request // fails with a 401 HTTP response code. This a needed because there may be multiple // authentication functions for different Identity service versions. - ReauthFunc func() error + ReauthFunc func(context.Context) error // Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client // with the token and reauth func zeroed. Such client can be used to perform reauthorization. Throwaway bool - // Context is the context passed to the HTTP request. - Context context.Context - // Retry backoff func is called when rate limited. RetryBackoffFunc RetryBackoffFunc @@ -269,13 +266,13 @@ func (client *ProviderClient) SetThrowaway(v bool) { // this case, the reauthentication can be skipped if another thread has already // reauthenticated in the meantime. If no previous token is known, an empty // string should be passed instead to force unconditional reauthentication. -func (client *ProviderClient) Reauthenticate(previousToken string) error { +func (client *ProviderClient) Reauthenticate(ctx context.Context, previousToken string) error { if client.ReauthFunc == nil { return nil } if client.reauthmut == nil { - return client.ReauthFunc() + return client.ReauthFunc(ctx) } future := newReauthFuture() @@ -296,7 +293,7 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { // Perform the actual reauthentication. var err error if previousToken == "" || client.TokenID == previousToken { - err = client.ReauthFunc() + err = client.ReauthFunc(ctx) } else { err = nil } @@ -315,13 +312,13 @@ type RequestOpts struct { // JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The // content type of the request will default to "application/json" unless overridden by MoreHeaders. // It's an error to specify both a JSONBody and a RawBody. - JSONBody interface{} + JSONBody any // RawBody contains an io.Reader that will be consumed by the request directly. No content-type // will be set unless one is provided explicitly by MoreHeaders. RawBody io.Reader // JSONResponse, if provided, will be populated with the contents of the response body parsed as // JSON. - JSONResponse interface{} + JSONResponse any // OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If // the response has a different code, an error will be returned. OkCodes []int @@ -331,9 +328,6 @@ type RequestOpts struct { // OmitHeaders specifies the HTTP headers which should be omitted. // OmitHeaders will override MoreHeaders OmitHeaders []string - // ErrorContext specifies the resource error type to return if an error is encountered. - // This lets resources override default error messages based on the response status code. - ErrorContext error // KeepResponseBody specifies whether to keep the HTTP response body. Usually used, when the HTTP // response body is considered for further use. Valid when JSONResponse is nil. KeepResponseBody bool @@ -352,15 +346,15 @@ type requestState struct { var applicationJSON = "application/json" -// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication -// header will automatically be provided. -func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { - return client.doRequest(method, url, options, &requestState{ +// Request performs an HTTP request using the ProviderClient's +// current HTTPClient. An authentication header will automatically be provided. +func (client *ProviderClient) Request(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { + return client.doRequest(ctx, method, url, options, &requestState{ hasReauthenticated: false, }) } -func (client *ProviderClient) doRequest(method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { +func (client *ProviderClient) doRequest(ctx context.Context, method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { var body io.Reader var contentType *string @@ -389,14 +383,10 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts body = options.RawBody } - // Construct the http.Request. - req, err := http.NewRequest(method, url, body) + req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } - if client.Context != nil { - req = req.WithContext(client.Context) - } // Populate the request headers. // Apply options.MoreHeaders and options.OmitHeaders, to give the caller the chance to @@ -432,12 +422,12 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if client.RetryFunc != nil { var e error state.retries = state.retries + 1 - e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + e = client.RetryFunc(ctx, method, url, options, err, state.retries) if e != nil { return nil, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } return nil, err } @@ -448,17 +438,9 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts okc = defaultOkCodes(method) } - // Validate the HTTP response status. - var ok bool - for _, code := range okc { - if resp.StatusCode == code { - ok = true - break - } - } - - if !ok { - body, _ := ioutil.ReadAll(resp.Body) + // Check the response code against the acceptable codes + if !slices.Contains(okc, resp.StatusCode) { + body, _ := io.ReadAll(resp.Body) resp.Body.Close() respErr := ErrUnexpectedResponseCode{ URL: url, @@ -469,16 +451,10 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts ResponseHeader: resp.Header, } - errType := options.ErrorContext switch resp.StatusCode { - case http.StatusBadRequest: - err = ErrDefault400{respErr} - if error400er, ok := errType.(Err400er); ok { - err = error400er.Error400(respErr) - } case http.StatusUnauthorized: if client.ReauthFunc != nil && !state.hasReauthenticated { - err = client.Reauthenticate(prereqtok) + err = client.Reauthenticate(ctx, prereqtok) if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr @@ -487,60 +463,28 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts } if options.RawBody != nil { if seeker, ok := options.RawBody.(io.Seeker); ok { - seeker.Seek(0, 0) + if _, err := seeker.Seek(0, 0); err != nil { + return nil, err + } } } state.hasReauthenticated = true - resp, err = client.doRequest(method, url, options, state) + resp, err = client.doRequest(ctx, method, url, options, state) if err != nil { - switch err.(type) { + switch e := err.(type) { case *ErrUnexpectedResponseCode: - e := &ErrErrorAfterReauthentication{} - e.ErrOriginal = err.(*ErrUnexpectedResponseCode) - return nil, e + err := &ErrErrorAfterReauthentication{} + err.ErrOriginal = e + return nil, err default: - e := &ErrErrorAfterReauthentication{} - e.ErrOriginal = err - return nil, e + err := &ErrErrorAfterReauthentication{} + err.ErrOriginal = e + return nil, err } } return resp, nil } - err = ErrDefault401{respErr} - if error401er, ok := errType.(Err401er); ok { - err = error401er.Error401(respErr) - } - case http.StatusForbidden: - err = ErrDefault403{respErr} - if error403er, ok := errType.(Err403er); ok { - err = error403er.Error403(respErr) - } - case http.StatusNotFound: - err = ErrDefault404{respErr} - if error404er, ok := errType.(Err404er); ok { - err = error404er.Error404(respErr) - } - case http.StatusMethodNotAllowed: - err = ErrDefault405{respErr} - if error405er, ok := errType.(Err405er); ok { - err = error405er.Error405(respErr) - } - case http.StatusRequestTimeout: - err = ErrDefault408{respErr} - if error408er, ok := errType.(Err408er); ok { - err = error408er.Error408(respErr) - } - case http.StatusConflict: - err = ErrDefault409{respErr} - if error409er, ok := errType.(Err409er); ok { - err = error409er.Error409(respErr) - } case http.StatusTooManyRequests, 498: - err = ErrDefault429{respErr} - if error429er, ok := errType.(Err429er); ok { - err = error429er.Error429(respErr) - } - maxTries := client.MaxBackoffRetries if maxTries == 0 { maxTries = DefaultMaxBackoffRetries @@ -550,33 +494,13 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts var e error state.retries = state.retries + 1 - e = f(client.Context, &respErr, err, state.retries) + e = f(ctx, &respErr, err, state.retries) if e != nil { return resp, e } - return client.doRequest(method, url, options, state) - } - case http.StatusInternalServerError: - err = ErrDefault500{respErr} - if error500er, ok := errType.(Err500er); ok { - err = error500er.Error500(respErr) - } - case http.StatusBadGateway: - err = ErrDefault502{respErr} - if error502er, ok := errType.(Err502er); ok { - err = error502er.Error502(respErr) - } - case http.StatusServiceUnavailable: - err = ErrDefault503{respErr} - if error503er, ok := errType.(Err503er); ok { - err = error503er.Error503(respErr) - } - case http.StatusGatewayTimeout: - err = ErrDefault504{respErr} - if error504er, ok := errType.(Err504er); ok { - err = error504er.Error504(respErr) + return client.doRequest(ctx, method, url, options, state) } } @@ -587,12 +511,12 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if err != nil && client.RetryFunc != nil { var e error state.retries = state.retries + 1 - e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + e = client.RetryFunc(ctx, method, url, options, err, state.retries) if e != nil { return resp, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } return resp, err @@ -604,19 +528,19 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts // Don't decode JSON when there is no content if resp.StatusCode == http.StatusNoContent { // read till EOF, otherwise the connection will be closed and cannot be reused - _, err = io.Copy(ioutil.Discard, resp.Body) + _, err = io.Copy(io.Discard, resp.Body) return resp, err } if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { if client.RetryFunc != nil { var e error state.retries = state.retries + 1 - e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + e = client.RetryFunc(ctx, method, url, options, err, state.retries) if e != nil { return resp, e } - return client.doRequest(method, url, options, state) + return client.doRequest(ctx, method, url, options, state) } return nil, err } @@ -626,7 +550,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if !options.KeepResponseBody && options.JSONResponse == nil { defer resp.Body.Close() // read till EOF, otherwise the connection will be closed and cannot be reused - if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { + if _, err := io.Copy(io.Discard, resp.Body); err != nil { return nil, err } } diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/v2/results.go similarity index 90% rename from vendor/github.com/gophercloud/gophercloud/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/results.go index b3ee9d568..3ca191683 100644 --- a/vendor/github.com/gophercloud/gophercloud/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/results.go @@ -28,7 +28,7 @@ provider- or extension-specific information as well. type Result struct { // Body is the payload of the HTTP response from the server. In most cases, // this will be the deserialized JSON structure. - Body interface{} + Body any // StatusCode is the HTTP status code of the original response. Will be // one of the OkCodes defined on the gophercloud.RequestOpts that was @@ -46,7 +46,7 @@ type Result struct { // ExtractInto allows users to provide an object into which `Extract` will extract // the `Result.Body`. This would be useful for OpenStack providers that have // different fields in the response object than OpenStack proper. -func (r Result) ExtractInto(to interface{}) error { +func (r Result) ExtractInto(to any) error { if r.Err != nil { return r.Err } @@ -67,12 +67,12 @@ func (r Result) ExtractInto(to interface{}) error { return err } -func (r Result) extractIntoPtr(to interface{}, label string) error { +func (r Result) extractIntoPtr(to any, label string) error { if label == "" { return r.ExtractInto(&to) } - var m map[string]interface{} + var m map[string]any err := r.ExtractInto(&m) if err != nil { return err @@ -84,7 +84,7 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { } toValue := reflect.ValueOf(to) - if toValue.Kind() == reflect.Ptr { + if toValue.Kind() == reflect.Pointer { toValue = toValue.Elem() } @@ -95,7 +95,7 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) - if mSlice, ok := m[label].([]interface{}); ok { + if mSlice, ok := m[label].([]any); ok { for _, v := range mSlice { // For each iteration of the slice, we create a new struct. // This is to work around a bug where elements of a slice @@ -117,6 +117,9 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { // a struct that is never used, but it's good enough to // trigger the UnmarshalJSON method. for i := 0; i < newType.NumField(); i++ { + if newType.Field(i).Kind() != reflect.Struct { + continue + } s := newType.Field(i).Addr().Interface() // Unmarshal is used rather than NewDecoder to also work @@ -171,7 +174,7 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { } // ExtractIntoStructPtr will unmarshal the Result (r) into the provided -// interface{} (to). +// any (to). // // NOTE: For internal use only // @@ -179,25 +182,34 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { // // If provided, `label` will be filtered out of the response // body prior to `r` being unmarshalled into `to`. -func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { +func (r Result) ExtractIntoStructPtr(to any, label string) error { if r.Err != nil { return r.Err } + if to == nil { + return fmt.Errorf("expected pointer, got %T", to) + } + t := reflect.TypeOf(to) - if k := t.Kind(); k != reflect.Ptr { - return fmt.Errorf("Expected pointer, got %v", k) + if k := t.Kind(); k != reflect.Pointer { + return fmt.Errorf("expected pointer, got %v", k) } + + if reflect.ValueOf(to).IsNil() { + return fmt.Errorf("expected pointer, got %T", to) + } + switch t.Elem().Kind() { case reflect.Struct: return r.extractIntoPtr(to, label) default: - return fmt.Errorf("Expected pointer to struct, got: %v", t) + return fmt.Errorf("expected pointer to struct, got: %v", t) } } // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided -// interface{} (to). +// any (to). // // NOTE: For internal use only // @@ -205,20 +217,29 @@ func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { // // If provided, `label` will be filtered out of the response // body prior to `r` being unmarshalled into `to`. -func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { +func (r Result) ExtractIntoSlicePtr(to any, label string) error { if r.Err != nil { return r.Err } + if to == nil { + return fmt.Errorf("expected pointer, got %T", to) + } + t := reflect.TypeOf(to) - if k := t.Kind(); k != reflect.Ptr { - return fmt.Errorf("Expected pointer, got %v", k) + if k := t.Kind(); k != reflect.Pointer { + return fmt.Errorf("expected pointer, got %v", k) } + + if reflect.ValueOf(to).IsNil() { + return fmt.Errorf("expected pointer, got %T", to) + } + switch t.Elem().Kind() { case reflect.Slice: return r.extractIntoPtr(to, label) default: - return fmt.Errorf("Expected pointer to slice, got: %v", t) + return fmt.Errorf("expected pointer to slice, got: %v", t) } } @@ -267,7 +288,7 @@ type HeaderResult struct { // ExtractInto allows users to provide an object into which `Extract` will // extract the http.Header headers of the result. -func (r HeaderResult) ExtractInto(to interface{}) error { +func (r HeaderResult) ExtractInto(to any) error { if r.Err != nil { return r.Err } diff --git a/vendor/github.com/gophercloud/gophercloud/service_client.go b/vendor/github.com/gophercloud/gophercloud/v2/service_client.go similarity index 68% rename from vendor/github.com/gophercloud/gophercloud/service_client.go rename to vendor/github.com/gophercloud/gophercloud/v2/service_client.go index 94a161e34..015c3f233 100644 --- a/vendor/github.com/gophercloud/gophercloud/service_client.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/service_client.go @@ -1,6 +1,7 @@ package gophercloud import ( + "context" "io" "net/http" "strings" @@ -47,7 +48,7 @@ func (client *ServiceClient) ServiceURL(parts ...string) string { return client.ResourceBaseURL() + strings.Join(parts, "/") } -func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { +func (client *ServiceClient) initReqOpts(JSONBody any, JSONResponse any, opts *RequestOpts) { if v, ok := (JSONBody).(io.Reader); ok { opts.RawBody = v } else if JSONBody != nil { @@ -60,80 +61,87 @@ func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse inte } // Get calls `Request` with the "GET" HTTP verb. -func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Get(ctx context.Context, url string, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, JSONResponse, opts) - return client.Request("GET", url, opts) + return client.Request(ctx, "GET", url, opts) } // Post calls `Request` with the "POST" HTTP verb. -func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Post(ctx context.Context, url string, JSONBody any, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.Request("POST", url, opts) + return client.Request(ctx, "POST", url, opts) } // Put calls `Request` with the "PUT" HTTP verb. -func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Put(ctx context.Context, url string, JSONBody any, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.Request("PUT", url, opts) + return client.Request(ctx, "PUT", url, opts) } // Patch calls `Request` with the "PATCH" HTTP verb. -func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Patch(ctx context.Context, url string, JSONBody any, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(JSONBody, JSONResponse, opts) - return client.Request("PATCH", url, opts) + return client.Request(ctx, "PATCH", url, opts) } // Delete calls `Request` with the "DELETE" HTTP verb. -func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Delete(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.Request("DELETE", url, opts) + return client.Request(ctx, "DELETE", url, opts) } // Head calls `Request` with the "HEAD" HTTP verb. -func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Head(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.Request("HEAD", url, opts) + return client.Request(ctx, "HEAD", url, opts) } func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { + serviceType := client.Type + switch client.Type { case "compute": opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion - case "sharev2": + case "shared-file-system", "sharev2", "share": opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion - case "volume": + case "block-storage", "block-store", "volume", "volumev3": opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion + // cinder should accept block-storage but (as of Dalmatian) does not + serviceType = "volume" case "baremetal": opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion case "baremetal-introspection": opts.MoreHeaders["X-OpenStack-Ironic-Inspector-API-Version"] = client.Microversion + case "container-infrastructure-management", "container-infrastructure", "container-infra": + // magnum should accept container-infrastructure-management but (as of Epoxy) does not + serviceType = "container-infra" } if client.Type != "" { - opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion + opts.MoreHeaders["OpenStack-API-Version"] = serviceType + " " + client.Microversion } } // Request carries out the HTTP operation for the service client -func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Request(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { if options.MoreHeaders == nil { options.MoreHeaders = make(map[string]string) } @@ -151,7 +159,7 @@ func (client *ServiceClient) Request(method, url string, options *RequestOpts) ( options.MoreHeaders[k] = v } } - return client.ProviderClient.Request(method, url, options) + return client.ProviderClient.Request(ctx, method, url, options) } // ParseResponse is a helper function to parse http.Response to constituents. diff --git a/vendor/github.com/gophercloud/gophercloud/util.go b/vendor/github.com/gophercloud/gophercloud/v2/util.go similarity index 68% rename from vendor/github.com/gophercloud/gophercloud/util.go rename to vendor/github.com/gophercloud/gophercloud/v2/util.go index 2740c301e..d11a723b1 100644 --- a/vendor/github.com/gophercloud/gophercloud/util.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/util.go @@ -1,7 +1,7 @@ package gophercloud import ( - "fmt" + "context" "net/url" "path/filepath" "reflect" @@ -37,9 +37,6 @@ func NormalizePathURL(basePath, rawPath string) (string, error) { absPathSys = filepath.Join(basePath, rawPath) u.Path = filepath.ToSlash(absPathSys) - if err != nil { - return "", err - } u.Scheme = "file" return u.String(), nil } @@ -61,8 +58,8 @@ func NormalizeURL(url string) string { // // 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{}) +func RemainingKeys(s any, m map[string]any) (extras map[string]any) { + extras = make(map[string]any) for k, v := range m { extras[k] = v } @@ -83,48 +80,27 @@ func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]i return } -// WaitFor polls a predicate function, once per second, up to a timeout limit. +// WaitFor polls a predicate function, once per second, up to a context cancellation. // This is useful to wait for a resource to transition to a certain state. -// To handle situations when the predicate might hang indefinitely, the -// predicate will be prematurely cancelled after the timeout. // Resource packages will wrap this in a more convenient function that's // specific to a certain resource, but it can also be useful on its own. -func WaitFor(timeout int, predicate func() (bool, error)) error { - type WaitForResult struct { - Success bool - Error error +func WaitFor(ctx context.Context, predicate func(context.Context) (bool, error)) error { + if done, err := predicate(ctx); done || err != nil { + return err } - start := time.Now().Unix() + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() for { - // If a timeout is set, and that's been exceeded, shut it down. - if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) { - return fmt.Errorf("A timeout occurred") - } - - time.Sleep(1 * time.Second) - - var result WaitForResult - ch := make(chan bool, 1) - go func() { - defer close(ch) - satisfied, err := predicate() - result.Success = satisfied - result.Error = err - }() - select { - case <-ch: - if result.Error != nil { - return result.Error + case <-ticker.C: + if done, err := predicate(ctx); done || err != nil { + return err } - if result.Success { - return nil - } - // If the predicate has not finished by the timeout, cancel it. - case <-time.After(time.Duration(timeout) * time.Second): - return fmt.Errorf("A timeout occurred") + + case <-ctx.Done(): + return ctx.Err() } } } diff --git a/vendor/github.com/gophercloud/utils/LICENSE b/vendor/github.com/gophercloud/utils/v2/LICENSE similarity index 100% rename from vendor/github.com/gophercloud/utils/LICENSE rename to vendor/github.com/gophercloud/utils/v2/LICENSE diff --git a/vendor/github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups/utils.go b/vendor/github.com/gophercloud/utils/v2/openstack/networking/v2/extensions/security/groups/utils.go similarity index 68% rename from vendor/github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups/utils.go rename to vendor/github.com/gophercloud/utils/v2/openstack/networking/v2/extensions/security/groups/utils.go index 577ff996c..fbade3e8a 100644 --- a/vendor/github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups/utils.go +++ b/vendor/github.com/gophercloud/utils/v2/openstack/networking/v2/extensions/security/groups/utils.go @@ -1,13 +1,15 @@ package groups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" ) // IDFromName is a convenience function that returns a security group's ID, // given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { +func IDFromName(ctx context.Context, client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" @@ -15,7 +17,7 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) Name: name, } - pages, err := groups.List(client, listOpts).AllPages() + pages, err := groups.List(client, listOpts).AllPages(ctx) if err != nil { return "", err } diff --git a/vendor/modules.txt b/vendor/modules.txt index 5fe6e45a5..b9261a24c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -504,31 +504,31 @@ github.com/googleapis/gax-go/v2/callctx github.com/googleapis/gax-go/v2/internal github.com/googleapis/gax-go/v2/internallog github.com/googleapis/gax-go/v2/internallog/internal -# github.com/gophercloud/gophercloud v1.14.1 -## explicit; go 1.14 -github.com/gophercloud/gophercloud -github.com/gophercloud/gophercloud/openstack -github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips -github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs -github.com/gophercloud/gophercloud/openstack/compute/v2/flavors -github.com/gophercloud/gophercloud/openstack/compute/v2/images -github.com/gophercloud/gophercloud/openstack/compute/v2/servers -github.com/gophercloud/gophercloud/openstack/identity/v2/tenants -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 -github.com/gophercloud/gophercloud/openstack/identity/v3/tokens -github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata -github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport -github.com/gophercloud/gophercloud/openstack/imageservice/v2/images -github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups -github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules -github.com/gophercloud/gophercloud/openstack/networking/v2/networks -github.com/gophercloud/gophercloud/openstack/utils -github.com/gophercloud/gophercloud/pagination -# github.com/gophercloud/utils v0.0.0-20220704184730-55bdbbaec4ba -## explicit; go 1.15 -github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups +# github.com/gophercloud/gophercloud/v2 v2.13.0 +## explicit; go 1.25.0 +github.com/gophercloud/gophercloud/v2 +github.com/gophercloud/gophercloud/v2/openstack +github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors +github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs +github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers +github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants +github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens +github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens +github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1 +github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens +github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata +github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport +github.com/gophercloud/gophercloud/v2/openstack/image/v2/images +github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips +github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups +github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules +github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks +github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports +github.com/gophercloud/gophercloud/v2/openstack/utils +github.com/gophercloud/gophercloud/v2/pagination +# github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13 +## explicit; go 1.25.0 +github.com/gophercloud/utils/v2/openstack/networking/v2/extensions/security/groups # github.com/hashicorp/go-cleanhttp v0.5.2 ## explicit; go 1.13 github.com/hashicorp/go-cleanhttp