From 744026e3321157a48fe4b888d8000d7d7870f93a Mon Sep 17 00:00:00 2001 From: Jesse Haka Date: Mon, 28 Oct 2024 11:47:27 +0200 Subject: [PATCH] update to gophercloud v2 --- go.mod | 7 +- go.sum | 14 +- pkg/volumes/openstack/BUILD.bazel | 11 +- pkg/volumes/openstack/discovery.go | 9 +- pkg/volumes/openstack/volumes.go | 48 +- .../openstack/blockstorage/v3/volumes/doc.go | 23 - .../blockstorage/v3/volumes/requests.go | 208 --- .../blockstorage/v3/volumes/results.go | 179 --- .../openstack/blockstorage/v3/volumes/util.go | 22 - .../openstack/compute/v2/servers/doc.go | 135 -- .../openstack/compute/v2/servers/requests.go | 784 ---------- .../openstack/compute/v2/servers/util.go | 21 - .../gophercloud/gophercloud/v2/.gitignore | 5 + .../gophercloud/gophercloud/v2/.golangci.yaml | 19 + .../gophercloud/gophercloud/v2/BUILD.bazel | 19 + .../gophercloud/gophercloud/v2/CHANGELOG.md | 1011 ++++++++++++ .../gophercloud/gophercloud/v2/LICENSE | 192 +++ .../gophercloud/gophercloud/v2/Makefile | 109 ++ .../gophercloud/gophercloud/v2/README.md | 252 +++ .../gophercloud/gophercloud/v2/RELEASE.md | 79 + .../gophercloud/v2/auth_options.go | 517 +++++++ .../gophercloud/gophercloud/v2/auth_result.go | 52 + .../gophercloud/v2/endpoint_search.go | 76 + .../gophercloud/gophercloud/v2/errors.go | 351 +++++ .../gophercloud/v2/openstack/BUILD.bazel | 23 + .../gophercloud/v2/openstack/auth_env.go | 137 ++ .../blockstorage/v3/volumes/BUILD.bazel | 8 +- .../openstack/blockstorage/v3/volumes/doc.go | 161 ++ .../blockstorage/v3/volumes/requests.go | 765 +++++++++ .../blockstorage/v3/volumes/results.go | 401 +++++ .../openstack/blockstorage/v3/volumes/urls.go | 6 +- .../openstack/blockstorage/v3/volumes/util.go | 23 + .../gophercloud/v2/openstack/client.go | 492 ++++++ .../compute/v2}/availabilityzones/BUILD.bazel | 8 +- .../compute/v2}/availabilityzones/doc.go | 27 +- .../compute/v2}/availabilityzones/requests.go | 4 +- .../compute/v2}/availabilityzones/results.go | 10 +- .../compute/v2}/availabilityzones/urls.go | 2 +- .../openstack/compute/v2/servers/BUILD.bazel | 8 +- .../v2/openstack/compute/v2/servers/doc.go | 313 ++++ .../openstack/compute/v2/servers/errors.go | 2 +- .../openstack/compute/v2/servers/requests.go | 1369 +++++++++++++++++ .../openstack/compute/v2/servers/results.go | 250 ++- .../openstack/compute/v2/servers/urls.go | 2 +- .../v2/openstack/compute/v2/servers/util.go | 24 + .../compute/v2}/volumeattach/BUILD.bazel | 8 +- .../openstack/compute/v2}/volumeattach/doc.go | 4 +- .../compute/v2}/volumeattach/requests.go | 22 +- .../compute/v2}/volumeattach/results.go | 4 +- .../compute/v2}/volumeattach/urls.go | 2 +- .../gophercloud/v2/openstack/doc.go | 14 + .../v2/openstack/endpoint_location.go | 111 ++ .../gophercloud/v2/openstack/errors.go | 47 + .../openstack/identity/v2/tenants/BUILD.bazel | 18 + .../v2/openstack/identity/v2/tenants/doc.go | 65 + .../openstack/identity/v2/tenants/requests.go | 122 ++ .../openstack/identity/v2/tenants/results.go | 95 ++ .../v2/openstack/identity/v2/tenants/urls.go | 23 + .../openstack/identity/v2/tokens/BUILD.bazel | 18 + .../v2/openstack/identity/v2/tokens/doc.go | 46 + .../openstack/identity/v2/tokens/requests.go | 114 ++ .../openstack/identity/v2/tokens/results.go | 174 +++ .../v2/openstack/identity/v2/tokens/urls.go | 13 + .../identity/v3/ec2tokens/BUILD.bazel | 17 + .../v2/openstack/identity/v3/ec2tokens/doc.go | 40 + .../identity/v3/ec2tokens/requests.go | 378 +++++ .../openstack/identity/v3/ec2tokens/urls.go | 11 + .../openstack/identity/v3/oauth1/BUILD.bazel | 19 + .../v2/openstack/identity/v3/oauth1/doc.go | 122 ++ .../openstack/identity/v3/oauth1/requests.go | 588 +++++++ .../openstack/identity/v3/oauth1/results.go | 317 ++++ .../v2/openstack/identity/v3/oauth1/urls.go | 43 + .../openstack/identity/v3/tokens/BUILD.bazel | 15 + .../v2/openstack/identity/v3/tokens/doc.go | 107 ++ .../openstack/identity/v3/tokens/requests.go | 179 +++ .../openstack/identity/v3/tokens/results.go | 215 +++ .../v2/openstack/identity/v3/tokens/urls.go | 7 + .../v2/openstack/utils/BUILD.bazel | 13 + .../v2/openstack/utils/base_endpoint.go | 28 + .../v2/openstack/utils/choose_version.go | 236 +++ .../gophercloud/v2/pagination/BUILD.bazel | 17 + .../gophercloud/v2/pagination/http.go | 63 + .../gophercloud/v2/pagination/linked.go | 92 ++ .../gophercloud/v2/pagination/marker.go | 58 + .../gophercloud/v2/pagination/pager.go | 256 +++ .../gophercloud/v2/pagination/pkg.go | 4 + .../gophercloud/v2/pagination/single.go | 33 + .../gophercloud/gophercloud/v2/params.go | 506 ++++++ .../gophercloud/v2/provider_client.go | 583 +++++++ .../gophercloud/gophercloud/v2/results.go | 465 ++++++ .../gophercloud/v2/service_client.go | 164 ++ .../gophercloud/gophercloud/v2/util.go | 109 ++ vendor/golang.org/x/crypto/blowfish/cipher.go | 2 +- .../chacha20poly1305/chacha20poly1305.go | 2 +- .../x/crypto/cryptobyte/asn1/asn1.go | 2 +- .../golang.org/x/crypto/cryptobyte/string.go | 2 +- .../x/crypto/curve25519/BUILD.bazel | 7 +- .../x/crypto/curve25519/curve25519.go | 39 +- .../x/crypto/curve25519/curve25519_compat.go | 105 -- .../x/crypto/curve25519/curve25519_go120.go | 46 - .../curve25519/internal/field/BUILD.bazel | 18 - .../x/crypto/curve25519/internal/field/README | 7 - .../x/crypto/curve25519/internal/field/fe.go | 416 ----- .../curve25519/internal/field/fe_amd64.go | 15 - .../curve25519/internal/field/fe_amd64.s | 378 ----- .../internal/field/fe_amd64_noasm.go | 11 - .../curve25519/internal/field/fe_arm64.go | 15 - .../curve25519/internal/field/fe_arm64.s | 42 - .../internal/field/fe_arm64_noasm.go | 11 - .../curve25519/internal/field/fe_generic.go | 264 ---- .../curve25519/internal/field/sync.checkpoint | 1 - .../crypto/curve25519/internal/field/sync.sh | 19 - vendor/golang.org/x/crypto/hkdf/hkdf.go | 2 +- vendor/golang.org/x/crypto/ssh/client_auth.go | 4 + vendor/golang.org/x/crypto/ssh/doc.go | 2 +- vendor/golang.org/x/sys/unix/mremap.go | 5 + .../golang.org/x/sys/unix/syscall_darwin.go | 12 + vendor/golang.org/x/sys/unix/syscall_unix.go | 9 + .../x/sys/unix/zsyscall_darwin_amd64.go | 33 + .../x/sys/unix/zsyscall_darwin_amd64.s | 10 + .../x/sys/unix/zsyscall_darwin_arm64.go | 33 + .../x/sys/unix/zsyscall_darwin_arm64.s | 10 + .../x/sys/windows/security_windows.go | 24 +- .../x/sys/windows/zsyscall_windows.go | 9 + vendor/modules.txt | 26 +- 125 files changed, 12474 insertions(+), 2870 deletions(-) delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/doc.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/results.go delete mode 100644 vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/util.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 create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/.gitignore create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/.golangci.yaml create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/LICENSE create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/Makefile create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/README.md create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/RELEASE.md create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/auth_options.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/auth_result.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/errors.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/blockstorage/v3/volumes/BUILD.bazel (53%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/results.go rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/blockstorage/v3/volumes/urls.go (75%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/util.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/client.go rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/availabilityzones/BUILD.bazel (52%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/availabilityzones/doc.go (63%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/availabilityzones/requests.go (87%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/availabilityzones/results.go (84%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/availabilityzones/urls.go (83%) rename vendor/github.com/gophercloud/gophercloud/{ => v2}/openstack/compute/v2/servers/BUILD.bazel (56%) 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 (63%) 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/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/volumeattach/BUILD.bazel (52%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/volumeattach/doc.go (73%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/volumeattach/requests.go (71%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/volumeattach/results.go (96%) rename vendor/github.com/gophercloud/gophercloud/{openstack/compute/v2/extensions => v2/openstack/compute/v2}/volumeattach/urls.go (93%) create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/errors.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/BUILD.bazel create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/linked.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/pkg.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/params.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/provider_client.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/results.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/service_client.go create mode 100644 vendor/github.com/gophercloud/gophercloud/v2/util.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/curve25519_compat.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/curve25519_go120.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/BUILD.bazel delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/README delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh diff --git a/go.mod b/go.mod index afe07e940..1e0cf405c 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/digitalocean/godo v1.118.0 github.com/golang/protobuf v1.5.4 - github.com/gophercloud/gophercloud v1.12.0 + github.com/gophercloud/gophercloud/v2 v2.2.0 github.com/hetznercloud/hcloud-go v1.56.0 github.com/prometheus/client_golang v1.19.1 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.28 @@ -83,6 +83,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.4 // indirect + github.com/gophercloud/gophercloud v1.12.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.6 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect @@ -104,9 +105,9 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.19.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 // indirect diff --git a/go.sum b/go.sum index 19874ba9a..0d9d1fa40 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= github.com/gophercloud/gophercloud v1.12.0 h1:Jrz16vPAL93l80q16fp8NplrTCp93y7rZh2P3Q4Yq7g= github.com/gophercloud/gophercloud v1.12.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud/v2 v2.2.0 h1:STqqnSXuhcg1OPBOZ14z6JDm8fKIN13H2bJg6bBuHp8= +github.com/gophercloud/gophercloud/v2 v2.2.0/go.mod h1:f2hMRC7Kakbv5vM7wSGHrIPZh6JZR60GVHryJlF/K44= 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= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -276,8 +278,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= @@ -327,14 +329,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/pkg/volumes/openstack/BUILD.bazel b/pkg/volumes/openstack/BUILD.bazel index 273f78dc1..ebcce80a2 100644 --- a/pkg/volumes/openstack/BUILD.bazel +++ b/pkg/volumes/openstack/BUILD.bazel @@ -15,12 +15,11 @@ go_library( deps = [ "//pkg/privateapi/discovery", "//pkg/volumes", - "//vendor/github.com/gophercloud/gophercloud", - "//vendor/github.com/gophercloud/gophercloud/openstack", - "//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes", - "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones", - "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", - "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers", + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach", "//vendor/github.com/prometheus/client_golang/prometheus", "//vendor/gopkg.in/gcfg.v1:gcfg_v1", "//vendor/k8s.io/klog/v2:klog", diff --git a/pkg/volumes/openstack/discovery.go b/pkg/volumes/openstack/discovery.go index 23afbbbe4..3b3bbf130 100644 --- a/pkg/volumes/openstack/discovery.go +++ b/pkg/volumes/openstack/discovery.go @@ -17,7 +17,9 @@ limitations under the License. package openstack import ( - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "context" + + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "k8s.io/klog/v2" "sigs.k8s.io/etcd-manager/pkg/privateapi/discovery" "sigs.k8s.io/etcd-manager/pkg/volumes" @@ -27,8 +29,9 @@ import ( var _ discovery.Interface = &OpenstackVolumes{} func (os *OpenstackVolumes) Poll() (map[string]discovery.Node, error) { + ctx := context.TODO() - allVolumes, err := os.findVolumes(false) + allVolumes, err := os.findVolumes(ctx, false) if err != nil { return nil, err } @@ -42,7 +45,7 @@ func (os *OpenstackVolumes) Poll() (map[string]discovery.Node, error) { } for i, volume := range instanceToVolumeMap { mc := NewMetricContext("server", "get") - server, err := servers.Get(os.computeClient, i).Extract() + server, err := servers.Get(ctx, os.computeClient, i).Extract() if mc.ObserveRequest(err) != nil { klog.Warningf("Could not find server with id '%s': %v", i, err) continue diff --git a/pkg/volumes/openstack/volumes.go b/pkg/volumes/openstack/volumes.go index a744103a3..817297fa2 100644 --- a/pkg/volumes/openstack/volumes.go +++ b/pkg/volumes/openstack/volumes.go @@ -17,6 +17,7 @@ limitations under the License. package openstack import ( + "context" "encoding/json" "fmt" "io" @@ -27,12 +28,11 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - cinderv3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + cinderv3 "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" "k8s.io/klog/v2" "k8s.io/mount-utils" utilexec "k8s.io/utils/exec" @@ -112,6 +112,8 @@ func NewOpenstackVolumes(clusterName string, volumeTags []string, nameTag string networkCIDR: networkCIDR, } + ctx := context.Background() + for _, volumeTag := range volumeTags { tokens := strings.SplitN(volumeTag, "=", 2) if len(tokens) == 1 { @@ -121,12 +123,12 @@ func NewOpenstackVolumes(clusterName string, volumeTags []string, nameTag string } } - err = stack.getClients() + err = stack.getClients(ctx) if err != nil { return nil, fmt.Errorf("could not build OpenstackVolumes: %v", err) } - err = stack.discoverTags() + err = stack.discoverTags(ctx) if err != nil { return nil, err } @@ -293,34 +295,34 @@ func getCredential() (gophercloud.AuthOptions, string, bool, error) { }, cfg.Global.Region, cfg.BlockStorage.IgnoreVolumeAZ, nil } -func (stack *OpenstackVolumes) getClients() error { +func (stack *OpenstackVolumes) getClients(ctx context.Context) error { authOption, region, ignoreAZ, err := getCredential() if err != nil { return fmt.Errorf("error building openstack credentials: %v", err) } stack.ignoreAZ = ignoreAZ - provider, err := openstack.NewClient(authOption.IdentityEndpoint) + client, err := openstack.NewClient(authOption.IdentityEndpoint) if err != nil { return fmt.Errorf("error building openstack storage client: %v", err) } ua := gophercloud.UserAgent{} ua.Prepend("etcd-manager") - provider.UserAgent = ua + client.UserAgent = ua klog.V(4).Infof("Using user-agent %s", ua.Join()) - err = openstack.Authenticate(provider, authOption) + err = openstack.Authenticate(ctx, client, authOption) if err != nil { return fmt.Errorf("error authenticating openstack client: %v", err) } - cinderClient, err := openstack.NewBlockStorageV3(provider, gophercloud.EndpointOpts{ + cinderClient, err := openstack.NewBlockStorageV3(client, gophercloud.EndpointOpts{ Type: "volumev3", Region: region, }) if err != nil { return fmt.Errorf("error building storage client: %v", err) } - computeClient, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ + computeClient, err := openstack.NewComputeV2(client, gophercloud.EndpointOpts{ Type: "compute", Region: region, }) @@ -337,7 +339,7 @@ func (stack *OpenstackVolumes) InternalIP() net.IP { return stack.internalIP } -func (stack *OpenstackVolumes) discoverTags() error { +func (stack *OpenstackVolumes) discoverTags(ctx context.Context) error { // Project ID { @@ -359,14 +361,8 @@ func (stack *OpenstackVolumes) discoverTags() error { // Internal IP & zone { - - var extendedServer struct { - servers.Server - availabilityzones.ServerAvailabilityZoneExt - } - mc := NewMetricContext("server", "get") - err := servers.Get(stack.computeClient, strings.TrimSpace(stack.meta.ServerID)).ExtractInto(&extendedServer) + extendedServer, err := servers.Get(ctx, stack.computeClient, strings.TrimSpace(stack.meta.ServerID)).Extract() if mc.ObserveRequest(err) != nil { return fmt.Errorf("failed to retrieve server information from cloud: %v", err) } @@ -444,10 +440,10 @@ func (stack *OpenstackVolumes) matchesTags(d *cinderv3.Volume, filterByAZ bool) } func (stack *OpenstackVolumes) FindVolumes() ([]*volumes.Volume, error) { - return stack.findVolumes(true) + return stack.findVolumes(context.TODO(), true) } -func (stack *OpenstackVolumes) findVolumes(filterByAZ bool) ([]*volumes.Volume, error) { +func (stack *OpenstackVolumes) findVolumes(ctx context.Context, filterByAZ bool) ([]*volumes.Volume, error) { var volumes []*volumes.Volume klog.V(2).Infof("Listing Openstack disks in %s/%s", stack.project, stack.meta.AvailabilityZone) @@ -455,7 +451,7 @@ func (stack *OpenstackVolumes) findVolumes(filterByAZ bool) ([]*volumes.Volume, mc := NewMetricContext("volumes", "list") pages, err := cinderv3.List(stack.volumeClient, cinderv3.ListOpts{ TenantID: stack.project, - }).AllPages() + }).AllPages(ctx) if mc.ObserveRequest(err) != nil { return volumes, fmt.Errorf("FindVolumes: Failed to list volumes: %v", err) } @@ -585,7 +581,7 @@ func (stack *OpenstackVolumes) AttachVolume(volume *volumes.Volume) error { VolumeID: volume.ProviderID, } mc := NewMetricContext("volume", "attach") - volumeAttachment, err := volumeattach.Create(stack.computeClient, stack.meta.ServerID, opts).Extract() + volumeAttachment, err := volumeattach.Create(context.TODO(), stack.computeClient, stack.meta.ServerID, opts).Extract() if mc.ObserveRequest(err) != nil { return fmt.Errorf("error attaching volume %s to server %s: %v", opts.VolumeID, stack.meta.ServerID, err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/doc.go deleted file mode 100644 index 0b834852d..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Package volumes provides information and interaction with volumes in the -OpenStack Block Storage service. A volume is a detachable block storage -device, akin to a USB hard drive. It can only be attached to one instance at -a time. - -Example to create a Volume from a Backup - - backupID := "20c792f0-bb03-434f-b653-06ef238e337e" - options := volumes.CreateOpts{ - Name: "vol-001", - BackupID: &backupID, - } - - client.Microversion = "3.47" - volume, err := volumes.Create(client, options).Extract() - if err != nil { - panic(err) - } - - fmt.Println(volume) -*/ -package volumes diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go deleted file mode 100644 index f6063c595..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go +++ /dev/null @@ -1,208 +0,0 @@ -package volumes - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume. This object is passed to -// the volumes.Create function. For more information about these parameters, -// see the Volume object. -type CreateOpts struct { - // The size of the volume, in GB - Size int `json:"size,omitempty"` - // The availability zone - AvailabilityZone string `json:"availability_zone,omitempty"` - // ConsistencyGroupID is the ID of a consistency group - ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` - // The volume description - Description string `json:"description,omitempty"` - // One or more metadata key and value pairs to associate with the volume - Metadata map[string]string `json:"metadata,omitempty"` - // The volume name - Name string `json:"name,omitempty"` - // the ID of the existing volume snapshot - SnapshotID string `json:"snapshot_id,omitempty"` - // SourceReplica is a UUID of an existing volume to replicate with - SourceReplica string `json:"source_replica,omitempty"` - // the ID of the existing volume - SourceVolID string `json:"source_volid,omitempty"` - // The ID of the image from which you want to create the volume. - // Required to create a bootable volume. - ImageID string `json:"imageRef,omitempty"` - // Specifies the backup ID, from which you want to create the volume. - // Create a volume from a backup is supported since 3.47 microversion - BackupID string `json:"backup_id,omitempty"` - // The associated volume type - VolumeType string `json:"volume_type,omitempty"` - // Multiattach denotes if the volume is multi-attach capable. - Multiattach bool `json:"multiattach,omitempty"` -} - -// ToVolumeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") -} - -// Create will create a new Volume based on the values in CreateOpts. To extract -// the Volume object from the response, call the Extract method on the -// CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DeleteOptsBuilder allows extensions to add additional parameters to the -// Delete request. -type DeleteOptsBuilder interface { - ToVolumeDeleteQuery() (string, error) -} - -// DeleteOpts contains options for deleting a Volume. This object is passed to -// the volumes.Delete function. -type DeleteOpts struct { - // Delete all snapshots of this volume as well. - Cascade bool `q:"cascade"` -} - -// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. -func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { - url := deleteURL(client, id) - if opts != nil { - query, err := opts.ToVolumeDeleteQuery() - if err != nil { - r.Err = err - return - } - url += query - } - resp, err := client.Delete(url, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves the Volume with the provided ID. To extract the Volume object -// from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeListQuery() (string, error) -} - -// ListOpts holds options for listing Volumes. It is passed to the volumes.List -// function. -type ListOpts struct { - // AllTenants will retrieve volumes of all tenants/projects. - AllTenants bool `q:"all_tenants"` - - // Metadata will filter results based on specified metadata. - Metadata map[string]string `q:"metadata"` - - // Name will filter by the specified volume name. - Name string `q:"name"` - - // Status will filter by the specified status. - Status string `q:"status"` - - // TenantID will filter by a specific tenant/project ID. - // Setting AllTenants is required for this. - TenantID string `q:"project_id"` - - // Comma-separated list of sort keys and optional sort directions in the - // form of [:]. - Sort string `q:"sort"` - - // Requests a page size of items. - Limit int `q:"limit"` - - // Used in conjunction with limit to return a slice of items. - Offset int `q:"offset"` - - // The ID of the last-seen item. - Marker string `q:"marker"` -} - -// ToVolumeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns Volumes optionally limited by the conditions provided in ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToVolumeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume. This object is passed -// to the volumes.Update function. For more information about the parameters, see -// the Volume object. -type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToVolumeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") -} - -// Update will update the Volume with provided information. To extract the updated -// Volume from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(updateURL(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/blockstorage/v3/volumes/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/results.go deleted file mode 100644 index df97e6694..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/results.go +++ /dev/null @@ -1,179 +0,0 @@ -package volumes - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// Attachment represents a Volume Attachment record -type Attachment struct { - AttachedAt time.Time `json:"-"` - AttachmentID string `json:"attachment_id"` - Device string `json:"device"` - HostName string `json:"host_name"` - ID string `json:"id"` - ServerID string `json:"server_id"` - VolumeID string `json:"volume_id"` -} - -// UnmarshalJSON is our unmarshalling helper -func (r *Attachment) UnmarshalJSON(b []byte) error { - type tmp Attachment - var s struct { - tmp - AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Attachment(s.tmp) - - r.AttachedAt = time.Time(s.AttachedAt) - - return err -} - -// Volume contains all the information associated with an OpenStack Volume. -type Volume struct { - // Unique identifier for the volume. - ID string `json:"id"` - // Current status of the volume. - Status string `json:"status"` - // Size of the volume in GB. - Size int `json:"size"` - // AvailabilityZone is which availability zone the volume is in. - AvailabilityZone string `json:"availability_zone"` - // The date when this volume was created. - CreatedAt time.Time `json:"-"` - // The date when this volume was last updated - UpdatedAt time.Time `json:"-"` - // Instances onto which the volume is attached. - Attachments []Attachment `json:"attachments"` - // Human-readable display name for the volume. - Name string `json:"name"` - // Human-readable description for the volume. - Description string `json:"description"` - // The type of volume to create, either SATA or SSD. - VolumeType string `json:"volume_type"` - // The ID of the snapshot from which the volume was created - SnapshotID string `json:"snapshot_id"` - // The ID of another block storage volume from which the current volume was created - SourceVolID string `json:"source_volid"` - // The backup ID, from which the volume was restored - // This field is supported since 3.47 microversion - BackupID *string `json:"backup_id"` - // Arbitrary key-value pairs defined by the user. - Metadata map[string]string `json:"metadata"` - // UserID is the id of the user who created the volume. - UserID string `json:"user_id"` - // Indicates whether this is a bootable volume. - Bootable string `json:"bootable"` - // Encrypted denotes if the volume is encrypted. - Encrypted bool `json:"encrypted"` - // ReplicationStatus is the status of replication. - ReplicationStatus string `json:"replication_status"` - // ConsistencyGroupID is the consistency group ID. - ConsistencyGroupID string `json:"consistencygroup_id"` - // Multiattach denotes if the volume is multi-attach capable. - Multiattach bool `json:"multiattach"` - // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. - VolumeImageMetadata map[string]string `json:"volume_image_metadata"` -} - -// UnmarshalJSON another unmarshalling function -func (r *Volume) UnmarshalJSON(b []byte) error { - type tmp Volume - var s struct { - tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Volume(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -// VolumePage is a pagination.pager that is returned from a call to the List function. -type VolumePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if a ListResult contains no Volumes. -func (r VolumePage) IsEmpty() (bool, error) { - if r.StatusCode == 204 { - return true, nil - } - - volumes, err := ExtractVolumes(r) - return len(volumes) == 0, err -} - -func (page VolumePage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"volumes_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. -func ExtractVolumes(r pagination.Page) ([]Volume, error) { - var s []Volume - err := ExtractVolumesInto(r, &s) - return s, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract will get the Volume object out of the commonResult object. -func (r commonResult) Extract() (*Volume, error) { - var s Volume - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractInto converts our response data into a volume struct -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "volume") -} - -// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes -func ExtractVolumesInto(r pagination.Page, v interface{}) error { - return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/util.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/util.go deleted file mode 100644 index e86c1b4b4..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package volumes - -import ( - "github.com/gophercloud/gophercloud" -) - -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} 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/v2/.gitignore b/vendor/github.com/gophercloud/gophercloud/v2/.gitignore new file mode 100644 index 000000000..8b1b79e61 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/.gitignore @@ -0,0 +1,5 @@ +**/*.swp +.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..828a099a4 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/.golangci.yaml @@ -0,0 +1,19 @@ +--- +linters: + disable-all: true + enable: + - errcheck + - gofmt + - goimports + - govet + - staticcheck + - unparam + - unused + +issues: + exclude: + - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) + exclude-rules: + - linters: + - staticcheck + text: 'SA1019: (x509.EncryptPEMBlock|strings.Title)' diff --git a/vendor/github.com/gophercloud/gophercloud/v2/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/BUILD.bazel new file mode 100644 index 000000000..34888e2dc --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "gophercloud", + srcs = [ + "auth_options.go", + "auth_result.go", + "endpoint_search.go", + "errors.go", + "params.go", + "provider_client.go", + "results.go", + "service_client.go", + "util.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2", + importpath = "github.com/gophercloud/gophercloud/v2", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md b/vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md new file mode 100644 index 000000000..efe2d9f6a --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/CHANGELOG.md @@ -0,0 +1,1011 @@ +## 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 + +New features and improvements: + +* [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) + +* [GH-2979](https://github.com/gophercloud/gophercloud/pull/2979) [v1] CI backports +* [GH-2985](https://github.com/gophercloud/gophercloud/pull/2985) [v1] baremetal: fix handling of the "fields" query argument +* [GH-2989](https://github.com/gophercloud/gophercloud/pull/2989) [v1] [CI] Fix portbiding tests +* [GH-2992](https://github.com/gophercloud/gophercloud/pull/2992) [v1] [CI] Fix portbiding tests +* [GH-2993](https://github.com/gophercloud/gophercloud/pull/2993) [v1] build(deps): bump EmilienM/devstack-action from 0.14 to 0.15 +* [GH-2998](https://github.com/gophercloud/gophercloud/pull/2998) [v1] testhelper: mark all helpers with t.Helper +* [GH-3043](https://github.com/gophercloud/gophercloud/pull/3043) [v1] CI: remove Zed from testing coverage + +## v1.11.0 (2024-03-07) + +This version reverts the inclusion of Context in the v1 branch. This inclusion +didn't add much value because no packages were using it; on the other hand, it +introduced a bug when using the Context property of the Provider client. + +## v1.10.0 (2024-02-27) **RETRACTED**: see https://github.com/gophercloud/gophercloud/issues/2969 + +* [GH-2893](https://github.com/gophercloud/gophercloud/pull/2893) [v1] authentication: Add WithContext functions +* [GH-2894](https://github.com/gophercloud/gophercloud/pull/2894) [v1] pager: Add WithContext functions +* [GH-2899](https://github.com/gophercloud/gophercloud/pull/2899) [v1] Authenticate with a clouds.yaml +* [GH-2917](https://github.com/gophercloud/gophercloud/pull/2917) [v1] Add ParseOption type to made clouds.Parse() more usable for optional With* funcs +* [GH-2924](https://github.com/gophercloud/gophercloud/pull/2924) [v1] build(deps): bump EmilienM/devstack-action from 0.11 to 0.14 +* [GH-2933](https://github.com/gophercloud/gophercloud/pull/2933) [v1] Fix AllowReauth reauthentication +* [GH-2950](https://github.com/gophercloud/gophercloud/pull/2950) [v1] compute: Use volumeID, not attachmentID for volume attachments + +## v1.9.0 (2024-02-02) **RETRACTED**: see https://github.com/gophercloud/gophercloud/issues/2969 + +New features and improvements: + +* [GH-2884](https://github.com/gophercloud/gophercloud/pull/2884) [v1] Context-aware methods to ProviderClient and ServiceClient +* [GH-2887](https://github.com/gophercloud/gophercloud/pull/2887) [v1] Add support of Flavors and FlavorProfiles for Octavia +* [GH-2875](https://github.com/gophercloud/gophercloud/pull/2875) [v1] [db/v1/instance]: adding support for availability_zone for a db instance + +CI changes: + +* [GH-2856](https://github.com/gophercloud/gophercloud/pull/2856) [v1] Fix devstack install on EOL magnum branches +* [GH-2857](https://github.com/gophercloud/gophercloud/pull/2857) [v1] Fix networking acceptance tests +* [GH-2858](https://github.com/gophercloud/gophercloud/pull/2858) [v1] build(deps): bump actions/upload-artifact from 3 to 4 +* [GH-2859](https://github.com/gophercloud/gophercloud/pull/2859) [v1] build(deps): bump github/codeql-action from 2 to 3 + +## v1.8.0 (2023-11-30) + +New features and improvements: + +* [GH-2800](https://github.com/gophercloud/gophercloud/pull/2800) [v1] Fix options initialization in ServiceClient.Request (fixes #2798) +* [GH-2823](https://github.com/gophercloud/gophercloud/pull/2823) [v1] Add more godoc to GuestFormat +* [GH-2826](https://github.com/gophercloud/gophercloud/pull/2826) Allow objects.CreateTempURL with names containing /v1/ + +CI changes: + +* [GH-2802](https://github.com/gophercloud/gophercloud/pull/2802) [v1] Add job for bobcat stable/2023.2 +* [GH-2819](https://github.com/gophercloud/gophercloud/pull/2819) [v1] Test files alongside code +* [GH-2814](https://github.com/gophercloud/gophercloud/pull/2814) Make fixtures part of tests +* [GH-2796](https://github.com/gophercloud/gophercloud/pull/2796) [v1] ci/unit: switch to coverallsapp/github-action +* [GH-2840](https://github.com/gophercloud/gophercloud/pull/2840) unit tests: Fix the installation of tools + +## v1.7.0 (2023-09-22) + +New features and improvements: + +* [GH-2782](https://github.com/gophercloud/gophercloud/pull/2782) [v1] (manual clean backport) Add tag field to compute block_device_v2 + +CI changes: + +* [GH-2760](https://github.com/gophercloud/gophercloud/pull/2760) [v1 backports] semver auto labels +* [GH-2775](https://github.com/gophercloud/gophercloud/pull/2775) [v1] Fix typos in comments +* [GH-2783](https://github.com/gophercloud/gophercloud/pull/2783) [v1] (clean manual backport) ci/functional: fix ubuntu version & add antelope +* [GH-2785](https://github.com/gophercloud/gophercloud/pull/2785) [v1] Acceptance: Handle numerical version names in version comparison helpers +* [GH-2787](https://github.com/gophercloud/gophercloud/pull/2787) backport-v1: fixes to semver label +* [GH-2788](https://github.com/gophercloud/gophercloud/pull/2788) [v1] Make acceptance tests internal + + +## v1.6.0 (2023-08-30) + +New features and improvements: + +* [GH-2712](https://github.com/gophercloud/gophercloud/pull/2712) [v1] README: minor change to test backport workflow +* [GH-2713](https://github.com/gophercloud/gophercloud/pull/2713) [v1] tests: run MultiAttach with a capable Cinder Type +* [GH-2714](https://github.com/gophercloud/gophercloud/pull/2714) [v1] Add CRUD support for encryption in volume v3 types +* [GH-2715](https://github.com/gophercloud/gophercloud/pull/2715) [v1] Add projectID to fwaas_v2 policy CreateOpts and ListOpts +* [GH-2716](https://github.com/gophercloud/gophercloud/pull/2716) [v1] Add projectID to fwaas_v2 CreateOpts +* [GH-2717](https://github.com/gophercloud/gophercloud/pull/2717) [v1] [manila]: add reset and force delete actions to a snapshot +* [GH-2718](https://github.com/gophercloud/gophercloud/pull/2718) [v1] [cinder]: add reset and force delete actions to volumes and snapshots +* [GH-2721](https://github.com/gophercloud/gophercloud/pull/2721) [v1] orchestration: Explicit error in optionsmap creation +* [GH-2723](https://github.com/gophercloud/gophercloud/pull/2723) [v1] Add conductor API to Baremetal V1 +* [GH-2729](https://github.com/gophercloud/gophercloud/pull/2729) [v1] networking/v2/ports: allow list filter by security group + +CI changes: + +* [GH-2675](https://github.com/gophercloud/gophercloud/pull/2675) [v1][CI] Drop periodic jobs from stable branch +* [GH-2683](https://github.com/gophercloud/gophercloud/pull/2683) [v1] CI tweaks + + +## v1.5.0 (2023-06-21) + +New features and improvements: + +* [GH-2634](https://github.com/gophercloud/gophercloud/pull/2634) baremetal: update inspection inventory with recent additions +* [GH-2635](https://github.com/gophercloud/gophercloud/pull/2635) [manila]: Add Share Replicas support +* [GH-2637](https://github.com/gophercloud/gophercloud/pull/2637) [FWaaS_v2]: Add FWaaS_V2 workflow and enable tests +* [GH-2639](https://github.com/gophercloud/gophercloud/pull/2639) Implement errors.Unwrap() on unexpected status code errors +* [GH-2648](https://github.com/gophercloud/gophercloud/pull/2648) [manila]: implement share transfer API + + +## v1.4.0 (2023-05-25) + +New features and improvements: + +* [GH-2465](https://github.com/gophercloud/gophercloud/pull/2465) keystone: add v3 limits update operation +* [GH-2596](https://github.com/gophercloud/gophercloud/pull/2596) keystone: add v3 limits get operation +* [GH-2618](https://github.com/gophercloud/gophercloud/pull/2618) keystone: add v3 limits delete operation +* [GH-2616](https://github.com/gophercloud/gophercloud/pull/2616) Add CRUD support for register limit APIs +* [GH-2610](https://github.com/gophercloud/gophercloud/pull/2610) Add PUT/HEAD/DELETE for identity/v3/OS-INHERIT +* [GH-2597](https://github.com/gophercloud/gophercloud/pull/2597) Add validation and optimise objects.BulkDelete +* [GH-2602](https://github.com/gophercloud/gophercloud/pull/2602) [swift v1]: introduce a TempURLKey argument for objects.CreateTempURLOpts struct +* [GH-2623](https://github.com/gophercloud/gophercloud/pull/2623) Add the ability to remove ingress/egress policies from fwaas_v2 groups +* [GH-2625](https://github.com/gophercloud/gophercloud/pull/2625) neutron: Support trunk_details extension + +CI changes: + +* [GH-2608](https://github.com/gophercloud/gophercloud/pull/2608) Drop train and ussuri jobs +* [GH-2589](https://github.com/gophercloud/gophercloud/pull/2589) Bump EmilienM/devstack-action from 0.10 to 0.11 +* [GH-2604](https://github.com/gophercloud/gophercloud/pull/2604) Bump mheap/github-action-required-labels from 3 to 4 +* [GH-2620](https://github.com/gophercloud/gophercloud/pull/2620) Pin goimport dep to a version that works with go 1.14 +* [GH-2619](https://github.com/gophercloud/gophercloud/pull/2619) Fix version comparison for acceptance tests +* [GH-2627](https://github.com/gophercloud/gophercloud/pull/2627) Limits: Fix ToDo to create registered limit and use it +* [GH-2629](https://github.com/gophercloud/gophercloud/pull/2629) [manila]: Add share from snapshot restore functional test + + +## v1.3.0 (2023-03-28) + +* [GH-2464](https://github.com/gophercloud/gophercloud/pull/2464) keystone: add v3 limits create operation +* [GH-2512](https://github.com/gophercloud/gophercloud/pull/2512) Manila: add List for share-access-rules API +* [GH-2529](https://github.com/gophercloud/gophercloud/pull/2529) Added target state "rebuild" for Ironic nodes +* [GH-2539](https://github.com/gophercloud/gophercloud/pull/2539) Add release instructions +* [GH-2540](https://github.com/gophercloud/gophercloud/pull/2540) [all] IsEmpty to check for HTTP status 204 +* [GH-2543](https://github.com/gophercloud/gophercloud/pull/2543) keystone: add v3 OS-FEDERATION mappings get operation +* [GH-2545](https://github.com/gophercloud/gophercloud/pull/2545) baremetal: add inspection_{started,finished}_at to Node +* [GH-2546](https://github.com/gophercloud/gophercloud/pull/2546) Drop train job for baremetal +* [GH-2549](https://github.com/gophercloud/gophercloud/pull/2549) objects: Clarify ExtractContent usage +* [GH-2550](https://github.com/gophercloud/gophercloud/pull/2550) keystone: add v3 OS-FEDERATION mappings update operation +* [GH-2552](https://github.com/gophercloud/gophercloud/pull/2552) objectstorage: Reject container names with a slash +* [GH-2555](https://github.com/gophercloud/gophercloud/pull/2555) nova: introduce servers.ListSimple along with the more detailed servers.List +* [GH-2558](https://github.com/gophercloud/gophercloud/pull/2558) Expand docs on 'clientconfig' usage +* [GH-2563](https://github.com/gophercloud/gophercloud/pull/2563) Support propagate_uplink_status for Ports +* [GH-2567](https://github.com/gophercloud/gophercloud/pull/2567) Fix invalid baremetal-introspection service type +* [GH-2568](https://github.com/gophercloud/gophercloud/pull/2568) Prefer github mirrors over opendev repos +* [GH-2571](https://github.com/gophercloud/gophercloud/pull/2571) Swift V1: support object versioning +* [GH-2572](https://github.com/gophercloud/gophercloud/pull/2572) networking v2: add extraroutes Add and Remove methods +* [GH-2573](https://github.com/gophercloud/gophercloud/pull/2573) Enable tests for object versioning +* [GH-2576](https://github.com/gophercloud/gophercloud/pull/2576) keystone: add v3 OS-FEDERATION mappings delete operation +* [GH-2578](https://github.com/gophercloud/gophercloud/pull/2578) Add periodic jobs for OpenStack zed release and reduce periodic jobs frequency +* [GH-2580](https://github.com/gophercloud/gophercloud/pull/2580) [neutron v2]: Add support for network segments update +* [GH-2583](https://github.com/gophercloud/gophercloud/pull/2583) Add missing rule protocol constants for IPIP +* [GH-2584](https://github.com/gophercloud/gophercloud/pull/2584) CI: workaround mongodb dependency for messaging and clustering master jobs +* [GH-2587](https://github.com/gophercloud/gophercloud/pull/2587) fix: Incorrect Documentation +* [GH-2593](https://github.com/gophercloud/gophercloud/pull/2593) Make TestMTUNetworkCRUDL deterministic +* [GH-2594](https://github.com/gophercloud/gophercloud/pull/2594) Bump actions/setup-go from 3 to 4 + + +## v1.2.0 (2023-01-27) + +Starting with this version, Gophercloud sends its actual version in the +user-agent string in the format `gophercloud/v1.2.0`. It no longer sends the +hardcoded string `gophercloud/2.0.0`. + +* [GH-2542](https://github.com/gophercloud/gophercloud/pull/2542) Add field hidden in containerinfra/v1/clustertemplates +* [GH-2537](https://github.com/gophercloud/gophercloud/pull/2537) Support value_specs for Ports +* [GH-2530](https://github.com/gophercloud/gophercloud/pull/2530) keystone: add v3 OS-FEDERATION mappings create operation +* [GH-2519](https://github.com/gophercloud/gophercloud/pull/2519) Modify user-agent header to ensure current gophercloud version is provided + +## v1.1.1 (2022-12-07) + +The GOPROXY cache for v1.1.0 was corrupted with a tag pointing to the wrong commit. This release fixes the problem by exposing a new release with the same content. + +Please use `v1.1.1` instead of `v1.1.0` to avoid cache issues. + +## v1.1.0 (2022-11-24) + +* [GH-2513](https://github.com/gophercloud/gophercloud/pull/2513) objectstorage: Do not parse NoContent responses +* [GH-2503](https://github.com/gophercloud/gophercloud/pull/2503) Bump golang.org/x/crypto +* [GH-2501](https://github.com/gophercloud/gophercloud/pull/2501) Staskraev/l3 agent scheduler +* [GH-2496](https://github.com/gophercloud/gophercloud/pull/2496) Manila: add Get for share-access-rules API +* [GH-2491](https://github.com/gophercloud/gophercloud/pull/2491) Add VipQosPolicyID to loadbalancer Create and Update +* [GH-2488](https://github.com/gophercloud/gophercloud/pull/2488) Add Persistance for octavia pools.UpdateOpts +* [GH-2487](https://github.com/gophercloud/gophercloud/pull/2487) Add Prometheus protocol for octavia listeners +* [GH-2482](https://github.com/gophercloud/gophercloud/pull/2482) Add createdAt, updatedAt and provisionUpdatedAt fields in Baremetal V1 nodes +* [GH-2479](https://github.com/gophercloud/gophercloud/pull/2479) Add service_types support for neutron subnet +* [GH-2477](https://github.com/gophercloud/gophercloud/pull/2477) Port CreatedAt and UpdatedAt: add back JSON tags +* [GH-2475](https://github.com/gophercloud/gophercloud/pull/2475) Support old time format for port CreatedAt and UpdatedAt +* [GH-2474](https://github.com/gophercloud/gophercloud/pull/2474) Implementing re-image volumeaction +* [GH-2470](https://github.com/gophercloud/gophercloud/pull/2470) keystone: add v3 limits GetEnforcementModel operation +* [GH-2468](https://github.com/gophercloud/gophercloud/pull/2468) keystone: add v3 OS-FEDERATION extension List Mappings +* [GH-2458](https://github.com/gophercloud/gophercloud/pull/2458) Fix typo in blockstorage/v3/attachments docs +* [GH-2456](https://github.com/gophercloud/gophercloud/pull/2456) Add support for Update for flavors +* [GH-2453](https://github.com/gophercloud/gophercloud/pull/2453) Add description to flavor +* [GH-2417](https://github.com/gophercloud/gophercloud/pull/2417) Neutron v2: ScheduleBGPSpeakerOpts, RemoveBGPSpeaker, Lis… + +## 1.0.0 (2022-08-29) + +UPGRADE NOTES + PROMISE OF COMPATIBILITY + +* Introducing Gophercloud v1! Like for every other release so far, all clients will upgrade automatically with `go get -d github.com/gophercloud/gophercloud` unless the dependency is pinned in `go.mod`. +* Gophercloud v1 comes with a promise of compatibility: no breaking changes are expected to merge before v2.0.0. + +IMPROVEMENTS + +* Added `compute.v2/extensions/services.Delete` [GH-2427](https://github.com/gophercloud/gophercloud/pull/2427) +* Added support for `standard-attr-revisions` to `networking/v2/networks`, `networking/v2/ports`, and `networking/v2/subnets` [GH-2437](https://github.com/gophercloud/gophercloud/pull/2437) +* Added `updated_at` and `created_at` fields to `networking/v2/ports.Port` [GH-2445](https://github.com/gophercloud/gophercloud/pull/2445) + +## 0.25.0 (May 30, 2022) + +BREAKING CHANGES + +* Replaced `blockstorage/noauth.NewBlockStorageNoAuth` with `NewBlockStorageNoAuthV2` and `NewBlockStorageNoAuthV3` [GH-2343](https://github.com/gophercloud/gophercloud/pull/2343) +* Renamed `blockstorage/extensions/schedulerstats.Capabilities`'s `GoodnessFuction` field to `GoodnessFunction` [GH-2346](https://github.com/gophercloud/gophercloud/pull/2346) + +IMPROVEMENTS + +* Added `RequestOpts.OmitHeaders` to provider client [GH-2315](https://github.com/gophercloud/gophercloud/pull/2315) +* Added `identity/v3/extensions/projectendpoints.List` [GH-2304](https://github.com/gophercloud/gophercloud/pull/2304) +* Added `identity/v3/extensions/projectendpoints.Create` [GH-2304](https://github.com/gophercloud/gophercloud/pull/2304) +* Added `identity/v3/extensions/projectendpoints.Delete` [GH-2304](https://github.com/gophercloud/gophercloud/pull/2304) +* Added protocol `any` to `networking/v2/extensions/security/rules.Create` [GH-2310](https://github.com/gophercloud/gophercloud/pull/2310) +* Added `REDIRECT_PREFIX` and `REDIRECT_HTTP_CODE` to `loadbalancer/v2/l7policies.Create` [GH-2324](https://github.com/gophercloud/gophercloud/pull/2324) +* Added `SOURCE_IP_PORT` LB method to `loadbalancer/v2/pools.Create` [GH-2300](https://github.com/gophercloud/gophercloud/pull/2300) +* Added `AllocatedCapacityGB` capability to `blockstorage/extensions/schedulerstats.Capabilities` [GH-2348](https://github.com/gophercloud/gophercloud/pull/2348) +* Added `Metadata` to `dns/v2/recordset.RecordSet` [GH-2353](https://github.com/gophercloud/gophercloud/pull/2353) +* Added missing fields to `compute/v2/extensions/servergroups.List` [GH-2355](https://github.com/gophercloud/gophercloud/pull/2355) +* Added missing labels fields to `containerinfra/v1/nodegroups` [GH-2377](https://github.com/gophercloud/gophercloud/pull/2377) +* Added missing fields to `loadbalancer/v2/listeners.Listener` [GH-2407](https://github.com/gophercloud/gophercloud/pull/2407) +* Added `identity/v3/limits.List` [GH-2360](https://github.com/gophercloud/gophercloud/pull/2360) +* Added `ParentProviderUUID` to `placement/v1/resourceproviders.Create` [GH-2356](https://github.com/gophercloud/gophercloud/pull/2356) +* Added `placement/v1/resourceproviders.Delete` [GH-2357](https://github.com/gophercloud/gophercloud/pull/2357) +* Added `placement/v1/resourceproviders.Get` [GH-2358](https://github.com/gophercloud/gophercloud/pull/2358) +* Added `placement/v1/resourceproviders.Update` [GH-2359](https://github.com/gophercloud/gophercloud/pull/2359) +* Added `networking/v2/extensions/bgp/peers.List` [GH-2241](https://github.com/gophercloud/gophercloud/pull/2241) +* Added `networking/v2/extensions/bgp/peers.Get` [GH-2241](https://github.com/gophercloud/gophercloud/pull/2241) +* Added `networking/v2/extensions/bgp/peers.Create` [GH-2388](https://github.com/gophercloud/gophercloud/pull/2388) +* Added `networking/v2/extensions/bgp/peers.Delete` [GH-2388](https://github.com/gophercloud/gophercloud/pull/2388) +* Added `networking/v2/extensions/bgp/peers.Update` [GH-2396](https://github.com/gophercloud/gophercloud/pull/2396) +* Added `networking/v2/extensions/bgp/speakers.Create` [GH-2395](https://github.com/gophercloud/gophercloud/pull/2395) +* Added `networking/v2/extensions/bgp/speakers.Delete` [GH-2395](https://github.com/gophercloud/gophercloud/pull/2395) +* Added `networking/v2/extensions/bgp/speakers.Update` [GH-2400](https://github.com/gophercloud/gophercloud/pull/2400) +* Added `networking/v2/extensions/bgp/speakers.AddBGPPeer` [GH-2400](https://github.com/gophercloud/gophercloud/pull/2400) +* Added `networking/v2/extensions/bgp/speakers.RemoveBGPPeer` [GH-2400](https://github.com/gophercloud/gophercloud/pull/2400) +* Added `networking/v2/extensions/bgp/speakers.GetAdvertisedRoutes` [GH-2406](https://github.com/gophercloud/gophercloud/pull/2406) +* Added `networking/v2/extensions/bgp/speakers.AddGatewayNetwork` [GH-2406](https://github.com/gophercloud/gophercloud/pull/2406) +* Added `networking/v2/extensions/bgp/speakers.RemoveGatewayNetwork` [GH-2406](https://github.com/gophercloud/gophercloud/pull/2406) +* Added `baremetal/v1/nodes.SetMaintenance` and `baremetal/v1/nodes.UnsetMaintenance` [GH-2384](https://github.com/gophercloud/gophercloud/pull/2384) +* Added `sharedfilesystems/v2/services.List` [GH-2350](https://github.com/gophercloud/gophercloud/pull/2350) +* Added `sharedfilesystems/v2/schedulerstats.List` [GH-2350](https://github.com/gophercloud/gophercloud/pull/2350) +* Added `sharedfilesystems/v2/schedulerstats.ListDetail` [GH-2350](https://github.com/gophercloud/gophercloud/pull/2350) +* Added ability to handle 502 and 504 errors [GH-2245](https://github.com/gophercloud/gophercloud/pull/2245) +* Added `IncludeSubtree` to `identity/v3/roles.ListAssignments` [GH-2411](https://github.com/gophercloud/gophercloud/pull/2411) + +## 0.24.0 (December 13, 2021) + +UPGRADE NOTES + +* Set Go minimum version to 1.14 [GH-2294](https://github.com/gophercloud/gophercloud/pull/2294) + +IMPROVEMENTS + +* Added `blockstorage/v3/qos.Get` [GH-2283](https://github.com/gophercloud/gophercloud/pull/2283) +* Added `blockstorage/v3/qos.Update` [GH-2283](https://github.com/gophercloud/gophercloud/pull/2283) +* Added `blockstorage/v3/qos.DeleteKeys` [GH-2283](https://github.com/gophercloud/gophercloud/pull/2283) +* Added `blockstorage/v3/qos.Associate` [GH-2284](https://github.com/gophercloud/gophercloud/pull/2284) +* Added `blockstorage/v3/qos.Disassociate` [GH-2284](https://github.com/gophercloud/gophercloud/pull/2284) +* Added `blockstorage/v3/qos.DisassociateAll` [GH-2284](https://github.com/gophercloud/gophercloud/pull/2284) +* Added `blockstorage/v3/qos.ListAssociations` [GH-2284](https://github.com/gophercloud/gophercloud/pull/2284) + +## 0.23.0 (November 12, 2021) + +IMPROVEMENTS + +* Added `networking/v2/extensions/agents.ListBGPSpeakers` [GH-2229](https://github.com/gophercloud/gophercloud/pull/2229) +* Added `networking/v2/extensions/bgp/speakers.BGPSpeaker` [GH-2229](https://github.com/gophercloud/gophercloud/pull/2229) +* Added `identity/v3/roles.Project.Domain` [GH-2235](https://github.com/gophercloud/gophercloud/pull/2235) +* Added `identity/v3/roles.User.Domain` [GH-2235](https://github.com/gophercloud/gophercloud/pull/2235) +* Added `identity/v3/roles.Group.Domain` [GH-2235](https://github.com/gophercloud/gophercloud/pull/2235) +* Added `loadbalancer/v2/pools.CreateOpts.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) +* Added `loadbalancer/v2/pools.UpdateOpts.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) +* Added `loadbalancer/v2/pools.Pool.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) +* Added `networking/v2/extensions/bgp/speakers.List` [GH-2238](https://github.com/gophercloud/gophercloud/pull/2238) +* Added `networking/v2/extensions/bgp/speakers.Get` [GH-2238](https://github.com/gophercloud/gophercloud/pull/2238) +* Added `compute/v2/extensions/keypairs.CreateOpts.Type` [GH-2231](https://github.com/gophercloud/gophercloud/pull/2231) +* When doing Keystone re-authentification, keep the error if it failed [GH-2259](https://github.com/gophercloud/gophercloud/pull/2259) +* Added new loadbalancer pool monitor types (TLS-HELLO, UDP-CONNECT and SCTP) [GH-2237](https://github.com/gophercloud/gophercloud/pull/2261) + +## 0.22.0 (October 7, 2021) + +BREAKING CHANGES + +* The types of several Object Storage Update fields have been changed to pointers in order to allow the value to be unset via the HTTP headers: + * `objectstorage/v1/accounts.UpdateOpts.ContentType` + * `objectstorage/v1/accounts.UpdateOpts.DetectContentType` + * `objectstorage/v1/containers.UpdateOpts.ContainerRead` + * `objectstorage/v1/containers.UpdateOpts.ContainerSyncTo` + * `objectstorage/v1/containers.UpdateOpts.ContainerSyncKey` + * `objectstorage/v1/containers.UpdateOpts.ContainerWrite` + * `objectstorage/v1/containers.UpdateOpts.ContentType` + * `objectstorage/v1/containers.UpdateOpts.DetectContentType` + * `objectstorage/v1/objects.UpdateOpts.ContentDisposition` + * `objectstorage/v1/objects.UpdateOpts.ContentEncoding` + * `objectstorage/v1/objects.UpdateOpts.ContentType` + * `objectstorage/v1/objects.UpdateOpts.DeleteAfter` + * `objectstorage/v1/objects.UpdateOpts.DeleteAt` + * `objectstorage/v1/objects.UpdateOpts.DetectContentType` + +BUG FIXES + +* Fixed issue with not being able to unset Object Storage values via HTTP headers [GH-2218](https://github.com/gophercloud/gophercloud/pull/2218) + +IMPROVEMENTS + +* Added `compute/v2/servers.Server.ServerGroups` [GH-2217](https://github.com/gophercloud/gophercloud/pull/2217) +* Added `imageservice/v2/images.ReplaceImageProtected` to allow the `protected` field to be updated [GH-2221](https://github.com/gophercloud/gophercloud/pull/2221) +* More details added to the 404/Not Found error message [GH-2223](https://github.com/gophercloud/gophercloud/pull/2223) +* Added `openstack/baremetal/v1/nodes.CreateSubscriptionOpts.HttpHeaders` [GH-2224](https://github.com/gophercloud/gophercloud/pull/2224) + +## 0.21.0 (September 14, 2021) + +IMPROVEMENTS + +* Added `blockstorage/extensions/volumehost` [GH-2212](https://github.com/gophercloud/gophercloud/pull/2212) +* Added `loadbalancer/v2/listeners.CreateOpts.Tags` [GH-2214](https://github.com/gophercloud/gophercloud/pull/2214) +* Added `loadbalancer/v2/listeners.UpdateOpts.Tags` [GH-2214](https://github.com/gophercloud/gophercloud/pull/2214) +* Added `loadbalancer/v2/listeners.Listener.Tags` [GH-2214](https://github.com/gophercloud/gophercloud/pull/2214) + +## 0.20.0 (August 10, 2021) + +IMPROVEMENTS + +* Added `RetryFunc` to enable custom retry functions. [GH-2194](https://github.com/gophercloud/gophercloud/pull/2194) +* Added `openstack/baremetal/v1/nodes.GetVendorPassthruMethods` [GH-2201](https://github.com/gophercloud/gophercloud/pull/2201) +* Added `openstack/baremetal/v1/nodes.GetAllSubscriptions` [GH-2201](https://github.com/gophercloud/gophercloud/pull/2201) +* Added `openstack/baremetal/v1/nodes.GetSubscription` [GH-2201](https://github.com/gophercloud/gophercloud/pull/2201) +* Added `openstack/baremetal/v1/nodes.DeleteSubscription` [GH-2201](https://github.com/gophercloud/gophercloud/pull/2201) +* Added `openstack/baremetal/v1/nodes.CreateSubscription` [GH-2201](https://github.com/gophercloud/gophercloud/pull/2201) + +## 0.19.0 (July 22, 2021) + +NOTES / BREAKING CHANGES + +* `compute/v2/extensions/keypairs.List` now takes a `ListOptsBuilder` argument [GH-2186](https://github.com/gophercloud/gophercloud/pull/2186) +* `compute/v2/extensions/keypairs.Get` now takes a `GetOptsBuilder` argument [GH-2186](https://github.com/gophercloud/gophercloud/pull/2186) +* `compute/v2/extensions/keypairs.Delete` now takes a `DeleteOptsBuilder` argument [GH-2186](https://github.com/gophercloud/gophercloud/pull/2186) +* `compute/v2/extensions/hypervisors.List` now takes a `ListOptsBuilder` argument [GH-2187](https://github.com/gophercloud/gophercloud/pull/2187) + +IMPROVEMENTS + +* Added `blockstorage/v3/qos.List` [GH-2167](https://github.com/gophercloud/gophercloud/pull/2167) +* Added `compute/v2/extensions/volumeattach.CreateOpts.Tag` [GH-2177](https://github.com/gophercloud/gophercloud/pull/2177) +* Added `compute/v2/extensions/volumeattach.CreateOpts.DeleteOnTermination` [GH-2177](https://github.com/gophercloud/gophercloud/pull/2177) +* Added `compute/v2/extensions/volumeattach.VolumeAttachment.Tag` [GH-2177](https://github.com/gophercloud/gophercloud/pull/2177) +* Added `compute/v2/extensions/volumeattach.VolumeAttachment.DeleteOnTermination` [GH-2177](https://github.com/gophercloud/gophercloud/pull/2177) +* Added `db/v1/instances.Instance.Address` [GH-2179](https://github.com/gophercloud/gophercloud/pull/2179) +* Added `compute/v2/servers.ListOpts.AvailabilityZone` [GH-2098](https://github.com/gophercloud/gophercloud/pull/2098) +* Added `compute/v2/extensions/keypairs.ListOpts` [GH-2186](https://github.com/gophercloud/gophercloud/pull/2186) +* Added `compute/v2/extensions/keypairs.GetOpts` [GH-2186](https://github.com/gophercloud/gophercloud/pull/2186) +* Added `compute/v2/extensions/keypairs.DeleteOpts` [GH-2186](https://github.com/gophercloud/gophercloud/pull/2186) +* Added `objectstorage/v2/containers.GetHeader.Timestamp` [GH-2185](https://github.com/gophercloud/gophercloud/pull/2185) +* Added `compute/v2/extensions.ListOpts` [GH-2187](https://github.com/gophercloud/gophercloud/pull/2187) +* Added `sharedfilesystems/v2/shares.Share.CreateShareFromSnapshotSupport` [GH-2191](https://github.com/gophercloud/gophercloud/pull/2191) +* Added `compute/v2/servers.Network.Tag` for use in `CreateOpts` [GH-2193](https://github.com/gophercloud/gophercloud/pull/2193) + +## 0.18.0 (June 11, 2021) + +NOTES / BREAKING CHANGES + +* As of [GH-2160](https://github.com/gophercloud/gophercloud/pull/2160), Gophercloud no longer URL encodes Object Storage containers and object names. You can still encode them yourself before passing the names to the Object Storage functions. + +* `baremetal/v1/nodes.ListBIOSSettings` now takes three parameters. The third, new, parameter is `ListBIOSSettingsOptsBuilder` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) + +BUG FIXES + +* Fixed expected OK codes to use default codes [GH-2173](https://github.com/gophercloud/gophercloud/pull/2173) +* Fixed inablity to create sub-containers (objects with `/` in their name) [GH-2160](https://github.com/gophercloud/gophercloud/pull/2160) + +IMPROVEMENTS + +* Added `orchestration/v1/stacks.ListOpts.ShowHidden` [GH-2104](https://github.com/gophercloud/gophercloud/pull/2104) +* Added `loadbalancer/v2/listeners.ProtocolSCTP` [GH-2149](https://github.com/gophercloud/gophercloud/pull/2149) +* Added `loadbalancer/v2/listeners.CreateOpts.TLSVersions` [GH-2150](https://github.com/gophercloud/gophercloud/pull/2150) +* Added `loadbalancer/v2/listeners.UpdateOpts.TLSVersions` [GH-2150](https://github.com/gophercloud/gophercloud/pull/2150) +* Added `baremetal/v1/nodes.CreateOpts.NetworkData` [GH-2154](https://github.com/gophercloud/gophercloud/pull/2154) +* Added `baremetal/v1/nodes.Node.NetworkData` [GH-2154](https://github.com/gophercloud/gophercloud/pull/2154) +* Added `loadbalancer/v2/pools.ProtocolPROXYV2` [GH-2158](https://github.com/gophercloud/gophercloud/pull/2158) +* Added `loadbalancer/v2/pools.ProtocolSCTP` [GH-2158](https://github.com/gophercloud/gophercloud/pull/2158) +* Added `placement/v1/resourceproviders.GetAllocations` [GH-2162](https://github.com/gophercloud/gophercloud/pull/2162) +* Added `baremetal/v1/nodes.CreateOpts.BIOSInterface` [GH-2164](https://github.com/gophercloud/gophercloud/pull/2164) +* Added `baremetal/v1/nodes.Node.BIOSInterface` [GH-2164](https://github.com/gophercloud/gophercloud/pull/2164) +* Added `baremetal/v1/nodes.NodeValidation.BIOS` [GH-2164](https://github.com/gophercloud/gophercloud/pull/2164) +* Added `baremetal/v1/nodes.ListBIOSSettings` [GH-2171](https://github.com/gophercloud/gophercloud/pull/2171) +* Added `baremetal/v1/nodes.GetBIOSSetting` [GH-2171](https://github.com/gophercloud/gophercloud/pull/2171) +* Added `baremetal/v1/nodes.ListBIOSSettingsOpts` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.AttributeType` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.AllowableValues` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.LowerBound` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.UpperBound` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.MinLength` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.MaxLength` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.ReadOnly` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.ResetRequired` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) +* Added `baremetal/v1/nodes.BIOSSetting.Unique` [GH-2174](https://github.com/gophercloud/gophercloud/pull/2174) + +## 0.17.0 (April 9, 2021) + +IMPROVEMENTS + +* `networking/v2/extensions/quotas.QuotaDetail.Reserved` can handle both `int` and `string` values [GH-2126](https://github.com/gophercloud/gophercloud/pull/2126) +* Added `blockstorage/v3/volumetypes.ListExtraSpecs` [GH-2123](https://github.com/gophercloud/gophercloud/pull/2123) +* Added `blockstorage/v3/volumetypes.GetExtraSpec` [GH-2123](https://github.com/gophercloud/gophercloud/pull/2123) +* Added `blockstorage/v3/volumetypes.CreateExtraSpecs` [GH-2123](https://github.com/gophercloud/gophercloud/pull/2123) +* Added `blockstorage/v3/volumetypes.UpdateExtraSpec` [GH-2123](https://github.com/gophercloud/gophercloud/pull/2123) +* Added `blockstorage/v3/volumetypes.DeleteExtraSpec` [GH-2123](https://github.com/gophercloud/gophercloud/pull/2123) +* Added `identity/v3/roles.ListAssignmentOpts.IncludeNames` [GH-2133](https://github.com/gophercloud/gophercloud/pull/2133) +* Added `identity/v3/roles.AssignedRoles.Name` [GH-2133](https://github.com/gophercloud/gophercloud/pull/2133) +* Added `identity/v3/roles.Domain.Name` [GH-2133](https://github.com/gophercloud/gophercloud/pull/2133) +* Added `identity/v3/roles.Project.Name` [GH-2133](https://github.com/gophercloud/gophercloud/pull/2133) +* Added `identity/v3/roles.User.Name` [GH-2133](https://github.com/gophercloud/gophercloud/pull/2133) +* Added `identity/v3/roles.Group.Name` [GH-2133](https://github.com/gophercloud/gophercloud/pull/2133) +* Added `blockstorage/extensions/availabilityzones.List` [GH-2135](https://github.com/gophercloud/gophercloud/pull/2135) +* Added `blockstorage/v3/volumetypes.ListAccesses` [GH-2138](https://github.com/gophercloud/gophercloud/pull/2138) +* Added `blockstorage/v3/volumetypes.AddAccess` [GH-2138](https://github.com/gophercloud/gophercloud/pull/2138) +* Added `blockstorage/v3/volumetypes.RemoveAccess` [GH-2138](https://github.com/gophercloud/gophercloud/pull/2138) +* Added `blockstorage/v3/qos.Create` [GH-2140](https://github.com/gophercloud/gophercloud/pull/2140) +* Added `blockstorage/v3/qos.Delete` [GH-2140](https://github.com/gophercloud/gophercloud/pull/2140) + +## 0.16.0 (February 23, 2021) + +UPGRADE NOTES + +* `baremetal/v1/nodes.CleanStep.Interface` has changed from `string` to `StepInterface` [GH-2120](https://github.com/gophercloud/gophercloud/pull/2120) + +BUG FIXES + +* Fixed `xor` logic issues in `loadbalancers/v2/l7policies.CreateOpts` [GH-2087](https://github.com/gophercloud/gophercloud/pull/2087) +* Fixed `xor` logic issues in `loadbalancers/v2/listeners.CreateOpts` [GH-2087](https://github.com/gophercloud/gophercloud/pull/2087) +* Fixed `If-Modified-Since` so it's correctly sent in a `objectstorage/v1/objects.Download` request [GH-2108](https://github.com/gophercloud/gophercloud/pull/2108) +* Fixed `If-Unmodified-Since` so it's correctly sent in a `objectstorage/v1/objects.Download` request [GH-2108](https://github.com/gophercloud/gophercloud/pull/2108) + +IMPROVEMENTS + +* Added `blockstorage/extensions/limits.Get` [GH-2084](https://github.com/gophercloud/gophercloud/pull/2084) +* `clustering/v1/clusters.RemoveNodes` now returns an `ActionResult` [GH-2089](https://github.com/gophercloud/gophercloud/pull/2089) +* Added `identity/v3/projects.ListAvailable` [GH-2090](https://github.com/gophercloud/gophercloud/pull/2090) +* Added `blockstorage/extensions/backups.ListDetail` [GH-2085](https://github.com/gophercloud/gophercloud/pull/2085) +* Allow all ports to be removed in `networking/v2/extensions/fwaas_v2/groups.UpdateOpts` [GH-2073] +* Added `imageservice/v2/images.ListOpts.Hidden` [GH-2094](https://github.com/gophercloud/gophercloud/pull/2094) +* Added `imageservice/v2/images.CreateOpts.Hidden` [GH-2094](https://github.com/gophercloud/gophercloud/pull/2094) +* Added `imageservice/v2/images.ReplaceImageHidden` [GH-2094](https://github.com/gophercloud/gophercloud/pull/2094) +* Added `imageservice/v2/images.Image.Hidden` [GH-2094](https://github.com/gophercloud/gophercloud/pull/2094) +* Added `containerinfra/v1/clusters.CreateOpts.MasterLBEnabled` [GH-2102](https://github.com/gophercloud/gophercloud/pull/2102) +* Added the ability to define a custom function to handle "Retry-After" (429) responses [GH-2097](https://github.com/gophercloud/gophercloud/pull/2097) +* Added `baremetal/v1/nodes.JBOD` constant for the `RAIDLevel` type [GH-2103](https://github.com/gophercloud/gophercloud/pull/2103) +* Added support for Block Storage quotas of volume typed resources [GH-2109](https://github.com/gophercloud/gophercloud/pull/2109) +* Added `blockstorage/extensions/volumeactions.ChangeType` [GH-2113](https://github.com/gophercloud/gophercloud/pull/2113) +* Added `baremetal/v1/nodes.DeployStep` [GH-2120](https://github.com/gophercloud/gophercloud/pull/2120) +* Added `baremetal/v1/nodes.ProvisionStateOpts.DeploySteps` [GH-2120](https://github.com/gophercloud/gophercloud/pull/2120) +* Added `baremetal/v1/nodes.CreateOpts.AutomatedClean` [GH-2122](https://github.com/gophercloud/gophercloud/pull/2122) + +## 0.15.0 (December 27, 2020) + +BREAKING CHANGES + +* `compute/v2/extensions/servergroups.List` now takes a `ListOpts` parameter. You can pass `nil` if you don't need to use this. + +IMPROVEMENTS + +* Added `loadbalancer/v2/pools.CreateMemberOpts.Tags` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.UpdateMemberOpts.Backup` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.UpdateMemberOpts.MonitorAddress` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.UpdateMemberOpts.MonitorPort` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.UpdateMemberOpts.Tags` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.BatchUpdateMemberOpts.Backup` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.BatchUpdateMemberOpts.MonitorAddress` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.BatchUpdateMemberOpts.MonitorPort` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `loadbalancer/v2/pools.BatchUpdateMemberOpts.Tags` [GH-2056](https://github.com/gophercloud/gophercloud/pull/2056) +* Added `networking/v2/extensions/quotas.GetDetail` [GH-2061](https://github.com/gophercloud/gophercloud/pull/2061) +* Added `networking/v2/extensions/quotas.UpdateOpts.Trunk` [GH-2061](https://github.com/gophercloud/gophercloud/pull/2061) +* Added `objectstorage/v1/accounts.UpdateOpts.RemoveMetadata` [GH-2063](https://github.com/gophercloud/gophercloud/pull/2063) +* Added `objectstorage/v1/objects.UpdateOpts.RemoveMetadata` [GH-2063](https://github.com/gophercloud/gophercloud/pull/2063) +* Added `identity/v3/catalog.List` [GH-2067](https://github.com/gophercloud/gophercloud/pull/2067) +* Added `networking/v2/extensions/fwaas_v2/policies.List` [GH-2057](https://github.com/gophercloud/gophercloud/pull/2057) +* Added `networking/v2/extensions/fwaas_v2/policies.Create` [GH-2057](https://github.com/gophercloud/gophercloud/pull/2057) +* Added `networking/v2/extensions/fwaas_v2/policies.Get` [GH-2057](https://github.com/gophercloud/gophercloud/pull/2057) +* Added `networking/v2/extensions/fwaas_v2/policies.Update` [GH-2057](https://github.com/gophercloud/gophercloud/pull/2057) +* Added `networking/v2/extensions/fwaas_v2/policies.Delete` [GH-2057](https://github.com/gophercloud/gophercloud/pull/2057) +* Added `compute/v2/extensions/servergroups.ListOpts.AllProjects` [GH-2070](https://github.com/gophercloud/gophercloud/pull/2070) +* Added `objectstorage/v1/containers.CreateOpts.StoragePolicy` [GH-2075](https://github.com/gophercloud/gophercloud/pull/2075) +* Added `blockstorage/v3/snapshots.Update` [GH-2081](https://github.com/gophercloud/gophercloud/pull/2081) +* Added `loadbalancer/v2/l7policies.CreateOpts.Rules` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/listeners.CreateOpts.DefaultPool` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/listeners.CreateOpts.L7Policies` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/listeners.Listener.DefaultPool` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/loadbalancers.CreateOpts.Listeners` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/loadbalancers.CreateOpts.Pools` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/pools.CreateOpts.Members` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) +* Added `loadbalancer/v2/pools.CreateOpts.Monitor` [GH-2077](https://github.com/gophercloud/gophercloud/pull/2077) + + +## 0.14.0 (November 11, 2020) + +IMPROVEMENTS + +* Added `identity/v3/endpoints.Endpoint.Enabled` [GH-2030](https://github.com/gophercloud/gophercloud/pull/2030) +* Added `containerinfra/v1/clusters.Upgrade` [GH-2032](https://github.com/gophercloud/gophercloud/pull/2032) +* Added `compute/apiversions.List` [GH-2037](https://github.com/gophercloud/gophercloud/pull/2037) +* Added `compute/apiversions.Get` [GH-2037](https://github.com/gophercloud/gophercloud/pull/2037) +* Added `compute/v2/servers.ListOpts.IP` [GH-2038](https://github.com/gophercloud/gophercloud/pull/2038) +* Added `compute/v2/servers.ListOpts.IP6` [GH-2038](https://github.com/gophercloud/gophercloud/pull/2038) +* Added `compute/v2/servers.ListOpts.UserID` [GH-2038](https://github.com/gophercloud/gophercloud/pull/2038) +* Added `dns/v2/transfer/accept.List` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `dns/v2/transfer/accept.Get` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `dns/v2/transfer/accept.Create` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `dns/v2/transfer/requests.List` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `dns/v2/transfer/requests.Get` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `dns/v2/transfer/requests.Update` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `dns/v2/transfer/requests.Delete` [GH-2041](https://github.com/gophercloud/gophercloud/pull/2041) +* Added `baremetal/v1/nodes.RescueWait` [GH-2052](https://github.com/gophercloud/gophercloud/pull/2052) +* Added `baremetal/v1/nodes.Unrescuing` [GH-2052](https://github.com/gophercloud/gophercloud/pull/2052) +* Added `networking/v2/extensions/fwaas_v2/groups.List` [GH-2050](https://github.com/gophercloud/gophercloud/pull/2050) +* Added `networking/v2/extensions/fwaas_v2/groups.Get` [GH-2050](https://github.com/gophercloud/gophercloud/pull/2050) +* Added `networking/v2/extensions/fwaas_v2/groups.Create` [GH-2050](https://github.com/gophercloud/gophercloud/pull/2050) +* Added `networking/v2/extensions/fwaas_v2/groups.Update` [GH-2050](https://github.com/gophercloud/gophercloud/pull/2050) +* Added `networking/v2/extensions/fwaas_v2/groups.Delete` [GH-2050](https://github.com/gophercloud/gophercloud/pull/2050) + +BUG FIXES + +* Changed `networking/v2/extensions/layer3/routers.Routes` from `[]Route` to `*[]Route` [GH-2043](https://github.com/gophercloud/gophercloud/pull/2043) + +## 0.13.0 (September 27, 2020) + +IMPROVEMENTS + +* Added `ProtocolTerminatedHTTPS` as a valid listener protocol to `loadbalancer/v2/listeners` [GH-1992](https://github.com/gophercloud/gophercloud/pull/1992) +* Added `objectstorage/v1/objects.CreateTempURLOpts.Timestamp` [GH-1994](https://github.com/gophercloud/gophercloud/pull/1994) +* Added `compute/v2/extensions/schedulerhints.SchedulerHints.DifferentCell` [GH-2012](https://github.com/gophercloud/gophercloud/pull/2012) +* Added `loadbalancer/v2/quotas.Get` [GH-2010](https://github.com/gophercloud/gophercloud/pull/2010) +* Added `messaging/v2/queues.CreateOpts.EnableEncryptMessages` [GH-2016](https://github.com/gophercloud/gophercloud/pull/2016) +* Added `messaging/v2/queues.ListOpts.Name` [GH-2018](https://github.com/gophercloud/gophercloud/pull/2018) +* Added `messaging/v2/queues.ListOpts.WithCount` [GH-2018](https://github.com/gophercloud/gophercloud/pull/2018) +* Added `loadbalancer/v2/quotas.Update` [GH-2023](https://github.com/gophercloud/gophercloud/pull/2023) +* Added `loadbalancer/v2/loadbalancers.ListOpts.AvailabilityZone` [GH-2026](https://github.com/gophercloud/gophercloud/pull/2026) +* Added `loadbalancer/v2/loadbalancers.CreateOpts.AvailabilityZone` [GH-2026](https://github.com/gophercloud/gophercloud/pull/2026) +* Added `loadbalancer/v2/loadbalancers.LoadBalancer.AvailabilityZone` [GH-2026](https://github.com/gophercloud/gophercloud/pull/2026) +* Added `networking/v2/extensions/layer3/routers.ListL3Agents` [GH-2025](https://github.com/gophercloud/gophercloud/pull/2025) + +BUG FIXES + +* Fixed URL escaping in `objectstorage/v1/objects.CreateTempURL` [GH-1994](https://github.com/gophercloud/gophercloud/pull/1994) +* Remove unused `ServiceClient` from `compute/v2/servers.CreateOpts` [GH-2004](https://github.com/gophercloud/gophercloud/pull/2004) +* Changed `objectstorage/v1/objects.CreateOpts.DeleteAfter` from `int` to `int64` [GH-2014](https://github.com/gophercloud/gophercloud/pull/2014) +* Changed `objectstorage/v1/objects.CreateOpts.DeleteAt` from `int` to `int64` [GH-2014](https://github.com/gophercloud/gophercloud/pull/2014) +* Changed `objectstorage/v1/objects.UpdateOpts.DeleteAfter` from `int` to `int64` [GH-2014](https://github.com/gophercloud/gophercloud/pull/2014) +* Changed `objectstorage/v1/objects.UpdateOpts.DeleteAt` from `int` to `int64` [GH-2014](https://github.com/gophercloud/gophercloud/pull/2014) + + +## 0.12.0 (June 25, 2020) + +UPGRADE NOTES + +* The URL used in the `compute/v2/extensions/bootfromvolume` package has been changed from `os-volumes_boot` to `servers`. + +IMPROVEMENTS + +* The URL used in the `compute/v2/extensions/bootfromvolume` package has been changed from `os-volumes_boot` to `servers` [GH-1973](https://github.com/gophercloud/gophercloud/pull/1973) +* Modify `baremetal/v1/nodes.LogicalDisk.PhysicalDisks` type to support physical disks hints [GH-1982](https://github.com/gophercloud/gophercloud/pull/1982) +* Added `baremetalintrospection/httpbasic` which provides an HTTP Basic Auth client [GH-1986](https://github.com/gophercloud/gophercloud/pull/1986) +* Added `baremetal/httpbasic` which provides an HTTP Basic Auth client [GH-1983](https://github.com/gophercloud/gophercloud/pull/1983) +* Added `containerinfra/v1/clusters.CreateOpts.MergeLabels` [GH-1985](https://github.com/gophercloud/gophercloud/pull/1985) + +BUG FIXES + +* Changed `containerinfra/v1/clusters.Cluster.HealthStatusReason` from `string` to `map[string]interface{}` [GH-1968](https://github.com/gophercloud/gophercloud/pull/1968) +* Fixed marshalling of `blockstorage/extensions/backups.ImportBackup.Metadata` [GH-1967](https://github.com/gophercloud/gophercloud/pull/1967) +* Fixed typo of "OAUth" to "OAuth" in `identity/v3/extensions/oauth1` [GH-1969](https://github.com/gophercloud/gophercloud/pull/1969) +* Fixed goroutine leak during reauthentication [GH-1978](https://github.com/gophercloud/gophercloud/pull/1978) +* Changed `baremetalintrospection/v1/introspection.RootDiskType.Size` from `int` to `int64` [GH-1988](https://github.com/gophercloud/gophercloud/pull/1988) + +## 0.11.0 (May 14, 2020) + +UPGRADE NOTES + +* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* All responses now have access to the returned headers. Please report any issues this has caused [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942) +* Changes have been made to the internal HTTP client to ensure response bodies are handled in a way that enables connections to be re-used more efficiently [GH-1952](https://github.com/gophercloud/gophercloud/pull/1952) + +IMPROVEMENTS + +* Added `objectstorage/v1/containers.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* Added `objectstorage/v1/objects.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* All responses now have access to the returned headers [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942) +* Added `compute/v2/extensions/injectnetworkinfo.InjectNetworkInfo` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) +* Added `compute/v2/extensions/resetnetwork.ResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) +* Added `identity/v3/extensions/trusts.ListRoles` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/trusts.GetRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/trusts.CheckRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/oauth1.Create` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.CreateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.DeleteConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListConsumers` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.UpdateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.RequestToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.AuthorizeToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.CreateAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.RevokeAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListAccessTokens` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListAccessTokenRoles` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetAccessTokenRole` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `networking/v2/extensions/agents.Update` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `networking/v2/extensions/agents.Delete` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `networking/v2/extensions/agents.ScheduleDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `networking/v2/extensions/agents.RemoveDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `identity/v3/projects.CreateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.CreateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.UpdateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.UpdateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.Project.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.Options.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `imageservice/v2/images.Image.OpenStackImageImportMethods` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962) +* Added `imageservice/v2/images.Image.OpenStackImageStoreIDs` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962) + +BUG FIXES + +* Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Changed `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from `string` to `*time.Time` [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937) +* Fixed issue with unmarshalling/decoding slices of composed structs [GH-1964](https://github.com/gophercloud/gophercloud/pull/1964) + +## 0.10.0 (April 12, 2020) + +UPGRADE NOTES + +* The various `IDFromName` convenience functions have been moved to https://github.com/gophercloud/utils [GH-1897](https://github.com/gophercloud/gophercloud/pull/1897) +* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) + +IMPROVEMENTS + +* Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891) +* Added `blockstorage/extensions/backups.Export` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894) +* Added `blockstorage/extensions/backups.Import` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894) +* Added `placement/v1/resourceproviders.GetTraits` [GH-1899](https://github.com/gophercloud/gophercloud/pull/1899) +* Added the ability to authenticate with Amazon EC2 Credentials [GH-1900](https://github.com/gophercloud/gophercloud/pull/1900) +* Added ability to list Nova services by binary and host [GH-1904](https://github.com/gophercloud/gophercloud/pull/1904) +* Added `compute/v2/extensions/services.Update` [GH-1902](https://github.com/gophercloud/gophercloud/pull/1902) +* Added system scope to v3 authentication [GH-1908](https://github.com/gophercloud/gophercloud/pull/1908) +* Added `identity/v3/extensions/ec2tokens.ValidateS3Token` [GH-1906](https://github.com/gophercloud/gophercloud/pull/1906) +* Added `containerinfra/v1/clusters.Cluster.HealthStatus` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910) +* Added `containerinfra/v1/clusters.Cluster.HealthStatusReason` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910) +* Added `loadbalancer/v2/amphorae.Failover` [GH-1912](https://github.com/gophercloud/gophercloud/pull/1912) +* Added `identity/v3/extensions/ec2credentials.List` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `identity/v3/extensions/ec2credentials.Get` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `identity/v3/extensions/ec2credentials.Create` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `identity/v3/extensions/ec2credentials.Delete` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `ErrUnexpectedResponseCode.ResponseHeader` [GH-1919](https://github.com/gophercloud/gophercloud/pull/1919) +* Added support for TOTP authentication [GH-1922](https://github.com/gophercloud/gophercloud/pull/1922) +* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) +* Added `sharedfilesystems/v2/shares.GetExportLocation` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) +* Added `sharedfilesystems/v2/shares.Revert` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `sharedfilesystems/v2/shares.ResetStatus` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `sharedfilesystems/v2/shares.ForceDelete` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `sharedfilesystems/v2/shares.Unmanage` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `blockstorage/v3/attachments.Create` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.List` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Get` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Update` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Delete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Complete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) + +BUG FIXES + +* Fixed issue with Orchestration `get_file` only being able to read JSON and YAML files [GH-1915](https://github.com/gophercloud/gophercloud/pull/1915) + +## 0.9.0 (March 10, 2020) + +UPGRADE NOTES + +* The way we implement new API result fields added by microversions has changed. Previously, we would declare a dedicated `ExtractFoo` function in a file called `microversions.go`. Now, we are declaring those fields inline of the original result struct as a pointer. [GH-1854](https://github.com/gophercloud/gophercloud/pull/1854) + +* `compute/v2/servers.CreateOpts.Networks` has changed from `[]Network` to `interface{}` in order to support creating servers that have no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884) + +IMPROVEMENTS + +* Added `compute/v2/extensions/instanceactions.List` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848) +* Added `compute/v2/extensions/instanceactions.Get` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848) +* Added `networking/v2/ports.List.FixedIPs` [GH-1849](https://github.com/gophercloud/gophercloud/pull/1849) +* Added `identity/v3/extensions/trusts.List` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855) +* Added `identity/v3/extensions/trusts.Get` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855) +* Added `identity/v3/extensions/trusts.Trust.ExpiresAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857) +* Added `identity/v3/extensions/trusts.Trust.DeletedAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857) +* Added `compute/v2/extensions/instanceactions.InstanceActionDetail` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851) +* Added `compute/v2/extensions/instanceactions.Event` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851) +* Added `compute/v2/extensions/instanceactions.ListOpts` [GH-1858](https://github.com/gophercloud/gophercloud/pull/1858) +* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) +* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey2` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) +* Added `placement/v1/resourceproviders.GetUsages` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862) +* Added `placement/v1/resourceproviders.GetInventories` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862) +* Added `imageservice/v2/images.ReplaceImageMinRam` [GH-1867](https://github.com/gophercloud/gophercloud/pull/1867) +* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865) +* Added `objectstorage/v1/containers.CreateOpts.TempURLKey2` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865) +* Added `blockstorage/extensions/volumetransfers.List` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) +* Added `blockstorage/extensions/volumetransfers.Create` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) +* Added `blockstorage/extensions/volumetransfers.Accept` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) +* Added `blockstorage/extensions/volumetransfers.Get` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) +* Added `blockstorage/extensions/volumetransfers.Delete` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869) +* Added `blockstorage/extensions/backups.RestoreFromBackup` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871) +* Added `blockstorage/v3/volumes.CreateOpts.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871) +* Added `blockstorage/v3/volumes.Volume.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871) +* Added `identity/v3/projects.ListOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Added `identity/v3/projects.ListOpts.TagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Added `identity/v3/projects.ListOpts.NotTags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Added `identity/v3/projects.ListOpts.NotTagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Added `identity/v3/projects.CreateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Added `identity/v3/projects.UpdateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Added `identity/v3/projects.Project.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882) +* Changed `compute/v2/servers.CreateOpts.Networks` from `[]Network` to `interface{}` to support creating servers with no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884) + + +BUG FIXES + +* Added support for `int64` headers, which were previously being silently dropped [GH-1860](https://github.com/gophercloud/gophercloud/pull/1860) +* Allow image properties with empty values [GH-1875](https://github.com/gophercloud/gophercloud/pull/1875) +* Fixed `compute/v2/extensions/extendedserverattributes.ServerAttributesExt.Userdata` JSON tag [GH-1881](https://github.com/gophercloud/gophercloud/pull/1881) + +## 0.8.0 (February 8, 2020) + +UPGRADE NOTES + +* The behavior of `keymanager/v1/acls.SetOpts` has changed. Instead of a struct, it is now `[]SetOpt`. See [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816) for implementation details. + +IMPROVEMENTS + +* The result of `containerinfra/v1/clusters.Resize` now returns only the UUID when calling `Extract`. This is a backwards-breaking change from the previous struct that was returned [GH-1649](https://github.com/gophercloud/gophercloud/pull/1649) +* Added `compute/v2/extensions/shelveunshelve.Shelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799) +* Added `compute/v2/extensions/shelveunshelve.ShelveOffload` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799) +* Added `compute/v2/extensions/shelveunshelve.Unshelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799) +* Added `containerinfra/v1/nodegroups.Get` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774) +* Added `containerinfra/v1/nodegroups.List` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774) +* Added `orchestration/v1/resourcetypes.List` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806) +* Added `orchestration/v1/resourcetypes.GetSchema` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806) +* Added `orchestration/v1/resourcetypes.GenerateTemplate` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806) +* Added `keymanager/v1/acls.SetOpt` and changed `keymanager/v1/acls.SetOpts` to `[]SetOpt` [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816) +* Added `blockstorage/apiversions.List` [GH-458](https://github.com/gophercloud/gophercloud/pull/458) +* Added `blockstorage/apiversions.Get` [GH-458](https://github.com/gophercloud/gophercloud/pull/458) +* Added `StatusCodeError` interface and `GetStatusCode` convenience method [GH-1820](https://github.com/gophercloud/gophercloud/pull/1820) +* Added pagination support to `compute/v2/extensions/usage.SingleTenant` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819) +* Added pagination support to `compute/v2/extensions/usage.AllTenants` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819) +* Added `placement/v1/resourceproviders.List` [GH-1815](https://github.com/gophercloud/gophercloud/pull/1815) +* Allow `CreateMemberOptsBuilder` to be passed in `loadbalancer/v2/pools.Create` [GH-1822](https://github.com/gophercloud/gophercloud/pull/1822) +* Added `Backup` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) +* Added `MonitorAddress` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) +* Added `MonitorPort` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) +* Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818) +* Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835](https://github.com/gophercloud/gophercloud/pull/1835) +* Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842) +* Added `placement/v1/resourceproviders.Create` [GH-1841](https://github.com/gophercloud/gophercloud/pull/1841) +* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) +* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) +* Added `blockstorage/extensions/volumeactions.VolumeImage.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) +* Added `blockstorage/extensions/volumeactions.VolumeImage.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) + +BUG FIXES + +* Changed `sort_key` to `sort_keys` in ` workflow/v2/crontriggers.ListOpts` [GH-1809](https://github.com/gophercloud/gophercloud/pull/1809) +* Allow `blockstorage/extensions/schedulerstats.Capabilities.MaxOverSubscriptionRatio` to accept both string and int/float responses [GH-1817](https://github.com/gophercloud/gophercloud/pull/1817) +* Fixed bug in `NewLoadBalancerV2` for situations when the LBaaS service was advertised without a `/v2.0` endpoint [GH-1829](https://github.com/gophercloud/gophercloud/pull/1829) +* Fixed JSON tags in `baremetal/v1/ports.UpdateOperation` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840) +* Fixed JSON tags in `networking/v2/extensions/lbaas/vips.commonResult.Extract()` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840) + +## 0.7.0 (December 3, 2019) + +IMPROVEMENTS + +* Allow a token to be used directly for authentication instead of generating a new token based on a given token [GH-1752](https://github.com/gophercloud/gophercloud/pull/1752) +* Moved `tags.ServerTagsExt` to servers.TagsExt` [GH-1760](https://github.com/gophercloud/gophercloud/pull/1760) +* Added `tags`, `tags-any`, `not-tags`, and `not-tags-any` to `compute/v2/servers.ListOpts` [GH-1759](https://github.com/gophercloud/gophercloud/pull/1759) +* Added `AccessRule` to `identity/v3/applicationcredentials` [GH-1758](https://github.com/gophercloud/gophercloud/pull/1758) +* Gophercloud no longer returns an error when multiple endpoints are found. Instead, it will choose the first endpoint and discard the others [GH-1766](https://github.com/gophercloud/gophercloud/pull/1766) +* Added `networking/v2/extensions/fwaas_v2/rules.Create` [GH-1768](https://github.com/gophercloud/gophercloud/pull/1768) +* Added `networking/v2/extensions/fwaas_v2/rules.Delete` [GH-1771](https://github.com/gophercloud/gophercloud/pull/1771) +* Added `loadbalancer/v2/providers.List` [GH-1765](https://github.com/gophercloud/gophercloud/pull/1765) +* Added `networking/v2/extensions/fwaas_v2/rules.Get` [GH-1772](https://github.com/gophercloud/gophercloud/pull/1772) +* Added `networking/v2/extensions/fwaas_v2/rules.Update` [GH-1776](https://github.com/gophercloud/gophercloud/pull/1776) +* Added `networking/v2/extensions/fwaas_v2/rules.List` [GH-1783](https://github.com/gophercloud/gophercloud/pull/1783) +* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.CreateOpts` [GH-1785](https://github.com/gophercloud/gophercloud/pull/1785) +* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.UpdateOpts` [GH-1786](https://github.com/gophercloud/gophercloud/pull/1786) +* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.Monitor` [GH-1787](https://github.com/gophercloud/gophercloud/pull/1787) +* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.ListOpts` [GH-1788](https://github.com/gophercloud/gophercloud/pull/1788) +* Updated `go.mod` dependencies, specifically to account for CVE-2019-11840 with `golang.org/x/crypto` [GH-1793](https://github.com/gophercloud/gophercloud/pull/1788) + +## 0.6.0 (October 17, 2019) + +UPGRADE NOTES + +* The way reauthentication works has been refactored. This should not cause a problem, but please report bugs if it does. See [GH-1746](https://github.com/gophercloud/gophercloud/pull/1746) for more information. + +IMPROVEMENTS + +* Added `networking/v2/extensions/quotas.Get` [GH-1742](https://github.com/gophercloud/gophercloud/pull/1742) +* Added `networking/v2/extensions/quotas.Update` [GH-1747](https://github.com/gophercloud/gophercloud/pull/1747) +* Refactored the reauthentication implementation to use goroutines and added a check to prevent an infinite loop in certain situations. [GH-1746](https://github.com/gophercloud/gophercloud/pull/1746) + +BUG FIXES + +* Changed `Flavor` to `FlavorID` in `loadbalancer/v2/loadbalancers` [GH-1744](https://github.com/gophercloud/gophercloud/pull/1744) +* Changed `Flavor` to `FlavorID` in `networking/v2/extensions/lbaas_v2/loadbalancers` [GH-1744](https://github.com/gophercloud/gophercloud/pull/1744) +* The `go-yaml` dependency was updated to `v2.2.4` to fix possible DDOS vulnerabilities [GH-1751](https://github.com/gophercloud/gophercloud/pull/1751) + +## 0.5.0 (October 13, 2019) + +IMPROVEMENTS + +* Added `VolumeType` to `compute/v2/extensions/bootfromvolume.BlockDevice`[GH-1690](https://github.com/gophercloud/gophercloud/pull/1690) +* Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688](https://github.com/gophercloud/gophercloud/pull/1688) +* Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698](https://github.com/gophercloud/gophercloud/pull/1696) +* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696](https://github.com/gophercloud/gophercloud/pull/1696) +* Added `compute/v2/extensions/tags.Add` [GH-1696](https://github.com/gophercloud/gophercloud/pull/1696) +* Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703](https://github.com/gophercloud/gophercloud/pull/1703) +* Added `ExtractDomain` method to token results in `identity/v3/tokens` [GH-1712](https://github.com/gophercloud/gophercloud/pull/1712) +* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.CreateOpts` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710) +* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710) +* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.Listener` [GH-1710](https://github.com/gophercloud/gophercloud/pull/1710) +* Added `compute/v2/extensions/tags.Add` [GH-1695](https://github.com/gophercloud/gophercloud/pull/1695) +* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1694](https://github.com/gophercloud/gophercloud/pull/1694) +* Added `compute/v2/extensions/tags.Delete` [GH-1699](https://github.com/gophercloud/gophercloud/pull/1699) +* Added `compute/v2/extensions/tags.DeleteAll` [GH-1700](https://github.com/gophercloud/gophercloud/pull/1700) +* Added `ImageStatusImporting` as an image status [GH-1725](https://github.com/gophercloud/gophercloud/pull/1725) +* Added `ByPath` to `baremetalintrospection/v1/introspection.RootDiskType` [GH-1730](https://github.com/gophercloud/gophercloud/pull/1730) +* Added `AttachedVolumes` to `compute/v2/servers.Server` [GH-1732](https://github.com/gophercloud/gophercloud/pull/1732) +* Enable unmarshaling server tags to a `compute/v2/servers.Server` struct [GH-1734] +* Allow setting an empty members list in `loadbalancer/v2/pools.BatchUpdateMembers` [GH-1736](https://github.com/gophercloud/gophercloud/pull/1736) +* Allow unsetting members' subnet ID and name in `loadbalancer/v2/pools.BatchUpdateMemberOpts` [GH-1738](https://github.com/gophercloud/gophercloud/pull/1738) + +BUG FIXES + +* Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705](https://github.com/gophercloud/gophercloud/pull/1705) +* Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706](https://github.com/gophercloud/gophercloud/pull/1706) +* Fixed issue with `blockstorage/v1/volumes.Create` where the response was expected to be 202 [GH-1720](https://github.com/gophercloud/gophercloud/pull/1720) +* Changed `DefaultTlsContainerRef` from `string` to `*string` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) +* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) +* Changed `DefaultTlsContainerRef` from `string` to `*string` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) +* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. [GH-1723](https://github.com/gophercloud/gophercloud/pull/1723) + + +## 0.4.0 (September 3, 2019) + +IMPROVEMENTS + +* Added `blockstorage/extensions/quotasets.results.QuotaSet.Groups` [GH-1668](https://github.com/gophercloud/gophercloud/pull/1668) +* Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668](https://github.com/gophercloud/gophercloud/pull/1668) +* Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674](https://github.com/gophercloud/gophercloud/pull/1674) +* Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676](https://github.com/gophercloud/gophercloud/pull/1676) +* Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677](https://github.com/gophercloud/gophercloud/pull/1677) +* Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681](https://github.com/gophercloud/gophercloud/pull/1681) +* Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651](https://github.com/gophercloud/gophercloud/pull/1651) +* Added `networking/v2/extensions/agents.ListDHCPNetworks` [GH-1686](https://github.com/gophercloud/gophercloud/pull/1686) +* Added `networking/v2/extensions/layer3/portforwarding.Delete` [GH-1652](https://github.com/gophercloud/gophercloud/pull/1652) +* Added `compute/v2/extensions/tags.List` [GH-1679](https://github.com/gophercloud/gophercloud/pull/1679) +* Added `compute/v2/extensions/tags.Check` [GH-1679](https://github.com/gophercloud/gophercloud/pull/1679) + +BUG FIXES + +* Changed `identity/v3/endpoints.ListOpts.RegionID` from `int` to `string` [GH-1664](https://github.com/gophercloud/gophercloud/pull/1664) +* Fixed issue where older time formats in some networking APIs/resources were unable to be parsed [GH-1671](https://github.com/gophercloud/gophercloud/pull/1664) +* Changed `SATA`, `SCSI`, and `SAS` types to `InterfaceType` in `baremetal/v1/nodes` [GH-1683] + +## 0.3.0 (July 31, 2019) + +IMPROVEMENTS + +* Added `baremetal/apiversions.List` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) +* Added `baremetal/apiversions.Get` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) +* Added `compute/v2/extensions/servergroups.CreateOpts.Policy` [GH-1636](https://github.com/gophercloud/gophercloud/pull/1636) +* Added `identity/v3/extensions/trusts.Create` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644) +* Added `identity/v3/extensions/trusts.Delete` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644) +* Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/layer3/floatingips.FloatingIP` [GH-1647](https://github.com/gophercloud/gophercloud/issues/1646) +* Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/security/groups.SecGroup` [GH-1654](https://github.com/gophercloud/gophercloud/issues/1654) +* Added `CreatedAt` and `UpdatedAt` to `networking/v2/networks.Network` [GH-1657](https://github.com/gophercloud/gophercloud/issues/1657) +* Added `keymanager/v1/containers.CreateSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659) +* Added `keymanager/v1/containers.DeleteSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659) +* Added `sharedfilesystems/v2/shares.GetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) +* Added `sharedfilesystems/v2/shares.GetMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) +* Added `sharedfilesystems/v2/shares.SetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) +* Added `sharedfilesystems/v2/shares.UpdateMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) +* Added `sharedfilesystems/v2/shares.DeleteMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) +* Added `sharedfilesystems/v2/sharetypes.IDFromName` [GH-1662](https://github.com/gophercloud/gophercloud/issues/1662) + + + +BUG FIXES + +* Changed `baremetal/v1/nodes.CleanStep.Args` from `map[string]string` to `map[string]interface{}` [GH-1638](https://github.com/gophercloud/gophercloud/pull/1638) +* Removed `URLPath` and `ExpectedCodes` from `loadbalancer/v2/monitors.ToMonitorCreateMap` since Octavia now provides default values when these fields are not specified [GH-1640](https://github.com/gophercloud/gophercloud/pull/1540) + + +## 0.2.0 (June 17, 2019) + +IMPROVEMENTS + +* Added `networking/v2/extensions/qos/rules.ListBandwidthLimitRules` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) +* Added `networking/v2/extensions/qos/rules.GetBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) +* Added `networking/v2/extensions/qos/rules.CreateBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) +* Added `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` [GH-1589](https://github.com/gophercloud/gophercloud/pull/1589) +* Added `networking/v2/extensions/qos/rules.DeleteBandwidthLimitRule` [GH-1590](https://github.com/gophercloud/gophercloud/pull/1590) +* Added `networking/v2/extensions/qos/policies.List` [GH-1591](https://github.com/gophercloud/gophercloud/pull/1591) +* Added `networking/v2/extensions/qos/policies.Get` [GH-1593](https://github.com/gophercloud/gophercloud/pull/1593) +* Added `networking/v2/extensions/qos/rules.ListDSCPMarkingRules` [GH-1594](https://github.com/gophercloud/gophercloud/pull/1594) +* Added `networking/v2/extensions/qos/policies.Create` [GH-1595](https://github.com/gophercloud/gophercloud/pull/1595) +* Added `compute/v2/extensions/diagnostics.Get` [GH-1592](https://github.com/gophercloud/gophercloud/pull/1592) +* Added `networking/v2/extensions/qos/policies.Update` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603) +* Added `networking/v2/extensions/qos/policies.Delete` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603) +* Added `networking/v2/extensions/qos/rules.CreateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605) +* Added `networking/v2/extensions/qos/rules.UpdateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605) +* Added `networking/v2/extensions/qos/rules.GetDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609) +* Added `networking/v2/extensions/qos/rules.DeleteDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609) +* Added `networking/v2/extensions/qos/rules.ListMinimumBandwidthRules` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) +* Added `networking/v2/extensions/qos/rules.GetMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) +* Added `networking/v2/extensions/qos/rules.CreateMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) +* Added `Hostname` to `baremetalintrospection/v1/introspection.Data` [GH-1627](https://github.com/gophercloud/gophercloud/pull/1627) +* Added `networking/v2/extensions/qos/rules.UpdateMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624) +* Added `networking/v2/extensions/qos/rules.DeleteMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624) +* Added `networking/v2/extensions/qos/ruletypes.GetRuleType` [GH-1625](https://github.com/gophercloud/gophercloud/pull/1625) +* Added `Extra` to `baremetalintrospection/v1/introspection.Data` [GH-1611](https://github.com/gophercloud/gophercloud/pull/1611) +* Added `blockstorage/extensions/volumeactions.SetImageMetadata` [GH-1621](https://github.com/gophercloud/gophercloud/pull/1621) + +BUG FIXES + +* Updated `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` to use return code 200 [GH-1606](https://github.com/gophercloud/gophercloud/pull/1606) +* Fixed bug in `compute/v2/extensions/schedulerhints.SchedulerHints.Query` where contents will now be marshalled to a string [GH-1620](https://github.com/gophercloud/gophercloud/pull/1620) + +## 0.1.0 (May 27, 2019) + +Initial tagged release. diff --git a/vendor/github.com/gophercloud/gophercloud/v2/LICENSE b/vendor/github.com/gophercloud/gophercloud/v2/LICENSE new file mode 100644 index 000000000..c3f4f2f7c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/LICENSE @@ -0,0 +1,192 @@ +Copyright 2012-2013 Rackspace, Inc. +Copyright Gophercloud authors + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/github.com/gophercloud/gophercloud/v2/Makefile b/vendor/github.com/gophercloud/gophercloud/v2/Makefile new file mode 100644 index 000000000..128beec00 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/Makefile @@ -0,0 +1,109 @@ +undefine GOFLAGS + +GOLANGCI_LINT_VERSION?=v1.57.1 + +ifeq ($(shell command -v podman 2> /dev/null),) + RUNNER=docker +else + RUNNER=podman +endif + +# if the golangci-lint steps fails with the following error message: +# +# directory prefix . does not contain main module or its selected dependencies +# +# 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: + $(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 +.PHONY: lint + +unit: + go test ./... +.PHONY: unit + +coverage: + go test -covermode count -coverprofile cover.out -coverpkg=./... ./... +.PHONY: coverage + +acceptance: acceptance-baremetal acceptance-blockstorage acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-imageservice acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow +.PHONY: acceptance + +acceptance-baremetal: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/baremetal/... +.PHONY: acceptance-baremetal + +acceptance-blockstorage: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/blockstorage/... +.PHONY: acceptance-blockstorage + +acceptance-compute: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/compute/... +.PHONY: acceptance-compute + +acceptance-container: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/container/... +.PHONY: acceptance-container + +acceptance-containerinfra: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/containerinfra/... +.PHONY: acceptance-containerinfra + +acceptance-db: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/db/... +.PHONY: acceptance-db + +acceptance-dns: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/dns/... +.PHONY: acceptance-dns + +acceptance-identity: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/identity/... +.PHONY: acceptance-identity + +acceptance-image: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/imageservice/... +.PHONY: acceptance-image + +acceptance-keymanager: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/keymanager/... +.PHONY: acceptance-keymanager + +acceptance-loadbalancer: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/loadbalancer/... +.PHONY: acceptance-loadbalancer + +acceptance-messaging: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/messaging/... +.PHONY: acceptance-messaging + +acceptance-networking: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/... +.PHONY: acceptance-networking + +acceptance-objectstorage: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/objectstorage/... +.PHONY: acceptance-objectstorage + +acceptance-orchestration: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/orchestration/... +.PHONY: acceptance-orchestration + +acceptance-placement: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/placement/... +.PHONY: acceptance-placement + +acceptance-sharedfilesystems: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/sharedfilesystems/... +.PHONY: acceptance-sharefilesystems + +acceptance-workflow: + go test -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/v2/RELEASE.md b/vendor/github.com/gophercloud/gophercloud/v2/RELEASE.md new file mode 100644 index 000000000..6490ed887 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/RELEASE.md @@ -0,0 +1,79 @@ +# Gophercloud release + +## Contributions + +### The semver label + +Gophercloud follows [semver](https://semver.org/). + +Each Pull request must have a label indicating its impact on the API: +* `semver:patch` for changes that don't impact the API +* `semver:minor` for changes that impact the API in a backwards-compatible fashion +* `semver:major` for changes that introduce a breaking change in the API + +Automation prevents merges if the label is not present. + +### Metadata + +The release notes for a given release are generated based on the PR title: make +sure that the PR title is descriptive. + +## Release of a new version + +Requirements: +* [`gh`](https://github.com/cli/cli) +* [`jq`](https://stedolan.github.io/jq/) + +### Step 1: Collect all PRs since the last release + +Supposing that the base release is `v1.2.0`: + +``` +for commit_sha in $(git log --pretty=format:"%h" v1.2.0..HEAD); do + gh pr list --search "$commit_sha" --state merged --json number,title,labels,url +done | jq '.[]' | jq --slurp 'unique_by(.number)' > prs.json +``` + +This JSON file will be useful later. + +### Step 2: Determine the version + +In order to determine the version of the next release, we first check that no incompatible change is detected in the code that has been merged since the last release. This step can be automated with the `gorelease` tool: + +```shell +gorelease | grep -B2 -A0 '^## incompatible changes' +``` + +If the tool detects incompatible changes outside a `testing` package, then the bump is major. + +Next, we check all PRs merged since the last release using the file `prs.json` that we generated above. + +* Find PRs labeled with `semver:major`: `jq 'map(select(contains({labels: [{name: "semver:major"}]}) ))' prs.json` +* Find PRs labeled with `semver:minor`: `jq 'map(select(contains({labels: [{name: "semver:minor"}]}) ))' prs.json` + +The highest semver descriptor determines the release bump. + +### Step 3: Release notes and version string + +Once all PRs have a sensible title, generate the release notes: + +```shell +jq -r '.[] | "* [GH-\(.number)](\(.url)) \(.title)"' prs.json +``` + +Add that to the top of `CHANGELOG.md`. Also add any information that could be useful to consumers willing to upgrade. + +**Set the new version string in the `DefaultUserAgent` constant in `provider_client.go`.** + +Create a PR with these two changes. The new PR should be labeled with the semver label corresponding to the type of bump. + +### Step 3: Git tag and Github release + +The Go mod system relies on Git tags. In order to simulate a review mechanism, we rely on Github to create the tag through the Release mechanism. + +* [Prepare a new release](https://github.com/gophercloud/gophercloud/releases/new) +* Let Github generate the release notes by clicking on Generate release notes +* Click on **Save draft** +* Ask another Gophercloud maintainer to review and publish the release + +_Note: never change a release or force-push a tag. Tags are almost immediately picked up by the Go proxy and changing the commit it points to will be detected as tampering._ diff --git a/vendor/github.com/gophercloud/gophercloud/v2/auth_options.go b/vendor/github.com/gophercloud/gophercloud/v2/auth_options.go new file mode 100644 index 000000000..616919d00 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/auth_options.go @@ -0,0 +1,517 @@ +package gophercloud + +/* +AuthOptions stores information needed to authenticate to an OpenStack Cloud. +You can populate one manually, or use a provider's AuthOptionsFromEnv() function +to read relevant information from the standard environment variables. Pass one +to a provider's AuthenticatedClient function to authenticate and obtain a +ProviderClient representing an active session on that provider. + +Its fields are the union of those recognized by each identity implementation and +provider. + +An example of manually providing authentication information: + + opts := gophercloud.AuthOptions{ + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", + Username: "{username}", + Password: "{password}", + TenantID: "{tenant_id}", + } + + provider, err := openstack.AuthenticatedClient(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(context.TODO(), opts) +*/ +type AuthOptions struct { + // IdentityEndpoint specifies the HTTP endpoint that is required to work with + // the Identity API of the appropriate version. While it's ultimately needed by + // all of the identity services, it will often be populated by a provider-level + // function. + // + // The IdentityEndpoint is typically referred to as the "auth_url" or + // "OS_AUTH_URL" in the information provided by the cloud operator. + IdentityEndpoint string `json:"-"` + + // Username is required if using Identity V2 API. Consult with your provider's + // control panel to discover your account's username. In Identity V3, either + // UserID or a combination of Username and DomainID or DomainName are needed. + Username string `json:"username,omitempty"` + UserID string `json:"-"` + + Password string `json:"password,omitempty"` + + // Passcode is used in TOTP authentication method + Passcode string `json:"passcode,omitempty"` + + // At most one of DomainID and DomainName must be provided if using Username + // with Identity V3. Otherwise, either are optional. + DomainID string `json:"-"` + DomainName string `json:"name,omitempty"` + + // The TenantID and TenantName fields are optional for the Identity V2 API. + // The same fields are known as project_id and project_name in the Identity + // V3 API, but are collected as TenantID and TenantName here in both cases. + // Some providers allow you to specify a TenantName instead of the TenantId. + // Some require both. Your provider's authentication policies will determine + // how these fields influence authentication. + // If DomainID or DomainName are provided, they will also apply to TenantName. + // It is not currently possible to authenticate with Username and a Domain + // and scope to a Project in a different Domain by using TenantName. To + // accomplish that, the ProjectID will need to be provided as the TenantID + // option. + TenantID string `json:"tenantId,omitempty"` + TenantName string `json:"tenantName,omitempty"` + + // AllowReauth should be set to true if you grant permission for Gophercloud to + // cache your credentials in memory, and to allow Gophercloud to attempt to + // re-authenticate automatically if/when your token expires. If you set it to + // false, it will not cache these settings, but re-authentication will not be + // possible. This setting defaults to false. + // + // NOTE: The reauth function will try to re-authenticate endlessly if left + // unchecked. The way to limit the number of attempts is to provide a custom + // HTTP client to the provider client and provide a transport that implements + // the RoundTripper interface and stores the number of failed retries. For an + // example of this, see here: + // https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 + AllowReauth bool `json:"-"` + + // TokenID allows users to authenticate (possibly as another user) with an + // authentication token ID. + TokenID string `json:"-"` + + // Scope determines the scoping of the authentication request. + Scope *AuthScope `json:"-"` + + // Authentication through Application Credentials requires supplying name, project and secret + // For project we can use TenantID + ApplicationCredentialID string `json:"-"` + ApplicationCredentialName string `json:"-"` + ApplicationCredentialSecret string `json:"-"` +} + +// AuthScope allows a created token to be limited to a specific domain or project. +type AuthScope struct { + ProjectID string + ProjectName string + 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]any, error) { + // Populate the request map. + authMap := make(map[string]any) + + if opts.Username != "" { + if opts.Password != "" { + authMap["passwordCredentials"] = map[string]any{ + "username": opts.Username, + "password": opts.Password, + } + } else { + return nil, ErrMissingInput{Argument: "Password"} + } + } else if opts.TokenID != "" { + authMap["token"] = map[string]any{ + "id": opts.TokenID, + } + } else { + return nil, ErrMissingInput{Argument: "Username"} + } + + if opts.TenantID != "" { + authMap["tenantId"] = opts.TenantID + } + if opts.TenantName != "" { + authMap["tenantName"] = opts.TenantName + } + + 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]any) (map[string]any, error) { + type domainReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + } + + type userReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Password *string `json:"password,omitempty"` + Passcode *string `json:"passcode,omitempty"` + Domain *domainReq `json:"domain,omitempty"` + } + + type passwordReq struct { + User userReq `json:"user"` + } + + type tokenReq struct { + ID string `json:"id"` + } + + type applicationCredentialReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + User *userReq `json:"user,omitempty"` + Secret *string `json:"secret,omitempty"` + } + + type totpReq struct { + User *userReq `json:"user,omitempty"` + } + + type identityReq struct { + Methods []string `json:"methods"` + Password *passwordReq `json:"password,omitempty"` + Token *tokenReq `json:"token,omitempty"` + ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` + TOTP *totpReq `json:"totp,omitempty"` + } + + type authReq struct { + Identity identityReq `json:"identity"` + } + + type request struct { + Auth authReq `json:"auth"` + } + + // Populate the request structure based on the provided arguments. Create and return an error + // if insufficient or incompatible information is present. + var req request + + if opts.Password == "" && opts.Passcode == "" { + if opts.TokenID != "" { + // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication + // parameters. + if opts.Username != "" { + return nil, ErrUsernameWithToken{} + } + if opts.UserID != "" { + return nil, ErrUserIDWithToken{} + } + if opts.DomainID != "" { + return nil, ErrDomainIDWithToken{} + } + if opts.DomainName != "" { + return nil, ErrDomainNameWithToken{} + } + + // Configure the request for Token authentication. + req.Auth.Identity.Methods = []string{"token"} + req.Auth.Identity.Token = &tokenReq{ + ID: opts.TokenID, + } + + } else if opts.ApplicationCredentialID != "" { + // Configure the request for ApplicationCredentialID authentication. + // https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67 + // There are three kinds of possible application_credential requests + // 1. application_credential id + secret + // 2. application_credential name + secret + user_id + // 3. application_credential name + secret + username + domain_id / domain_name + if opts.ApplicationCredentialSecret == "" { + return nil, ErrAppCredMissingSecret{} + } + req.Auth.Identity.Methods = []string{"application_credential"} + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + ID: &opts.ApplicationCredentialID, + Secret: &opts.ApplicationCredentialSecret, + } + } else if opts.ApplicationCredentialName != "" { + if opts.ApplicationCredentialSecret == "" { + return nil, ErrAppCredMissingSecret{} + } + + var userRequest *userReq + + if opts.UserID != "" { + // UserID could be used without the domain information + userRequest = &userReq{ + ID: &opts.UserID, + } + } + + if userRequest == nil && opts.Username == "" { + // Make sure that Username or UserID are provided + return nil, ErrUsernameOrUserID{} + } + + if userRequest == nil && opts.DomainID != "" { + userRequest = &userReq{ + Name: &opts.Username, + Domain: &domainReq{ID: &opts.DomainID}, + } + } + + if userRequest == nil && opts.DomainName != "" { + userRequest = &userReq{ + Name: &opts.Username, + Domain: &domainReq{Name: &opts.DomainName}, + } + } + + // Make sure that DomainID or DomainName are provided among Username + if userRequest == nil { + return nil, ErrDomainIDOrDomainName{} + } + + req.Auth.Identity.Methods = []string{"application_credential"} + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + Name: &opts.ApplicationCredentialName, + User: userRequest, + Secret: &opts.ApplicationCredentialSecret, + } + } else { + // If no password or token ID or ApplicationCredential are available, authentication can't continue. + return nil, ErrMissingPassword{} + } + } else { + // Password authentication. + if opts.Password != "" { + req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "password") + } + + // TOTP authentication. + if opts.Passcode != "" { + req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "totp") + } + + // At least one of Username and UserID must be specified. + if opts.Username == "" && opts.UserID == "" { + return nil, ErrUsernameOrUserID{} + } + + if opts.Username != "" { + // If Username is provided, UserID may not be provided. + if opts.UserID != "" { + return nil, ErrUsernameOrUserID{} + } + + // Either DomainID or DomainName must also be specified. + if opts.DomainID == "" && opts.DomainName == "" { + return nil, ErrDomainIDOrDomainName{} + } + + if opts.DomainID != "" { + if opts.DomainName != "" { + return nil, ErrDomainIDOrDomainName{} + } + + // Configure the request for Username and Password authentication with a DomainID. + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: &opts.Password, + Domain: &domainReq{ID: &opts.DomainID}, + }, + } + } + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + Name: &opts.Username, + Passcode: &opts.Passcode, + Domain: &domainReq{ID: &opts.DomainID}, + }, + } + } + } + + if opts.DomainName != "" { + // Configure the request for Username and Password authentication with a DomainName. + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: &opts.Password, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } + } + + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + Name: &opts.Username, + Passcode: &opts.Passcode, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } + } + } + } + + if opts.UserID != "" { + // If UserID is specified, neither DomainID nor DomainName may be. + if opts.DomainID != "" { + return nil, ErrDomainIDWithUserID{} + } + if opts.DomainName != "" { + return nil, ErrDomainNameWithUserID{} + } + + // Configure the request for UserID and Password authentication. + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + ID: &opts.UserID, + Password: &opts.Password, + }, + } + } + + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + ID: &opts.UserID, + Passcode: &opts.Passcode, + }, + } + } + } + } + + b, err := BuildRequestBody(req, "") + if err != nil { + return nil, err + } + + if len(scope) != 0 { + b["auth"].(map[string]any)["scope"] = scope + } + + return b, nil +} + +// ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in +// the v3 tokens package. +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. + if opts.Scope == nil { + opts.Scope = new(AuthScope) + if opts.TenantID != "" { + opts.Scope.ProjectID = opts.TenantID + } else { + if opts.TenantName != "" { + opts.Scope.ProjectName = opts.TenantName + opts.Scope.DomainID = opts.DomainID + opts.Scope.DomainName = opts.DomainName + } + } + } + + if opts.Scope.System { + 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. + if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { + return nil, ErrScopeDomainIDOrDomainName{} + } + if opts.Scope.ProjectID != "" { + return nil, ErrScopeProjectIDOrProjectName{} + } + + if opts.Scope.DomainID != "" { + // ProjectName + DomainID + return map[string]any{ + "project": map[string]any{ + "name": &opts.Scope.ProjectName, + "domain": map[string]any{"id": &opts.Scope.DomainID}, + }, + }, nil + } + + if opts.Scope.DomainName != "" { + // ProjectName + DomainName + return map[string]any{ + "project": map[string]any{ + "name": &opts.Scope.ProjectName, + "domain": map[string]any{"name": &opts.Scope.DomainName}, + }, + }, nil + } + } else if opts.Scope.ProjectID != "" { + // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. + if opts.Scope.DomainID != "" { + return nil, ErrScopeProjectIDAlone{} + } + if opts.Scope.DomainName != "" { + return nil, ErrScopeProjectIDAlone{} + } + + // ProjectID + return map[string]any{ + "project": map[string]any{ + "id": &opts.Scope.ProjectID, + }, + }, nil + } else if opts.Scope.DomainID != "" { + // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. + if opts.Scope.DomainName != "" { + return nil, ErrScopeDomainIDOrDomainName{} + } + + // DomainID + return map[string]any{ + "domain": map[string]any{ + "id": &opts.Scope.DomainID, + }, + }, nil + } else if opts.Scope.DomainName != "" { + // DomainName + return map[string]any{ + "domain": map[string]any{ + "name": &opts.Scope.DomainName, + }, + }, nil + } + + return nil, nil +} + +func (opts AuthOptions) CanReauth() bool { + if opts.Passcode != "" { + // cannot reauth using TOTP passcode + return false + } + + return opts.AllowReauth +} + +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { + return nil, nil +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/auth_result.go b/vendor/github.com/gophercloud/gophercloud/v2/auth_result.go new file mode 100644 index 000000000..9a49cce8e --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/auth_result.go @@ -0,0 +1,52 @@ +package gophercloud + +/* +AuthResult is the result from the request that was used to obtain a provider +client's Keystone token. It is returned from ProviderClient.GetAuthResult(). + +The following types satisfy this interface: + + github.com/gophercloud/gophercloud/openstack/identity/v2/tokens.CreateResult + github.com/gophercloud/gophercloud/openstack/identity/v3/tokens.CreateResult + +Usage example: + + import ( + "github.com/gophercloud/gophercloud/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) { + r := providerClient.GetAuthResult() + if r == nil { + //ProviderClient did not use openstack.Authenticate(), e.g. because token + //was set manually with ProviderClient.SetToken() + return "", errors.New("no AuthResult available") + } + switch r := r.(type) { + case tokens2.CreateResult: + u, err := r.ExtractUser() + if err != nil { + return "", err + } + return u.ID, nil + case tokens3.CreateResult: + u, err := r.ExtractUser() + if err != nil { + return "", err + } + return u.ID, nil + default: + panic(fmt.Sprintf("got unexpected AuthResult type %t", r)) + } + } + +Both implementing types share a lot of methods by name, like ExtractUser() in +this example. But those methods cannot be part of the AuthResult interface +because the return types are different (in this case, type tokens2.User vs. +type tokens3.User). +*/ +type AuthResult interface { + ExtractTokenID() (string, error) +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go b/vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go new file mode 100644 index 000000000..2fbc3c97f --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/endpoint_search.go @@ -0,0 +1,76 @@ +package gophercloud + +// 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 +// lists them as different kinds of URLs within the service catalog ("adminURL", +// "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an +// endpoint's response. +type Availability string + +const ( + // AvailabilityAdmin indicates that an endpoint is only available to + // administrators. + AvailabilityAdmin Availability = "admin" + + // AvailabilityPublic indicates that an endpoint is available to everyone on + // the internet. + AvailabilityPublic Availability = "public" + + // AvailabilityInternal indicates that an endpoint is only available within + // the cluster's internal network. + AvailabilityInternal Availability = "internal" +) + +// 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. +// +// Usually, these are passed to service client factory functions in a provider +// 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. + Type string + + // Name [optional] is the service name for the client (e.g., "nova") as it + // appears in the service catalog. Services can have the same Type but a + // different Name, which is why both Type and Name are sometimes needed. + Name 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 + + // Availability [optional] is the visibility of the endpoint to be returned. + // Valid types include the constants AvailabilityPublic, AvailabilityInternal, + // or AvailabilityAdmin from this package. + // + // Availability is not required, and defaults to AvailabilityPublic. Not all + // providers or services offer all Availability options. + Availability Availability +} + +/* +EndpointLocator is an internal function to be used by provider implementations. + +It provides an implementation that locates a single endpoint from a service +catalog for a specific ProviderClient based on user-provided EndpointOpts. The +provider then uses it to discover related ServiceClients. +*/ +type EndpointLocator func(EndpointOpts) (string, error) + +// ApplyDefaults is an internal method to be used by provider implementations. +// +// It sets EndpointOpts fields if not already set, including a default type. +// Currently, EndpointOpts.Availability defaults to the public endpoint. +func (eo *EndpointOpts) ApplyDefaults(t string) { + if eo.Type == "" { + eo.Type = t + } + if eo.Availability == "" { + eo.Availability = AvailabilityPublic + } +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/errors.go b/vendor/github.com/gophercloud/gophercloud/v2/errors.go new file mode 100644 index 000000000..2698c2039 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/errors.go @@ -0,0 +1,351 @@ +package gophercloud + +import ( + "bytes" + "errors" + "fmt" + "net/http" + "strings" +) + +// BaseError is an error type that all other error types embed. +type BaseError struct { + DefaultErrString string + Info string +} + +func (e BaseError) Error() string { + e.DefaultErrString = "An error occurred while executing a Gophercloud request." + return e.choseErrString() +} + +func (e BaseError) choseErrString() string { + if e.Info != "" { + return e.Info + } + return e.DefaultErrString +} + +// ErrMissingInput is the error when input is required in a particular +// situation but not provided by the user +type ErrMissingInput struct { + BaseError + Argument string +} + +func (e ErrMissingInput) Error() string { + e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument) + return e.choseErrString() +} + +// ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. +type ErrInvalidInput struct { + ErrMissingInput + Value any +} + +func (e ErrInvalidInput) Error() string { + e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value) + return e.choseErrString() +} + +// ErrMissingEnvironmentVariable is the error when environment variable is required +// in a particular situation but not provided by the user +type ErrMissingEnvironmentVariable struct { + BaseError + EnvironmentVariable string +} + +func (e ErrMissingEnvironmentVariable) Error() string { + e.DefaultErrString = fmt.Sprintf("Missing environment variable [%s]", e.EnvironmentVariable) + return e.choseErrString() +} + +// ErrMissingAnyoneOfEnvironmentVariables is the error when anyone of the environment variables +// is required in a particular situation but not provided by the user +type ErrMissingAnyoneOfEnvironmentVariables struct { + BaseError + EnvironmentVariables []string +} + +func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { + e.DefaultErrString = fmt.Sprintf( + "Missing one of the following environment variables [%s]", + strings.Join(e.EnvironmentVariables, ", "), + ) + return e.choseErrString() +} + +// ErrUnexpectedResponseCode is returned by the Request method when a response code other than +// those listed in OkCodes is encountered. +type ErrUnexpectedResponseCode struct { + BaseError + URL string + Method string + Expected []int + Actual int + Body []byte + ResponseHeader http.Header +} + +func (e ErrUnexpectedResponseCode) Error() string { + e.DefaultErrString = fmt.Sprintf( + "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() +} + +// GetStatusCode returns the actual status code of the error. +func (e ErrUnexpectedResponseCode) GetStatusCode() int { + return e.Actual +} + +// 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: +// +// 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. +type ErrTimeOut struct { + BaseError +} + +func (e ErrTimeOut) Error() string { + e.DefaultErrString = "A time out occurred" + return e.choseErrString() +} + +// ErrUnableToReauthenticate is the error type returned when reauthentication fails. +type ErrUnableToReauthenticate struct { + BaseError + ErrOriginal error + ErrReauth error +} + +func (e ErrUnableToReauthenticate) Error() string { + e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s: %s", e.ErrOriginal, e.ErrReauth) + return e.choseErrString() +} + +// ErrErrorAfterReauthentication is the error type returned when reauthentication +// succeeds, but an error occurs afterword (usually an HTTP error). +type ErrErrorAfterReauthentication struct { + BaseError + ErrOriginal error +} + +func (e ErrErrorAfterReauthentication) Error() string { + e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal) + return e.choseErrString() +} + +// ErrServiceNotFound is returned when no service in a service catalog matches +// the provided EndpointOpts. This is generally returned by provider service +// factory methods like "NewComputeV2()" and can mean that a service is not +// enabled for your account. +type ErrServiceNotFound struct { + BaseError +} + +func (e ErrServiceNotFound) Error() string { + e.DefaultErrString = "No suitable service could be found in the service catalog." + return e.choseErrString() +} + +// ErrEndpointNotFound is returned when no available endpoints match the +// provided EndpointOpts. This is also generally returned by provider service +// factory methods, and usually indicates that a region was specified +// incorrectly. +type ErrEndpointNotFound struct { + BaseError +} + +func (e ErrEndpointNotFound) Error() string { + e.DefaultErrString = "No suitable endpoint could be found in the service catalog." + return e.choseErrString() +} + +// ErrResourceNotFound is the error when trying to retrieve a resource's +// ID by name and the resource doesn't exist. +type ErrResourceNotFound struct { + BaseError + Name string + ResourceType string +} + +func (e ErrResourceNotFound) Error() string { + e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name) + return e.choseErrString() +} + +// ErrMultipleResourcesFound is the error when trying to retrieve a resource's +// ID by name and multiple resources have the user-provided name. +type ErrMultipleResourcesFound struct { + BaseError + Name string + Count int + ResourceType string +} + +func (e ErrMultipleResourcesFound) Error() string { + e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name) + return e.choseErrString() +} + +// ErrUnexpectedType is the error when an unexpected type is encountered +type ErrUnexpectedType struct { + BaseError + Expected string + Actual string +} + +func (e ErrUnexpectedType) Error() string { + e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual) + return e.choseErrString() +} + +func unacceptedAttributeErr(attribute string) string { + return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute) +} + +func redundantWithTokenErr(attribute string) string { + return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute) +} + +func redundantWithUserID(attribute string) string { + return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute) +} + +// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. +type ErrAPIKeyProvided struct{ BaseError } + +func (e ErrAPIKeyProvided) Error() string { + return unacceptedAttributeErr("APIKey") +} + +// ErrTenantIDProvided indicates that a TenantID was provided but can't be used. +type ErrTenantIDProvided struct{ BaseError } + +func (e ErrTenantIDProvided) Error() string { + return unacceptedAttributeErr("TenantID") +} + +// ErrTenantNameProvided indicates that a TenantName was provided but can't be used. +type ErrTenantNameProvided struct{ BaseError } + +func (e ErrTenantNameProvided) Error() string { + return unacceptedAttributeErr("TenantName") +} + +// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. +type ErrUsernameWithToken struct{ BaseError } + +func (e ErrUsernameWithToken) Error() string { + return redundantWithTokenErr("Username") +} + +// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. +type ErrUserIDWithToken struct{ BaseError } + +func (e ErrUserIDWithToken) Error() string { + return redundantWithTokenErr("UserID") +} + +// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. +type ErrDomainIDWithToken struct{ BaseError } + +func (e ErrDomainIDWithToken) Error() string { + return redundantWithTokenErr("DomainID") +} + +// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s +type ErrDomainNameWithToken struct{ BaseError } + +func (e ErrDomainNameWithToken) Error() string { + return redundantWithTokenErr("DomainName") +} + +// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. +type ErrUsernameOrUserID struct{ BaseError } + +func (e ErrUsernameOrUserID) Error() string { + return "Exactly one of Username and UserID must be provided for password authentication" +} + +// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. +type ErrDomainIDWithUserID struct{ BaseError } + +func (e ErrDomainIDWithUserID) Error() string { + return redundantWithUserID("DomainID") +} + +// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. +type ErrDomainNameWithUserID struct{ BaseError } + +func (e ErrDomainNameWithUserID) Error() string { + return redundantWithUserID("DomainName") +} + +// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. +// It may also indicate that both a DomainID and a DomainName were provided at once. +type ErrDomainIDOrDomainName struct{ BaseError } + +func (e ErrDomainIDOrDomainName) Error() string { + return "You must provide exactly one of DomainID or DomainName to authenticate by Username" +} + +// ErrMissingPassword indicates that no password was provided and no token is available. +type ErrMissingPassword struct{ BaseError } + +func (e ErrMissingPassword) Error() string { + return "You must provide a password to authenticate" +} + +// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. +type ErrScopeDomainIDOrDomainName struct{ BaseError } + +func (e ErrScopeDomainIDOrDomainName) Error() string { + return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName" +} + +// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. +type ErrScopeProjectIDOrProjectName struct{ BaseError } + +func (e ErrScopeProjectIDOrProjectName) Error() string { + return "You must provide at most one of ProjectID or ProjectName in a Scope" +} + +// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. +type ErrScopeProjectIDAlone struct{ BaseError } + +func (e ErrScopeProjectIDAlone) Error() string { + return "ProjectID must be supplied alone in a Scope" +} + +// ErrScopeEmpty indicates that no credentials were provided in a Scope. +type ErrScopeEmpty struct{ BaseError } + +func (e ErrScopeEmpty) Error() string { + return "You must provide either a Project or Domain in a Scope" +} + +// ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name +type ErrAppCredMissingSecret struct{ BaseError } + +func (e ErrAppCredMissingSecret) Error() string { + return "You must provide an Application Credential Secret" +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/BUILD.bazel new file mode 100644 index 000000000..ec55b74a8 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "openstack", + srcs = [ + "auth_env.go", + "client.go", + "doc.go", + "endpoint_location.go", + "errors.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack", + importpath = "github.com/gophercloud/gophercloud/v2/openstack", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/utils", + ], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go new file mode 100644 index 000000000..893787b78 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/auth_env.go @@ -0,0 +1,137 @@ +package openstack + +import ( + "os" + + "github.com/gophercloud/gophercloud/v2" +) + +var nilOptions = gophercloud.AuthOptions{} + +/* +AuthOptionsFromEnv fills out an identity.AuthOptions structure with the +settings found on the various OpenStack OS_* environment variables. + +The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, +OS_PASSWORD and OS_PROJECT_ID. + +Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, +or an error will result. OS_PROJECT_ID, is optional. + +OS_TENANT_ID and OS_TENANT_NAME are deprecated forms of OS_PROJECT_ID and +OS_PROJECT_NAME and the latter are expected against a v3 auth api. + +If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred +as "tenant" in Gophercloud. + +If OS_PROJECT_NAME is set, it requires OS_PROJECT_ID to be set as well to +handle projects not on the default domain. + +To use this function, first set the OS_* environment variables (for example, +by sourcing an `openrc` file), then: + + opts, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) +*/ +func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { + authURL := os.Getenv("OS_AUTH_URL") + username := os.Getenv("OS_USERNAME") + userID := os.Getenv("OS_USERID") + password := os.Getenv("OS_PASSWORD") + passcode := os.Getenv("OS_PASSCODE") + tenantID := os.Getenv("OS_TENANT_ID") + tenantName := os.Getenv("OS_TENANT_NAME") + domainID := os.Getenv("OS_DOMAIN_ID") + domainName := os.Getenv("OS_DOMAIN_NAME") + applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID") + applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME") + applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET") + systemScope := os.Getenv("OS_SYSTEM_SCOPE") + + // If OS_PROJECT_ID is set, overwrite tenantID with the value. + if v := os.Getenv("OS_PROJECT_ID"); v != "" { + tenantID = v + } + + // If OS_PROJECT_NAME is set, overwrite tenantName with the value. + if v := os.Getenv("OS_PROJECT_NAME"); v != "" { + tenantName = v + } + + if authURL == "" { + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_AUTH_URL", + } + return nilOptions, err + } + + if userID == "" && username == "" { + // Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set + if applicationCredentialID == "" && applicationCredentialSecret == "" { + err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, + } + return nilOptions, err + } + } + + if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" { + err := gophercloud.ErrMissingEnvironmentVariable{ + // silently ignore TOTP passcode warning, since it is not a common auth method + EnvironmentVariable: "OS_PASSWORD", + } + return nilOptions, err + } + + if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" { + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET", + } + return nilOptions, err + } + + if domainID == "" && domainName == "" && tenantID == "" && tenantName != "" { + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_PROJECT_ID", + } + return nilOptions, err + } + + if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" { + if userID == "" && username == "" { + return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, + } + } + if username != "" && domainID == "" && domainName == "" { + return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"}, + } + } + } + + var scope *gophercloud.AuthScope + if systemScope == "all" { + scope = &gophercloud.AuthScope{ + System: true, + } + } + + ao := gophercloud.AuthOptions{ + IdentityEndpoint: authURL, + UserID: userID, + Username: username, + Password: password, + Passcode: passcode, + TenantID: tenantID, + TenantName: tenantName, + DomainID: domainID, + DomainName: domainName, + ApplicationCredentialID: applicationCredentialID, + ApplicationCredentialName: applicationCredentialName, + ApplicationCredentialSecret: applicationCredentialSecret, + Scope: scope, + } + + return ao, nil +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/BUILD.bazel similarity index 53% rename from vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/BUILD.bazel rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/BUILD.bazel index 1e75bf9df..f8db99693 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/BUILD.bazel @@ -9,11 +9,11 @@ go_library( "urls.go", "util.go", ], - importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes", - importpath = "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes", + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes", visibility = ["//visibility:public"], deps = [ - "//vendor/github.com/gophercloud/gophercloud", - "//vendor/github.com/gophercloud/gophercloud/pagination", + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/pagination", ], ) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/doc.go new file mode 100644 index 000000000..2ab4af93e --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/doc.go @@ -0,0 +1,161 @@ +/* +Package volumes provides information and interaction with volumes in the +OpenStack Block Storage service. A volume is a detachable block storage +device, akin to a USB hard drive. It can only be attached to one instance at +a time. + +Example of creating Volume B on a Different Host than Volume A + + schedulerHintOpts := volumes.SchedulerHintCreateOpts{ + DifferentHost: []string{ + "volume-a-uuid", + } + } + + createOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10, + } + + volume, err := volumes.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() + if err != nil { + panic(err) + } + +Example of creating Volume B on the Same Host as Volume A + + schedulerHintOpts := volumes.SchedulerHintCreateOpts{ + SameHost: []string{ + "volume-a-uuid", + } + } + + createOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10 + } + + volume, err := volumes.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() + if err != nil { + panic(err) + } + +Example of creating a Volume from a Backup + + backupID := "20c792f0-bb03-434f-b653-06ef238e337e" + options := volumes.CreateOpts{ + Name: "vol-001", + BackupID: &backupID, + } + + client.Microversion = "3.47" + volume, err := volumes.Create(context.TODO(), client, options, nil).Extract() + if err != nil { + panic(err) + } + + fmt.Println(volume) + +Example of Creating an Image from a Volume + + uploadImageOpts := volumes.UploadImageOpts{ + ImageName: "my_vol", + Force: true, + } + + volumeImage, err := volumes.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", volumeImage) + +Example of Extending a Volume's Size + + extendOpts := volumes.ExtendSizeOpts{ + NewSize: 100, + } + + err := volumes.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Initializing a Volume Connection + + connectOpts := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + connectionInfo, err := volumes.InitializeConnection(context.TODO(), client, volume.ID, connectOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", connectionInfo["data"]) + + terminateOpts := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + err = volumes.TerminateConnection(context.TODO(), client, volume.ID, terminateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Setting a Volume's Bootable status + + options := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client, volume.ID, options).ExtractErr() + if err != nil { + panic(err) + } + +Example of Changing Type of a Volume + + changeTypeOpts := volumes.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: volumes.MigrationPolicyOnDemand, + } + + err = volumes.ChangeType(context.TODO(), client, volumeID, changeTypeOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Attaching a Volume to an Instance + + attachOpts := volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + err := volumes.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr() + if err != nil { + panic(err) + } + + detachOpts := volumes.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + err = volumes.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr() + if err != nil { + panic(err) + } +*/ +package volumes diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/requests.go new file mode 100644 index 000000000..77210943b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/requests.go @@ -0,0 +1,765 @@ +package volumes + +import ( + "context" + "maps" + "regexp" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintOptsBuilder interface { + ToSchedulerHintsMap() (map[string]any, error) +} + +// SchedulerHintOpts contains options for providing scheduler hints +// when creating a Volume. This object is passed to the volumes.Create function. +// For more information about these parameters, see the Volume object. +type SchedulerHintOpts struct { + // DifferentHost will place the volume on a different back-end that does not + // host the given volumes. + DifferentHost []string + + // SameHost will place the volume on a back-end that hosts the given volumes. + SameHost []string + + // LocalToInstance will place volume on same host on a given instance + LocalToInstance string + + // Query is a conditional statement that results in back-ends able to + // host the volume. + Query 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 len(opts.DifferentHost) > 0 { + for _, diffHost := range opts.DifferentHost { + if !uuidRegex.MatchString(diffHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "volumes.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 = "volumes.SchedulerHintOpts.SameHost" + err.Value = opts.SameHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["same_host"] = opts.SameHost + } + + if opts.LocalToInstance != "" { + if !uuidRegex.MatchString(opts.LocalToInstance) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "volumes.SchedulerHintOpts.LocalToInstance" + err.Value = opts.LocalToInstance + err.Info = "The instance must be in UUID format." + return nil, err + } + sh["local_to_instance"] = opts.LocalToInstance + } + + if opts.Query != "" { + sh["query"] = opts.Query + } + + 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-SCH-HNT:scheduler_hints": sh}, nil +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToVolumeCreateMap() (map[string]any, error) +} + +// CreateOpts contains options for creating a Volume. This object is passed to +// the volumes.Create function. For more information about these parameters, +// see the Volume object. +type CreateOpts struct { + // The size of the volume, in GB + Size int `json:"size,omitempty"` + // The availability zone + AvailabilityZone string `json:"availability_zone,omitempty"` + // ConsistencyGroupID is the ID of a consistency group + ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` + // The volume description + Description string `json:"description,omitempty"` + // One or more metadata key and value pairs to associate with the volume + Metadata map[string]string `json:"metadata,omitempty"` + // The volume name + Name string `json:"name,omitempty"` + // the ID of the existing volume snapshot + SnapshotID string `json:"snapshot_id,omitempty"` + // SourceReplica is a UUID of an existing volume to replicate with + SourceReplica string `json:"source_replica,omitempty"` + // the ID of the existing volume + SourceVolID string `json:"source_volid,omitempty"` + // The ID of the image from which you want to create the volume. + // Required to create a bootable volume. + ImageID string `json:"imageRef,omitempty"` + // Specifies the backup ID, from which you want to create the volume. + // Create a volume from a backup is supported since 3.47 microversion + BackupID string `json:"backup_id,omitempty"` + // The associated volume type + VolumeType string `json:"volume_type,omitempty"` +} + +// ToVolumeCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToVolumeCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "volume") +} + +// Create will create a new Volume based on the values in CreateOpts. To extract +// the Volume object from the response, call the Extract method on the +// CreateResult. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) { + b, err := opts.ToVolumeCreateMap() + 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{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToVolumeDeleteQuery() (string, error) +} + +// DeleteOpts contains options for deleting a Volume. This object is passed to +// the volumes.Delete function. +type DeleteOpts struct { + // Delete all snapshots of this volume as well. + Cascade bool `q:"cascade"` +} + +// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Delete will delete the existing Volume with the provided ID. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, id) + if opts != nil { + query, err := opts.ToVolumeDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Delete(ctx, url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves the Volume with the provided ID. To extract the Volume object +// from the response, call the Extract method on the GetResult. +func Get(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 +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToVolumeListQuery() (string, error) +} + +// ListOpts holds options for listing Volumes. It is passed to the volumes.List +// function. +type ListOpts struct { + // AllTenants will retrieve volumes of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Metadata will filter results based on specified metadata. + Metadata map[string]string `q:"metadata"` + + // Name will filter by the specified volume name. + Name string `q:"name"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required for this. + TenantID string `q:"project_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` + + // Bootable will filter results based on whether they are bootable volumes + Bootable *bool `q:"bootable,omitempty"` +} + +// ToVolumeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volumes optionally limited by the conditions provided in ListOpts. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToVolumeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToVolumeUpdateMap() (map[string]any, error) +} + +// UpdateOpts contain options for updating an existing Volume. This object is passed +// to the volumes.Update function. For more information about the parameters, see +// the Volume object. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToVolumeUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "volume") +} + +// Update will update the Volume with provided information. To extract the updated +// Volume from the response, call the Extract method on the UpdateResult. +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToVolumeUpdateMap() + 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 +} + +// AttachOptsBuilder allows extensions to add additional parameters to the +// Attach request. +type AttachOptsBuilder interface { + ToVolumeAttachMap() (map[string]any, error) +} + +// AttachMode describes the attachment mode for volumes. +type AttachMode string + +// These constants determine how a volume is attached. +const ( + ReadOnly AttachMode = "ro" + ReadWrite AttachMode = "rw" +) + +// AttachOpts contains options for attaching a Volume. +type AttachOpts struct { + // The mountpoint of this volume. + MountPoint string `json:"mountpoint,omitempty"` + + // The nova instance ID, can't set simultaneously with HostName. + InstanceUUID string `json:"instance_uuid,omitempty"` + + // The hostname of baremetal host, can't set simultaneously with InstanceUUID. + HostName string `json:"host_name,omitempty"` + + // Mount mode of this volume. + Mode AttachMode `json:"mode,omitempty"` +} + +// ToVolumeAttachMap assembles a request body based on the contents of a +// AttachOpts. +func (opts AttachOpts) ToVolumeAttachMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-attach") +} + +// Attach will attach a volume based on the values in AttachOpts. +func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { + b, err := opts.ToVolumeAttachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BeginDetaching will mark the volume as detaching. +func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { + b := map[string]any{"os-begin_detaching": make(map[string]any)} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DetachOptsBuilder allows extensions to add additional parameters to the +// Detach request. +type DetachOptsBuilder interface { + ToVolumeDetachMap() (map[string]any, error) +} + +// DetachOpts contains options for detaching a Volume. +type DetachOpts struct { + // AttachmentID is the ID of the attachment between a volume and instance. + AttachmentID string `json:"attachment_id,omitempty"` +} + +// ToVolumeDetachMap assembles a request body based on the contents of a +// DetachOpts. +func (opts DetachOpts) ToVolumeDetachMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-detach") +} + +// Detach will detach a volume based on volume ID. +func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { + b, err := opts.ToVolumeDetachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Reserve will reserve a volume based on volume ID. +func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { + b := map[string]any{"os-reserve": make(map[string]any)} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unreserve will unreserve a volume based on volume ID. +func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { + b := map[string]any{"os-unreserve": make(map[string]any)} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the +// InitializeConnection request. +type InitializeConnectionOptsBuilder interface { + ToVolumeInitializeConnectionMap() (map[string]any, error) +} + +// InitializeConnectionOpts hosts options for InitializeConnection. +// The fields are specific to the storage driver in use and the destination +// attachment. +type InitializeConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a +// InitializeConnectionOpts. +func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]any, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]any{"os-initialize_connection": b}, err +} + +// InitializeConnection initializes an iSCSI connection by volume ID. +func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { + b, err := opts.ToVolumeInitializeConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the +// TerminateConnection request. +type TerminateConnectionOptsBuilder interface { + ToVolumeTerminateConnectionMap() (map[string]any, error) +} + +// TerminateConnectionOpts hosts options for TerminateConnection. +type TerminateConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a +// TerminateConnectionOpts. +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]any, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]any{"os-terminate_connection": b}, err +} + +// TerminateConnection terminates an iSCSI connection by volume ID. +func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { + b, err := opts.ToVolumeTerminateConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ExtendSizeOptsBuilder allows extensions to add additional parameters to the +// ExtendSize request. +type ExtendSizeOptsBuilder interface { + ToVolumeExtendSizeMap() (map[string]any, error) +} + +// ExtendSizeOpts contains options for extending the size of an existing Volume. +// This object is passed to the volumes.ExtendSize function. +type ExtendSizeOpts struct { + // NewSize is the new size of the volume, in GB. + NewSize int `json:"new_size" required:"true"` +} + +// ToVolumeExtendSizeMap assembles a request body based on the contents of an +// ExtendSizeOpts. +func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-extend") +} + +// ExtendSize will extend the size of the volume based on the provided information. +// This operation does not return a response body. +func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { + b, err := opts.ToVolumeExtendSizeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UploadImageOptsBuilder allows extensions to add additional parameters to the +// UploadImage request. +type UploadImageOptsBuilder interface { + ToVolumeUploadImageMap() (map[string]any, error) +} + +// UploadImageOpts contains options for uploading a Volume to image storage. +type UploadImageOpts struct { + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format,omitempty"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format,omitempty"` + + // The name of image that will be stored in glance. + ImageName string `json:"image_name,omitempty"` + + // Force image creation, usable if volume attached to instance. + Force bool `json:"force,omitempty"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility,omitempty"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected,omitempty"` +} + +// ToVolumeUploadImageMap assembles a request body based on the contents of a +// UploadImageOpts. +func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") +} + +// UploadImage will upload an image based on the values in UploadImageOptsBuilder. +func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { + b, err := opts.ToVolumeUploadImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the volume regardless of state. +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-force_delete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ImageMetadataOptsBuilder allows extensions to add additional parameters to the +// ImageMetadataRequest request. +type ImageMetadataOptsBuilder interface { + ToImageMetadataMap() (map[string]any, error) +} + +// ImageMetadataOpts contains options for setting image metadata to a volume. +type ImageMetadataOpts struct { + // The image metadata to add to the volume as a set of metadata key and value pairs. + Metadata map[string]string `json:"metadata"` +} + +// ToImageMetadataMap assembles a request body based on the contents of a +// ImageMetadataOpts. +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") +} + +// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. +func SetImageMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { + b, err := opts.ToImageMetadataMap() + 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 +} + +// BootableOpts contains options for setting bootable status to a volume. +type BootableOpts struct { + // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. + Bootable bool `json:"bootable"` +} + +// ToBootableMap assembles a request body based on the contents of a +// BootableOpts. +func (opts BootableOpts) ToBootableMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-set_bootable") +} + +// SetBootable will set bootable status on a volume based on the values in BootableOpts +func SetBootable(ctx context.Context, client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { + b, err := opts.ToBootableMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// MigrationPolicy type represents a migration_policy when changing types. +type MigrationPolicy string + +// Supported attributes for MigrationPolicy attribute for changeType operations. +const ( + MigrationPolicyNever MigrationPolicy = "never" + MigrationPolicyOnDemand MigrationPolicy = "on-demand" +) + +// ChangeTypeOptsBuilder allows extensions to add additional parameters to the +// ChangeType request. +type ChangeTypeOptsBuilder interface { + ToVolumeChangeTypeMap() (map[string]any, error) +} + +// ChangeTypeOpts contains options for changing the type of an existing Volume. +// This object is passed to the volumes.ChangeType function. +type ChangeTypeOpts struct { + // NewType is the name of the new volume type of the volume. + NewType string `json:"new_type" required:"true"` + + // MigrationPolicy specifies if the volume should be migrated when it is + // re-typed. Possible values are "on-demand" or "never". If not specified, + // the default is "never". + MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` +} + +// ToVolumeChangeTypeMap assembles a request body based on the contents of an +// ChangeTypeOpts. +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-retype") +} + +// ChangeType will change the volume type of the volume based on the provided information. +// This operation does not return a response body. +func ChangeType(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { + b, err := opts.ToVolumeChangeTypeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ReImageOpts contains options for Re-image a volume. +type ReImageOpts struct { + // New image id + ImageID string `json:"image_id"` + // set true to re-image volumes in reserved state + ReImageReserved bool `json:"reimage_reserved"` +} + +// ToReImageMap assembles a request body based on the contents of a ReImageOpts. +func (opts ReImageOpts) ToReImageMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-reimage") +} + +// ReImage will re-image a volume based on the values in ReImageOpts +func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { + b, err := opts.ToReImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToResetStatusMap() (map[string]any, error) +} + +// ResetStatusOpts contains options for resetting a Volume status. +// For more information about these parameters, please, refer to the Block Storage API V3, +// Volume Actions, ResetStatus volume documentation. +type ResetStatusOpts struct { + // Status is a volume status to reset to. + Status string `json:"status"` + // MigrationStatus is a volume migration status to reset to. + MigrationStatus string `json:"migration_status,omitempty"` + // AttachStatus is a volume attach status to reset to. + AttachStatus string `json:"attach_status,omitempty"` +} + +// ToResetStatusMap assembles a request body based on the contents of a +// ResetStatusOpts. +func (opts ResetStatusOpts) ToResetStatusMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "os-reset_status") +} + +// ResetStatus will reset the existing volume status. ResetStatusResult contains only the error. +// To extract it, call the ExtractErr method on the ResetStatusResult. +func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToResetStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/results.go new file mode 100644 index 000000000..3f184b398 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/results.go @@ -0,0 +1,401 @@ +package volumes + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Attachment represents a Volume Attachment record +type Attachment struct { + AttachedAt time.Time `json:"-"` + AttachmentID string `json:"attachment_id"` + Device string `json:"device"` + HostName string `json:"host_name"` + ID string `json:"id"` + ServerID string `json:"server_id"` + VolumeID string `json:"volume_id"` +} + +// UnmarshalJSON is our unmarshalling helper +func (r *Attachment) UnmarshalJSON(b []byte) error { + type tmp Attachment + var s struct { + tmp + AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Attachment(s.tmp) + + r.AttachedAt = time.Time(s.AttachedAt) + + return err +} + +// Volume contains all the information associated with an OpenStack Volume. +type Volume struct { + // Unique identifier for the volume. + ID string `json:"id"` + // Current status of the volume. + Status string `json:"status"` + // Size of the volume in GB. + Size int `json:"size"` + // AvailabilityZone is which availability zone the volume is in. + AvailabilityZone string `json:"availability_zone"` + // The date when this volume was created. + CreatedAt time.Time `json:"-"` + // The date when this volume was last updated + UpdatedAt time.Time `json:"-"` + // Instances onto which the volume is attached. + Attachments []Attachment `json:"attachments"` + // Human-readable display name for the volume. + Name string `json:"name"` + // Human-readable description for the volume. + Description string `json:"description"` + // The type of volume to create, either SATA or SSD. + VolumeType string `json:"volume_type"` + // The ID of the snapshot from which the volume was created + SnapshotID string `json:"snapshot_id"` + // The ID of another block storage volume from which the current volume was created + SourceVolID string `json:"source_volid"` + // The backup ID, from which the volume was restored + // This field is supported since 3.47 microversion + BackupID *string `json:"backup_id"` + // Arbitrary key-value pairs defined by the user. + Metadata map[string]string `json:"metadata"` + // UserID is the id of the user who created the volume. + UserID string `json:"user_id"` + // Indicates whether this is a bootable volume. + Bootable string `json:"bootable"` + // Encrypted denotes if the volume is encrypted. + Encrypted bool `json:"encrypted"` + // ReplicationStatus is the status of replication. + ReplicationStatus string `json:"replication_status"` + // ConsistencyGroupID is the consistency group ID. + ConsistencyGroupID string `json:"consistencygroup_id"` + // Multiattach denotes if the volume is multi-attach capable. + Multiattach bool `json:"multiattach"` + // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. + VolumeImageMetadata map[string]string `json:"volume_image_metadata"` + // Host is the identifier of the host holding the volume. + Host string `json:"os-vol-host-attr:host"` + // TenantID is the id of the project that owns the volume. + TenantID string `json:"os-vol-tenant-attr:tenant_id"` +} + +// UnmarshalJSON another unmarshalling function +func (r *Volume) UnmarshalJSON(b []byte) error { + type tmp Volume + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Volume(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// VolumePage is a pagination.pager that is returned from a call to the List function. +type VolumePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Volumes. +func (r VolumePage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + volumes, err := ExtractVolumes(r) + return len(volumes) == 0, err +} + +func (page VolumePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"volumes_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. +func ExtractVolumes(r pagination.Page) ([]Volume, error) { + var s []Volume + err := ExtractVolumesInto(r, &s) + return s, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Volume object out of the commonResult object. +func (r commonResult) Extract() (*Volume, error) { + var s Volume + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a volume struct +func (r commonResult) ExtractInto(v any) error { + return r.Result.ExtractIntoStructPtr(v, "volume") +} + +// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes +func ExtractVolumesInto(r pagination.Page, v any) error { + return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// AttachResult contains the response body and error from an Attach request. +type AttachResult struct { + gophercloud.ErrResult +} + +// BeginDetachingResult contains the response body and error from a BeginDetach +// request. +type BeginDetachingResult struct { + gophercloud.ErrResult +} + +// DetachResult contains the response body and error from a Detach request. +type DetachResult struct { + gophercloud.ErrResult +} + +// UploadImageResult contains the response body and error from an UploadImage +// request. +type UploadImageResult struct { + gophercloud.Result +} + +// SetImageMetadataResult contains the response body and error from an SetImageMetadata +// request. +type SetImageMetadataResult struct { + gophercloud.ErrResult +} + +// SetBootableResult contains the response body and error from a SetBootable +// request. +type SetBootableResult struct { + gophercloud.ErrResult +} + +// ReserveResult contains the response body and error from a Reserve request. +type ReserveResult struct { + gophercloud.ErrResult +} + +// UnreserveResult contains the response body and error from an Unreserve +// request. +type UnreserveResult struct { + gophercloud.ErrResult +} + +// TerminateConnectionResult contains the response body and error from a +// TerminateConnection request. +type TerminateConnectionResult struct { + gophercloud.ErrResult +} + +// InitializeConnectionResult contains the response body and error from an +// InitializeConnection request. +type InitializeConnectionResult struct { + gophercloud.Result +} + +// ExtendSizeResult contains the response body and error from an ExtendSize request. +type ExtendSizeResult struct { + gophercloud.ErrResult +} + +// Extract will get the connection information out of the +// InitializeConnectionResult object. +// +// This will be a generic map[string]any and the results will be +// dependent on the type of connection made. +func (r InitializeConnectionResult) Extract() (map[string]any, error) { + var s struct { + ConnectionInfo map[string]any `json:"connection_info"` + } + err := r.ExtractInto(&s) + return s.ConnectionInfo, err +} + +// ImageVolumeType contains volume type information obtained from UploadImage +// action. +type ImageVolumeType struct { + // The ID of a volume type. + ID string `json:"id"` + + // Human-readable display name for the volume type. + Name string `json:"name"` + + // Human-readable description for the volume type. + Description string `json:"display_description"` + + // Flag for public access. + IsPublic bool `json:"is_public"` + + // Extra specifications for volume type. + ExtraSpecs map[string]any `json:"extra_specs"` + + // ID of quality of service specs. + QosSpecsID string `json:"qos_specs_id"` + + // Flag for deletion status of volume type. + Deleted bool `json:"deleted"` + + // The date when volume type was deleted. + DeletedAt time.Time `json:"-"` + + // The date when volume type was created. + CreatedAt time.Time `json:"-"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { + type tmp ImageVolumeType + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImageVolumeType(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + + return err +} + +// VolumeImage contains information about volume uploaded to an image service. +type VolumeImage struct { + // The ID of a volume an image is created from. + VolumeID string `json:"id"` + + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format"` + + // Human-readable description for the volume. + Description string `json:"display_description"` + + // The ID of the created image. + ImageID string `json:"image_id"` + + // Human-readable display name for the image. + ImageName string `json:"image_name"` + + // Size of the volume in GB. + Size int `json:"size"` + + // Current status of the volume. + Status string `json:"status"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` + + // Volume type object of used volume. + VolumeType ImageVolumeType `json:"volume_type"` +} + +func (r *VolumeImage) UnmarshalJSON(b []byte) error { + type tmp VolumeImage + var s struct { + tmp + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = VolumeImage(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// Extract will get an object with info about the uploaded image out of the +// UploadImageResult object. +func (r UploadImageResult) Extract() (VolumeImage, error) { + var s struct { + VolumeImage VolumeImage `json:"os-volume_upload_image"` + } + err := r.ExtractInto(&s) + return s.VolumeImage, err +} + +// ForceDeleteResult contains the response body and error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} + +// ChangeTypeResult contains the response body and error from an ChangeType request. +type ChangeTypeResult struct { + gophercloud.ErrResult +} + +// ReImageResult contains the response body and error from a ReImage request. +type ReImageResult struct { + gophercloud.ErrResult +} + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/urls.go similarity index 75% rename from vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/urls.go index 170724905..a73e2d2b1 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/urls.go @@ -1,6 +1,6 @@ package volumes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") @@ -21,3 +21,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } + +func actionURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("volumes", id, "action") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/util.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/util.go new file mode 100644 index 000000000..6f8d899f5 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes/util.go @@ -0,0 +1,23 @@ +package volumes + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// WaitForStatus will continually poll the resource, checking for a particular 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/v2/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/client.go new file mode 100644 index 000000000..43b569d3b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/client.go @@ -0,0 +1,492 @@ +package openstack + +import ( + "context" + "fmt" + "reflect" + "strings" + + "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 ( + // v2 represents Keystone v2. + // It should never increase beyond 2.0. + v2 = "v2.0" + + // v3 represents Keystone v3. + // The version can be anything from v3 to v3.x. + 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{}) +func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { + base, err := utils.BaseEndpoint(endpoint) + if err != nil { + return nil, err + } + + endpoint = gophercloud.NormalizeURL(endpoint) + base = gophercloud.NormalizeURL(base) + + p := new(gophercloud.ProviderClient) + p.IdentityBase = base + p.IdentityEndpoint = endpoint + p.UseTokenLock() + + 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(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(ctx, client, options) + if err != nil { + return nil, err + } + return client, nil +} + +// 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(ctx, client, versions) + if err != nil { + return err + } + + switch chosen.ID { + case v2: + return v2auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) + case v3: + 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) + } +} + +// AuthenticateV2 explicitly authenticates against the identity v2 endpoint. +func AuthenticateV2(ctx context.Context, client *gophercloud.ProviderClient, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { + return v2auth(ctx, client, "", options, eo) +} + +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 + } + + if endpoint != "" { + v2Client.Endpoint = endpoint + } + + result := tokens2.Create(ctx, v2Client, options) + + err = client.SetTokenAndAuthResult(result) + if err != nil { + return err + } + + catalog, err := result.ExtractServiceCatalog() + if err != nil { + return err + } + + 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 + 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 + } + client.CopyTokenFrom(&tac) + return nil + } + } + client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { + return V2EndpointURL(catalog, opts) + } + + return nil +} + +// AuthenticateV3 explicitly authenticates against the identity v3 service. +func AuthenticateV3(ctx context.Context, client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { + return v3auth(ctx, client, "", options, eo) +} + +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 { + return err + } + + if endpoint != "" { + v3Client.Endpoint = endpoint + } + + var catalog *tokens3.ServiceCatalog + + var tokenID string + // passthroughToken allows to passthrough the token without a scope + var passthroughToken bool + switch v := opts.(type) { + case *gophercloud.AuthOptions: + tokenID = v.TokenID + passthroughToken = (v.Scope == nil || *v.Scope == gophercloud.AuthScope{}) + case *tokens3.AuthOptions: + tokenID = v.TokenID + passthroughToken = (v.Scope == tokens3.Scope{}) + } + + if tokenID != "" && passthroughToken { + // passing through the token ID without requesting a new scope + if opts.CanReauth() { + return fmt.Errorf("cannot use AllowReauth, when the token ID is defined and auth scope is not set") + } + + v3Client.SetToken(tokenID) + result := tokens3.Get(ctx, v3Client, tokenID) + if result.Err != nil { + return result.Err + } + + err = client.SetTokenAndAuthResult(result) + if err != nil { + return err + } + + catalog, err = result.ExtractServiceCatalog() + if err != nil { + return err + } + } else { + var result tokens3.CreateResult + switch opts.(type) { + case *ec2tokens.AuthOptions: + result = ec2tokens.Create(ctx, v3Client, opts) + case *oauth1.AuthOptions: + result = oauth1.Create(ctx, v3Client, opts) + default: + result = tokens3.Create(ctx, v3Client, opts) + } + + err = client.SetTokenAndAuthResult(result) + if err != nil { + return err + } + + catalog, err = result.ExtractServiceCatalog() + if err != nil { + return err + } + } + + if opts.CanReauth() { + // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but + // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, + // this should retry authentication only once + tac := *client + tac.SetThrowaway(true) + tac.ReauthFunc = nil + err = tac.SetTokenAndAuthResult(nil) + if err != nil { + return err + } + var tao tokens3.AuthOptionsBuilder + switch ot := opts.(type) { + case *gophercloud.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + case *tokens3.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + case *ec2tokens.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + case *oauth1.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + default: + tao = opts + } + client.ReauthFunc = func(ctx context.Context) error { + err := v3auth(ctx, &tac, endpoint, tao, eo) + if err != nil { + return err + } + client.CopyTokenFrom(&tac) + return nil + } + } + client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { + return V3EndpointURL(catalog, opts) + } + + return nil +} + +// NewIdentityV2 creates a ServiceClient that may be used to interact with the +// v2 identity service. +func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + endpoint := client.IdentityBase + "v2.0/" + clientType := "identity" + var err error + if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { + eo.ApplyDefaults(clientType) + endpoint, err = client.EndpointLocator(eo) + if err != nil { + return nil, err + } + } + + return &gophercloud.ServiceClient{ + ProviderClient: client, + Endpoint: endpoint, + Type: clientType, + }, nil +} + +// NewIdentityV3 creates a ServiceClient that may be used to access the v3 +// identity service. +func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + endpoint := client.IdentityBase + "v3/" + clientType := "identity" + var err error + if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { + eo.ApplyDefaults(clientType) + endpoint, err = client.EndpointLocator(eo) + if err != nil { + return nil, err + } + } + + // Ensure endpoint still has a suffix of v3. + // This is because EndpointLocator might have found a versionless + // endpoint or the published endpoint is still /v2.0. In both + // cases, we need to fix the endpoint to point to /v3. + base, err := utils.BaseEndpoint(endpoint) + if err != nil { + return nil, err + } + + base = gophercloud.NormalizeURL(base) + + endpoint = base + "v3/" + + return &gophercloud.ServiceClient{ + ProviderClient: client, + Endpoint: endpoint, + Type: clientType, + }, nil +} + +func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { + sc := new(gophercloud.ServiceClient) + eo.ApplyDefaults(clientType) + url, err := client.EndpointLocator(eo) + if err != nil { + return sc, err + } + sc.ProviderClient = client + sc.Endpoint = url + sc.Type = clientType + return sc, nil +} + +// NewBareMetalV1 creates a ServiceClient that may be used with the v1 +// bare metal package. +func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "baremetal") + 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") +} + +// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 +// object storage package. +func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "object-store") +} + +// NewComputeV2 creates a ServiceClient that may be used with the v2 compute +// package. +func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "compute") +} + +// NewNetworkV2 creates a ServiceClient that may be used with the v2 network +// package. +func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "network") + sc.ResourceBase = sc.Endpoint + "v2.0/" + return sc, err +} + +// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 +// block storage service. +func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "volume") +} + +// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 +// block storage service. +func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "volumev2") +} + +// 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") +} + +// 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") +} + +// 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") +} + +// 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") +} + +// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS +// service. +func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "dns") + sc.ResourceBase = sc.Endpoint + "v2/" + return sc, err +} + +// 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") + sc.ResourceBase = sc.Endpoint + "v2/" + return sc, err +} + +// 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") + + // Fixes edge case having an OpenStack lb endpoint with trailing version number. + endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1) + + sc.ResourceBase = endpoint + "v2.0/" + return sc, err +} + +// 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.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") +} + +// 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.ResourceBase = sc.Endpoint + "v1/" + return sc, err +} + +// 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") +} + +// 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") +} + +// 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") +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/BUILD.bazel similarity index 52% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/BUILD.bazel rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/BUILD.bazel index ff470716a..e23ae6848 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/BUILD.bazel @@ -8,11 +8,11 @@ go_library( "results.go", "urls.go", ], - importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones", - importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones", + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones", visibility = ["//visibility:public"], deps = [ - "//vendor/github.com/gophercloud/gophercloud", - "//vendor/github.com/gophercloud/gophercloud/pagination", + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/pagination", ], ) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/doc.go similarity index 63% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/doc.go index 13b6d9d42..59278fda3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/doc.go @@ -3,32 +3,9 @@ Package availabilityzones provides the ability to get lists and detailed availability zone information and to extend a server result with availability zone information. -Example of Extend server result with Availability Zone Information: - - type ServerWithAZ struct { - servers.Server - availabilityzones.ServerAvailabilityZoneExt - } - - var allServers []ServerWithAZ - - allPages, err := servers.List(client, nil).AllPages() - if err != nil { - panic("Unable to retrieve servers: %s", err) - } - - err = servers.ExtractServersInto(allPages, &allServers) - if err != nil { - panic("Unable to extract servers: %s", err) - } - - for _, server := range allServers { - fmt.Println(server.AvailabilityZone) - } - Example of Get Availability Zone Information - allPages, err := availabilityzones.List(computeClient).AllPages() + allPages, err := availabilityzones.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } @@ -44,7 +21,7 @@ Example of Get Availability Zone Information Example of Get Detailed Availability Zone Information - allPages, err := availabilityzones.ListDetail(computeClient).AllPages() + allPages, err := availabilityzones.ListDetail(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/requests.go similarity index 87% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/requests.go index f9a2e86e0..319e61978 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/requests.go @@ -1,8 +1,8 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will return the existing availability zones. diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/results.go similarity index 84% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/results.go index d48a0ea85..3c95a8a24 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/results.go @@ -4,16 +4,10 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) -// ServerAvailabilityZoneExt is an extension to the base Server object. -type ServerAvailabilityZoneExt struct { - // AvailabilityZone is the availabilty zone the server is in. - AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` -} - // ServiceState represents the state of a service in an AvailabilityZone. type ServiceState struct { Active bool `json:"active"` diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/urls.go similarity index 83% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/urls.go index 9d99ec74b..88c748275 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones/urls.go @@ -1,6 +1,6 @@ package availabilityzones -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/BUILD.bazel similarity index 56% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/BUILD.bazel index beda141d1..8b34fe11e 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/BUILD.bazel @@ -10,11 +10,11 @@ go_library( "urls.go", "util.go", ], - importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers", - importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/servers", + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers", visibility = ["//visibility:public"], deps = [ - "//vendor/github.com/gophercloud/gophercloud", - "//vendor/github.com/gophercloud/gophercloud/pagination", + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/pagination", ], ) 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..dd3b132d1 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers/requests.go @@ -0,0 +1,1369 @@ +package servers + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "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"` +} + +// 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"` +} + +// 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}, + }) + _, 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(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 63% 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..dc5156417 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 @@ -9,8 +9,8 @@ import ( "path" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type serverResult struct { @@ -24,11 +24,11 @@ func (r serverResult) Extract() (*Server, error) { return &s, err } -func (r serverResult) ExtractInto(v interface{}) error { +func (r serverResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "server") } -func ExtractServersInto(r pagination.Page, v interface{}) error { +func ExtractServersInto(r pagination.Page, v any) error { return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") } @@ -182,15 +182,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 +198,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 +211,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 +228,57 @@ 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"` } type AttachedVolume struct { @@ -241,11 +292,53 @@ 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"` } err := json.Unmarshal(b, &s) if err != nil { @@ -255,7 +348,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 +357,9 @@ func (r *Server) UnmarshalJSON(b []byte) error { } } + r.LaunchedAt = time.Time(s.LaunchedAt) + r.TerminatedAt = time.Time(s.TerminatedAt) + return err } @@ -442,3 +538,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/compute/v2/extensions/volumeattach/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/BUILD.bazel similarity index 52% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/BUILD.bazel rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/BUILD.bazel index cd0fc2c49..ca4e0c5fa 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/BUILD.bazel @@ -8,11 +8,11 @@ go_library( "results.go", "urls.go", ], - importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", - importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach", visibility = ["//visibility:public"], deps = [ - "//vendor/github.com/gophercloud/gophercloud", - "//vendor/github.com/gophercloud/gophercloud/pagination", + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/pagination", ], ) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/doc.go similarity index 73% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/doc.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/doc.go index 857ab19cc..9ab3253ef 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/doc.go @@ -12,7 +12,7 @@ Example to Attach a Volume VolumeID: volumeID, } - result, err := volumeattach.Create(computeClient, serverID, createOpts).Extract() + result, err := volumeattach.Create(context.TODO(), computeClient, serverID, createOpts).Extract() if err != nil { panic(err) } @@ -22,7 +22,7 @@ Example to Detach a Volume serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" volumeID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983" - err := volumeattach.Delete(computeClient, serverID, volumeID).ExtractErr() + err := volumeattach.Delete(context.TODO(), computeClient, serverID, volumeID).ExtractErr() if err != nil { panic(err) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/requests.go similarity index 71% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/requests.go index fe0b1075d..60d89ad7d 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/requests.go @@ -1,8 +1,10 @@ package volumeattach import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List returns a Pager that allows you to iterate over a collection of @@ -15,7 +17,7 @@ func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { // CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { - ToVolumeAttachmentCreateMap() (map[string]interface{}, error) + ToVolumeAttachmentCreateMap() (map[string]any, error) } // CreateOpts specifies volume attachment creation or import parameters. @@ -37,18 +39,18 @@ type CreateOpts struct { } // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volumeAttachment") } // Create requests the creation of a new volume attachment on the server. -func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeAttachmentCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -56,16 +58,16 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB } // Get returns public data about a previously created VolumeAttachment. -func Get(client *gophercloud.ServiceClient, serverID, volumeID string) (r GetResult) { - resp, err := client.Get(getURL(client, serverID, volumeID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, volumeID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, serverID, volumeID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored VolumeAttachment from // the server. -func Delete(client *gophercloud.ServiceClient, serverID, volumeID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, serverID, volumeID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, serverID, volumeID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, serverID, volumeID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/results.go similarity index 96% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/results.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/results.go index e5f565a35..3b817750c 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/results.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/results.go @@ -1,8 +1,8 @@ package volumeattach import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // VolumeAttachment contains attachment information between a volume diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/urls.go similarity index 93% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/urls.go rename to vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/urls.go index 083f8dc45..9a274294d 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach/urls.go @@ -1,6 +1,6 @@ package volumeattach -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-volume_attachments" diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go new file mode 100644 index 000000000..4d89fb5d0 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/doc.go @@ -0,0 +1,14 @@ +/* +Package openstack contains resources for the individual OpenStack projects +supported in Gophercloud. It also includes functions to authenticate to an +OpenStack cloud and for provisioning various service-level clients. + +Example of Creating a Service Client + + ao, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(context.TODO(), ao) + client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +*/ +package openstack diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go new file mode 100644 index 000000000..2cdbd3e7f --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/endpoint_location.go @@ -0,0 +1,111 @@ +package openstack + +import ( + "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" +) + +/* +V2EndpointURL discovers the endpoint URL for a specific service from a +ServiceCatalog acquired during the v2 identity service. + +The specified EndpointOpts are used to identify a unique, unambiguous endpoint +to return. It's an error both when multiple endpoints match the provided +criteria and when none do. The minimum that can be specified is a Type, but you +will also often need to specify a Name and/or a Region depending on what's +available on your OpenStack deployment. +*/ +func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { + // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. + var endpoints = make([]tokens2.Endpoint, 0, 1) + for _, entry := range catalog.Entries { + if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { + for _, endpoint := range entry.Endpoints { + if opts.Region == "" || endpoint.Region == opts.Region { + endpoints = append(endpoints, endpoint) + } + } + } + } + + // 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 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 + } + } + + // Report an error if there were no matching endpoints. + err := &gophercloud.ErrEndpointNotFound{} + return "", err +} + +/* +V3EndpointURL discovers the endpoint URL for a specific service from a Catalog +acquired during the v3 identity service. + +The specified EndpointOpts are used to identify a unique, unambiguous endpoint +to return. It's an error both when multiple endpoints match the provided +criteria and when none do. The minimum that can be specified is a Type, but you +will also often need to specify a Name and/or a Region depending on what's +available on your OpenStack deployment. +*/ +func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { + // Extract Endpoints from the catalog entries that match the requested Type, Interface, + // Name if provided, and Region if provided. + var endpoints = make([]tokens3.Endpoint, 0, 1) + for _, entry := range catalog.Entries { + if (entry.Type == opts.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)) && + (opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { + endpoints = append(endpoints, endpoint) + } + } + } + } + + // 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/v2/openstack/errors.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/errors.go new file mode 100644 index 000000000..f5273483e --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/errors.go @@ -0,0 +1,47 @@ +package openstack + +import ( + "fmt" + + "github.com/gophercloud/gophercloud/v2" +) + +// ErrEndpointNotFound is the error when no suitable endpoint can be found +// in the user's catalog +type ErrEndpointNotFound struct{ gophercloud.BaseError } + +func (e ErrEndpointNotFound) Error() string { + return "No suitable endpoint could be found in the service catalog." +} + +// ErrInvalidAvailabilityProvided is the error when an invalid endpoint +// availability is provided +type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput } + +func (e ErrInvalidAvailabilityProvided) Error() string { + return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value) +} + +// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not +// found +type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput } + +func (e ErrNoAuthURL) Error() string { + return "Environment variable OS_AUTH_URL needs to be set." +} + +// ErrNoUsername is the error when the OS_USERNAME environment variable is not +// found +type ErrNoUsername struct{ gophercloud.ErrInvalidInput } + +func (e ErrNoUsername) Error() string { + return "Environment variable OS_USERNAME needs to be set." +} + +// ErrNoPassword is the error when the OS_PASSWORD environment variable is not +// found +type ErrNoPassword struct{ gophercloud.ErrInvalidInput } + +func (e ErrNoPassword) Error() string { + return "Environment variable OS_PASSWORD needs to be set." +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/BUILD.bazel new file mode 100644 index 000000000..7ab856dc8 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tenants", + srcs = [ + "doc.go", + "requests.go", + "results.go", + "urls.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/pagination", + ], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go new file mode 100644 index 000000000..b14d69a56 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/doc.go @@ -0,0 +1,65 @@ +/* +Package tenants provides information and interaction with the +tenants API resource for the OpenStack Identity service. + +See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 +and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants +for more information. + +Example to List Tenants + + listOpts := &tenants.ListOpts{ + Limit: 2, + } + + allPages, err := tenants.List(identityClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allTenants, err := tenants.ExtractTenants(allPages) + if err != nil { + panic(err) + } + + for _, tenant := range allTenants { + fmt.Printf("%+v\n", tenant) + } + +Example to Create a Tenant + + createOpts := tenants.CreateOpts{ + Name: "tenant_name", + Description: "this is a tenant", + Enabled: gophercloud.Enabled, + } + + tenant, err := tenants.Create(context.TODO(), identityClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Tenant + + tenantID := "e6db6ed6277c461a853458589063b295" + + updateOpts := tenants.UpdateOpts{ + Description: "this is a new description", + Enabled: gophercloud.Disabled, + } + + tenant, err := tenants.Update(context.TODO(), identityClient, tenantID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Tenant + + tenantID := "e6db6ed6277c461a853458589063b295" + + err := tenants.Delete(context.TODO(), identitYClient, tenantID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package tenants diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go new file mode 100644 index 000000000..a08980df2 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/requests.go @@ -0,0 +1,122 @@ +package tenants + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// 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. + Marker string `q:"marker"` + + // Limit specifies the page size. + Limit int `q:"limit"` +} + +// List enumerates the Tenants to which the current token has access. +func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { + url := listURL(client) + if opts != nil { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return pagination.Pager{Err: err} + } + url += q.String() + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TenantPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOpts represents the options needed when creating new tenant. +type CreateOpts struct { + // Name is the name of the tenant. + Name string `json:"name" required:"true"` + + // Description is the description of the tenant. + Description string `json:"description,omitempty"` + + // Enabled sets the tenant status to enabled or disabled. + Enabled *bool `json:"enabled,omitempty"` +} + +// CreateOptsBuilder enables extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToTenantCreateMap() (map[string]any, error) +} + +// ToTenantCreateMap assembles a request body based on the contents of +// a CreateOpts. +func (opts CreateOpts) ToTenantCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "tenant") +} + +// Create is the operation responsible for creating new tenant. +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(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get requests details on a single tenant by ID. +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 +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToTenantUpdateMap() (map[string]any, error) +} + +// UpdateOpts specifies the base attributes that may be updated on an existing +// tenant. +type UpdateOpts struct { + // Name is the name of the tenant. + Name string `json:"name,omitempty"` + + // Description is the description of the tenant. + Description *string `json:"description,omitempty"` + + // Enabled sets the tenant status to enabled or disabled. + Enabled *bool `json:"enabled,omitempty"` +} + +// ToTenantUpdateMap formats an UpdateOpts structure into a request body. +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(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(ctx, updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete is the operation responsible for permanently deleting a tenant. +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/v2/openstack/identity/v2/tenants/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/results.go new file mode 100644 index 000000000..2569ffec0 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/results.go @@ -0,0 +1,95 @@ +package tenants + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Tenant is a grouping of users in the identity service. +type Tenant struct { + // ID is a unique identifier for this tenant. + ID string `json:"id"` + + // Name is a friendlier user-facing name for this tenant. + Name string `json:"name"` + + // Description is a human-readable explanation of this Tenant's purpose. + Description string `json:"description"` + + // Enabled indicates whether or not a tenant is active. + Enabled bool `json:"enabled"` +} + +// TenantPage is a single page of Tenant results. +type TenantPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Tenants contains any results. +func (r TenantPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + tenants, err := ExtractTenants(r) + return len(tenants) == 0, err +} + +// NextPageURL extracts the "next" link from the tenants_links section of the result. +func (r TenantPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"tenants_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractTenants returns a slice of Tenants contained in a single page of +// results. +func ExtractTenants(r pagination.Page) ([]Tenant, error) { + var s struct { + Tenants []Tenant `json:"tenants"` + } + err := (r.(TenantPage)).ExtractInto(&s) + return s.Tenants, err +} + +type tenantResult struct { + gophercloud.Result +} + +// Extract interprets any tenantResults as a Tenant. +func (r tenantResult) Extract() (*Tenant, error) { + var s struct { + Tenant *Tenant `json:"tenant"` + } + err := r.ExtractInto(&s) + return s.Tenant, err +} + +// GetResult is the response from a Get request. Call its Extract method to +// interpret it as a Tenant. +type GetResult struct { + tenantResult +} + +// CreateResult is the response from a Create request. Call its Extract method +// to interpret it as a Tenant. +type CreateResult struct { + tenantResult +} + +// DeleteResult is the response from a Get request. Call its ExtractErr method +// to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// UpdateResult is the response from a Update request. Call its Extract method +// to interpret it as a Tenant. +type UpdateResult struct { + tenantResult +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go new file mode 100644 index 000000000..4c2aaf384 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants/urls.go @@ -0,0 +1,23 @@ +package tenants + +import "github.com/gophercloud/gophercloud/v2" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("tenants") +} + +func getURL(client *gophercloud.ServiceClient, tenantID string) string { + return client.ServiceURL("tenants", tenantID) +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("tenants") +} + +func deleteURL(client *gophercloud.ServiceClient, tenantID string) string { + return client.ServiceURL("tenants", tenantID) +} + +func updateURL(client *gophercloud.ServiceClient, tenantID string) string { + return client.ServiceURL("tenants", tenantID) +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/BUILD.bazel new file mode 100644 index 000000000..2d7510b23 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tokens", + srcs = [ + "doc.go", + "requests.go", + "results.go", + "urls.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants", + ], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go new file mode 100644 index 000000000..7cfc36730 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/doc.go @@ -0,0 +1,46 @@ +/* +Package tokens provides information and interaction with the token API +resource for the OpenStack Identity service. + +For more information, see: +http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 + +Example to Create an Unscoped Token from a Password + + authOpts := gophercloud.AuthOptions{ + Username: "user", + Password: "pass" + } + + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token from a Tenant ID and Password + + authOpts := gophercloud.AuthOptions{ + Username: "user", + Password: "password", + TenantID: "fc394f2ab2df4114bde39905f800dc57" + } + + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token from a Tenant Name and Password + + authOpts := gophercloud.AuthOptions{ + Username: "user", + Password: "password", + TenantName: "tenantname" + } + + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() + if err != nil { + panic(err) + } +*/ +package tokens diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go new file mode 100644 index 000000000..5afa8fd26 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/requests.go @@ -0,0 +1,114 @@ +package tokens + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// PasswordCredentialsV2 represents the required options to authenticate +// with a username and password. +type PasswordCredentialsV2 struct { + Username string `json:"username" required:"true"` + Password string `json:"password" required:"true"` +} + +// TokenCredentialsV2 represents the required options to authenticate +// with a token. +type TokenCredentialsV2 struct { + ID string `json:"id,omitempty" required:"true"` +} + +// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the +// AuthOptionsBuilder interface. +type AuthOptionsV2 struct { + PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` + + // The TenantID and TenantName fields are optional for the Identity V2 API. + // Some providers allow you to specify a TenantName instead of the TenantId. + // Some require both. Your provider's authentication policies will determine + // how these fields influence authentication. + TenantID string `json:"tenantId,omitempty"` + TenantName string `json:"tenantName,omitempty"` + + // TokenCredentials allows users to authenticate (possibly as another user) + // with an authentication token ID. + TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` +} + +// AuthOptionsBuilder allows extensions to add additional parameters to the +// token create request. +type AuthOptionsBuilder interface { + // ToTokenCreateMap assembles the Create request body, returning an error + // if parameters are missing or inconsistent. + ToTokenV2CreateMap() (map[string]any, error) + CanReauth() bool +} + +// AuthOptions are the valid options for Openstack Identity v2 authentication. +// For field descriptions, see gophercloud.AuthOptions. +type AuthOptions struct { + IdentityEndpoint string `json:"-"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + TenantID string `json:"tenantId,omitempty"` + TenantName string `json:"tenantName,omitempty"` + AllowReauth bool `json:"-"` + TokenID string +} + +// ToTokenV2CreateMap builds a token request body from the given AuthOptions. +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]any, error) { + v2Opts := AuthOptionsV2{ + TenantID: opts.TenantID, + TenantName: opts.TenantName, + } + + if opts.Password != "" { + v2Opts.PasswordCredentials = &PasswordCredentialsV2{ + Username: opts.Username, + Password: opts.Password, + } + } else { + v2Opts.TokenCredentials = &TokenCredentialsV2{ + ID: opts.TokenID, + } + } + + b, err := gophercloud.BuildRequestBody(v2Opts, "auth") + if err != nil { + return nil, err + } + 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(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(ctx, CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + OmitHeaders: []string{"X-Auth-Token"}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get validates and retrieves information for user's token. +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) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go new file mode 100644 index 000000000..516371ee8 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/results.go @@ -0,0 +1,174 @@ +package tokens + +import ( + "time" + + "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 +// token. +type Token struct { + // ID provides the primary means of identifying a user to the OpenStack API. + // OpenStack defines this field as an opaque value, so do not depend on its + // content. It is safe, however, to compare for equality. + ID string + + // ExpiresAt provides a timestamp in ISO 8601 format, indicating when the + // authentication token becomes invalid. After this point in time, future + // API requests made using this authentication token will respond with + // errors. Either the caller will need to reauthenticate manually, or more + // preferably, the caller should exploit automatic re-authentication. + // See the AuthOptions structure for more details. + ExpiresAt time.Time + + // Tenant provides information about the tenant to which this token grants + // access. + Tenant tenants.Tenant +} + +// Role is a role for a user. +type Role struct { + Name string `json:"name"` +} + +// User is an OpenStack user. +type User struct { + ID string `json:"id"` + Name string `json:"name"` + UserName string `json:"username"` + Roles []Role `json:"roles"` +} + +// Endpoint represents a single API endpoint offered by a service. +// It provides the public and internal URLs, if supported, along with a region +// specifier, again if provided. +// +// The significance of the Region field will depend upon your provider. +// +// In addition, the interface offered by the service will have version +// information associated with it through the VersionId, VersionInfo, and +// VersionList fields, if provided or supported. +// +// In all cases, fields which aren't supported by the provider and service +// combined will assume a zero-value (""). +type Endpoint struct { + TenantID string `json:"tenantId"` + PublicURL string `json:"publicURL"` + InternalURL string `json:"internalURL"` + AdminURL string `json:"adminURL"` + Region string `json:"region"` + VersionID string `json:"versionId"` + VersionInfo string `json:"versionInfo"` + VersionList string `json:"versionList"` +} + +// CatalogEntry provides a type-safe interface to an Identity API V2 service +// catalog listing. +// +// Each class of service, such as cloud DNS or block storage services, will have +// a single CatalogEntry representing it. +// +// Note: when looking for the desired service, try, whenever possible, to key +// off the type field. Otherwise, you'll tie the representation of the service +// to a specific provider. +type CatalogEntry struct { + // Name will contain the provider-specified name for the service. + Name string `json:"name"` + + // Type will contain a type string if OpenStack defines a type for the + // service. Otherwise, for provider-specific services, the provider may assign + // their own type strings. + Type string `json:"type"` + + // Endpoints will let the caller iterate over all the different endpoints that + // may exist for the service. + Endpoints []Endpoint `json:"endpoints"` +} + +// ServiceCatalog provides a view into the service catalog from a previous, +// successful authentication. +type ServiceCatalog struct { + Entries []CatalogEntry +} + +// CreateResult is the response from a Create request. Use ExtractToken() to +// interpret it as a Token, or ExtractServiceCatalog() to interpret it as a +// service catalog. +type CreateResult struct { + gophercloud.Result +} + +// GetResult is the deferred response from a Get call, which is the same with a +// Created token. Use ExtractUser() to interpret it as a User. +type GetResult struct { + CreateResult +} + +// ExtractToken returns the just-created Token from a CreateResult. +func (r CreateResult) ExtractToken() (*Token, error) { + var s struct { + Access struct { + Token struct { + Expires string `json:"expires"` + ID string `json:"id"` + Tenant tenants.Tenant `json:"tenant"` + } `json:"token"` + } `json:"access"` + } + + err := r.ExtractInto(&s) + if err != nil { + return nil, err + } + + expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires) + if err != nil { + return nil, err + } + + return &Token{ + ID: s.Access.Token.ID, + ExpiresAt: expiresTs, + Tenant: s.Access.Token.Tenant, + }, nil +} + +// ExtractTokenID implements the gophercloud.AuthResult interface. The returned +// string is the same as the ID field of the Token struct returned from +// ExtractToken(). +func (r CreateResult) ExtractTokenID() (string, error) { + var s struct { + Access struct { + Token struct { + ID string `json:"id"` + } `json:"token"` + } `json:"access"` + } + err := r.ExtractInto(&s) + return s.Access.Token.ID, err +} + +// ExtractServiceCatalog returns the ServiceCatalog that was generated along +// with the user's Token. +func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { + var s struct { + Access struct { + Entries []CatalogEntry `json:"serviceCatalog"` + } `json:"access"` + } + err := r.ExtractInto(&s) + return &ServiceCatalog{Entries: s.Access.Entries}, err +} + +// ExtractUser returns the User from a GetResult. +func (r GetResult) ExtractUser() (*User, error) { + var s struct { + Access struct { + User User `json:"user"` + } `json:"access"` + } + err := r.ExtractInto(&s) + return &s.Access.User, err +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go new file mode 100644 index 000000000..845cdb58b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens/urls.go @@ -0,0 +1,13 @@ +package tokens + +import "github.com/gophercloud/gophercloud/v2" + +// CreateURL generates the URL used to create new Tokens. +func CreateURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("tokens") +} + +// GetURL generates the URL used to Validate Tokens. +func GetURL(client *gophercloud.ServiceClient, token string) string { + return client.ServiceURL("tokens", token) +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/BUILD.bazel new file mode 100644 index 000000000..eb5b3de1e --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "ec2tokens", + srcs = [ + "doc.go", + "requests.go", + "urls.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens", + ], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go new file mode 100644 index 000000000..bd74473da --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/doc.go @@ -0,0 +1,40 @@ +/* +Package tokens provides information and interaction with the EC2 token API +resource for the OpenStack Identity service. + +For more information, see: +https://docs.openstack.org/api-ref/identity/v2-ext/ + +Example to Create a Token From an EC2 access and secret keys + + var authOptions tokens.AuthOptionsBuilder + authOptions = &ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + } + + token, err := ec2tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to auth a client using EC2 access and secret keys + + client, err := openstack.NewClient("http://localhost:5000/v3") + if err != nil { + panic(err) + } + + var authOptions tokens.AuthOptionsBuilder + authOptions = &ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + AllowReauth: true, + } + + err = openstack.AuthenticateV3(context.TODO(), client, authOptions, gophercloud.EndpointOpts{}) + if err != nil { + panic(err) + } +*/ +package ec2tokens diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go new file mode 100644 index 000000000..5b1f3d688 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/requests.go @@ -0,0 +1,378 @@ +package ec2tokens + +import ( + "context" + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + "fmt" + "net/url" + "sort" + "strings" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" +) + +const ( + // EC2CredentialsAwsRequestV4 is a constant, used to generate AWS + // Credential V4. + EC2CredentialsAwsRequestV4 = "aws4_request" + // EC2CredentialsHmacSha1V2 is a HMAC SHA1 signature method. Used to + // generate AWS Credential V2. + EC2CredentialsHmacSha1V2 = "HmacSHA1" + // EC2CredentialsHmacSha256V2 is a HMAC SHA256 signature method. Used + // to generate AWS Credential V2. + EC2CredentialsHmacSha256V2 = "HmacSHA256" + // EC2CredentialsAwsHmacV4 is an AWS signature V4 signing method. + // More details: + // https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html + EC2CredentialsAwsHmacV4 = "AWS4-HMAC-SHA256" + // EC2CredentialsTimestampFormatV4 is an AWS signature V4 timestamp + // format. + EC2CredentialsTimestampFormatV4 = "20060102T150405Z" + // EC2CredentialsDateFormatV4 is an AWS signature V4 date format. + EC2CredentialsDateFormatV4 = "20060102" +) + +// AuthOptions represents options for authenticating a user using EC2 credentials. +type AuthOptions struct { + // Access is the EC2 Credential Access ID. + Access string `json:"access" required:"true"` + // Secret is the EC2 Credential Secret, used to calculate signature. + // Not used, when a Signature is is. + Secret string `json:"-"` + // Host is a HTTP request Host header. Used to calculate an AWS + // signature V2. For signature V4 set the Host inside Headers map. + // Optional. + Host string `json:"host"` + // Path is a HTTP request path. Optional. + Path string `json:"path"` + // Verb is a HTTP request method. Optional. + Verb string `json:"verb"` + // Headers is a map of HTTP request headers. Optional. + Headers map[string]string `json:"headers"` + // Region is a region name to calculate an AWS signature V4. Optional. + Region string `json:"-"` + // Service is a service name to calculate an AWS signature V4. Optional. + Service string `json:"-"` + // Params is a map of GET method parameters. Optional. + Params map[string]string `json:"params"` + // AllowReauth allows Gophercloud to re-authenticate automatically + // if/when your token expires. + AllowReauth bool `json:"-"` + // 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 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"` + // Timestamp is a timestamp to calculate a V4 signature. Optional. + Timestamp *time.Time `json:"-"` + // Token is a []byte string (encoded to base64 automatically) which was + // signed by an EC2 secret key. Used by S3 tokens for validation only. + // Token must be set with a Signature. If a Signature is not provided, + // a Token will be generated automatically along with a Signature. + Token []byte `json:"token,omitempty"` +} + +// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string +// for an AWS signature V2. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L133 +func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string { + var keys []string + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + var pairs []string + for _, k := range keys { + pairs = append(pairs, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k]))) + } + + return strings.Join(pairs, "&") +} + +// EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature +// V2. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148 +func EC2CredentialsBuildStringToSignV2(opts AuthOptions) []byte { + stringToSign := strings.Join([]string{ + opts.Verb, + opts.Host, + opts.Path, + }, "\n") + + return []byte(strings.Join([]string{ + stringToSign, + EC2CredentialsBuildCanonicalQueryStringV2(opts.Params), + }, "\n")) +} + +// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string +// for an AWS signature V4. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L244 +func EC2CredentialsBuildCanonicalQueryStringV4(verb string, params map[string]string) string { + if verb == "POST" { + return "" + } + return EC2CredentialsBuildCanonicalQueryStringV2(params) +} + +// EC2CredentialsBuildCanonicalHeadersV4 builds a canonical string based on +// "headers" map and "signedHeaders" string parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L216 +func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHeaders string) string { + headersLower := make(map[string]string, len(headers)) + for k, v := range headers { + headersLower[strings.ToLower(k)] = v + } + + var headersList []string + for _, h := range strings.Split(signedHeaders, ";") { + if v, ok := headersLower[h]; ok { + headersList = append(headersList, h+":"+v) + } + } + + return strings.Join(headersList, "\n") + "\n" +} + +// EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on +// input parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169 +func EC2CredentialsBuildSignatureKeyV4(secret, region, service string, date time.Time) []byte { + kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date.Format(EC2CredentialsDateFormatV4))) + kRegion := sumHMAC256(kDate, []byte(region)) + kService := sumHMAC256(kRegion, []byte(service)) + return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4)) +} + +// EC2CredentialsBuildStringToSignV4 builds an AWS v4 signature string to sign +// based on input parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251 +func EC2CredentialsBuildStringToSignV4(opts AuthOptions, signedHeaders string, bodyHash string, date time.Time) []byte { + scope := strings.Join([]string{ + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + }, "/") + + canonicalRequest := strings.Join([]string{ + opts.Verb, + opts.Path, + EC2CredentialsBuildCanonicalQueryStringV4(opts.Verb, opts.Params), + EC2CredentialsBuildCanonicalHeadersV4(opts.Headers, signedHeaders), + signedHeaders, + bodyHash, + }, "\n") + hash := sha256.Sum256([]byte(canonicalRequest)) + + return []byte(strings.Join([]string{ + EC2CredentialsAwsHmacV4, + date.Format(EC2CredentialsTimestampFormatV4), + scope, + hex.EncodeToString(hash[:]), + }, "\n")) +} + +// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input +// parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L285..L286 +func EC2CredentialsBuildSignatureV4(key []byte, stringToSign []byte) string { + return hex.EncodeToString(sumHMAC256(key, stringToSign)) +} + +// EC2CredentialsBuildAuthorizationHeaderV4 builds an AWS v4 Authorization +// header based on auth parameters, date and signature +func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders string, signature string, date time.Time) string { + return fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s", + EC2CredentialsAwsHmacV4, + opts.Access, + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + signedHeaders, + signature) +} + +// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder +// interface. +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]any) (map[string]string, error) { + return nil, nil +} + +// CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface +func (opts *AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +// ToTokenV3CreateMap formats an AuthOptions into a create request. +func (opts *AuthOptions) ToTokenV3CreateMap(map[string]any) (map[string]any, error) { + b, err := gophercloud.BuildRequestBody(opts, "credentials") + if err != nil { + return nil, err + } + + if opts.Signature != nil { + return b, nil + } + + // calculate signature, when it is not set + 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" { + delete(c, "body_hash") + delete(c, "headers") + if v, ok := p["SignatureMethod"]; ok { + // params is a map of strings + strToSign := EC2CredentialsBuildStringToSignV2(*opts) + switch v { + case EC2CredentialsHmacSha1V2: + // keystone uses this method only when HmacSHA256 is not available on the server side + // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156 + c["signature"] = sumHMAC1([]byte(opts.Secret), strToSign) + return b, nil + case EC2CredentialsHmacSha256V2: + c["signature"] = sumHMAC256([]byte(opts.Secret), strToSign) + return b, nil + } + return nil, fmt.Errorf("unsupported signature method: %s", v) + } + return nil, fmt.Errorf("signature method must be provided") + } else if ok { + return nil, fmt.Errorf("unsupported signature version: %s", v) + } + + // it is not a signature v2, but a signature v4 + date := time.Now().UTC() + if opts.Timestamp != nil { + date = *opts.Timestamp + } + if v := c["body_hash"]; v == nil { + // when body_hash is not set, generate a random one + bodyHash, err := randomBodyHash() + if err != nil { + return nil, fmt.Errorf("failed to generate random hash") + } + c["body_hash"] = bodyHash + } + + signedHeaders := h["X-Amz-SignedHeaders"] + + stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date) + key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date) + c["signature"] = EC2CredentialsBuildSignatureV4(key, stringToSign) + h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4) + h["Authorization"] = EC2CredentialsBuildAuthorizationHeaderV4(*opts, signedHeaders, c["signature"].(string), date) + + // token is only used for S3 tokens validation and will be removed when using EC2 validation + c["token"] = stringToSign + + return b, nil +} + +// Create authenticates and either generates a new token from EC2 credentials +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 + return + } + + // delete "token" element, since it is used in s3tokens + deleteBodyElements(b, "token") + + resp, err := c.Post(ctx, ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't +// generate a new token ID, but returns a 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 + return + } + + // delete unused element, since it is used in ec2tokens only + deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") + + resp, err := c.Post(ctx, s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// The following are small helper functions used to help build the signature. + +// sumHMAC1 is a func to implement the HMAC SHA1 signature method. +func sumHMAC1(key []byte, data []byte) []byte { + hash := hmac.New(sha1.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// sumHMAC256 is a func to implement the HMAC SHA256 signature method. +func sumHMAC256(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// randomBodyHash is a func to generate a random sha256 hexdigest. +func randomBodyHash() (string, error) { + h := make([]byte, 64) + 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]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]any); v != nil { + for k, v := range v { + m[k] = v.(string) + } + } + + c[key] = m + + return m +} + +// deleteBodyElements deletes map body elements +func deleteBodyElements(b map[string]any, elements ...string) { + if c, ok := b["credentials"].(map[string]any); ok { + for _, k := range elements { + delete(c, k) + } + } +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go new file mode 100644 index 000000000..91add91eb --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens/urls.go @@ -0,0 +1,11 @@ +package ec2tokens + +import "github.com/gophercloud/gophercloud/v2" + +func ec2tokensURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("ec2tokens") +} + +func s3tokensURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("s3tokens") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/BUILD.bazel new file mode 100644 index 000000000..dcb97d789 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "oauth1", + srcs = [ + "doc.go", + "requests.go", + "results.go", + "urls.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gophercloud/gophercloud/v2:gophercloud", + "//vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens", + "//vendor/github.com/gophercloud/gophercloud/v2/pagination", + ], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go new file mode 100644 index 000000000..c0cfa924a --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/doc.go @@ -0,0 +1,122 @@ +/* +Package oauth1 enables management of OpenStack OAuth1 tokens and Authentication. + +Example to Create an OAuth1 Consumer + + createConsumerOpts := oauth1.CreateConsumerOpts{ + Description: "My consumer", + } + consumer, err := oauth1.CreateConsumer(context.TODO(), identityClient, createConsumerOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Consumer secret is available only on create response + fmt.Printf("Consumer: %+v\n", consumer) + +Example to Request an unauthorized OAuth1 token + + requestTokenOpts := oauth1.RequestTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthSignatureMethod: oauth1.HMACSHA1, + RequestedProjectID: projectID, + } + requestToken, err := oauth1.RequestToken(context.TODO(), identityClient, requestTokenOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Request token secret is available only on request response + fmt.Printf("Request token: %+v\n", requestToken) + +Example to Authorize an unauthorized OAuth1 token + + authorizeTokenOpts := oauth1.AuthorizeTokenOpts{ + Roles: []oauth1.Role{ + {Name: "member"}, + }, + } + authToken, err := oauth1.AuthorizeToken(context.TODO(), identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Verifier ID of the unauthorized Token: %+v\n", authToken.OAuthVerifier) + +Example to Create an OAuth1 Access Token + + accessTokenOpts := oauth1.CreateAccessTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthToken: requestToken.OAuthToken, + OAuthTokenSecret: requestToken.OAuthTokenSecret, + OAuthVerifier: authToken.OAuthVerifier, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + accessToken, err := oauth1.CreateAccessToken(context.TODO(), identityClient, accessTokenOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Access token secret is available only on create response + fmt.Printf("OAuth1 Access Token: %+v\n", accessToken) + +Example to List User's OAuth1 Access Tokens + + allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages(context.TODO()) + if err != nil { + panic(err) + } + accessTokens, err := oauth1.ExtractAccessTokens(allPages) + if err != nil { + panic(err) + } + + for _, accessToken := range accessTokens { + fmt.Printf("Access Token: %+v\n", accessToken) + } + +Example to Authenticate a client using OAuth1 method + + client, err := openstack.NewClient("http://localhost:5000/v3") + if err != nil { + panic(err) + } + + authOptions := &oauth1.AuthOptions{ + // consumer token, created earlier + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + // access token, created earlier + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + err = openstack.AuthenticateV3(context.TODO(), client, authOptions, gophercloud.EndpointOpts{}) + if err != nil { + panic(err) + } + +Example to Create a Token using OAuth1 method + + var oauth1Token struct { + tokens.Token + oauth1.TokenExt + } + + createOpts := &oauth1.AuthOptions{ + // consumer token, created earlier + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + // access token, created earlier + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + err := tokens.Create(context.TODO(), identityClient, createOpts).ExtractInto(&oauth1Token) + if err != nil { + panic(err) + } +*/ +package oauth1 diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go new file mode 100644 index 000000000..8c66b36e2 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/requests.go @@ -0,0 +1,588 @@ +package oauth1 + +import ( + "context" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "io" + "math/rand" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "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. +type SignatureMethod string + +const ( + // HMACSHA1 is a recommended OAuth1 signature method. + HMACSHA1 SignatureMethod = "HMAC-SHA1" + + // PLAINTEXT signature method is not recommended to be used in + // production environment. + PLAINTEXT SignatureMethod = "PLAINTEXT" + + // OAuth1TokenContentType is a supported content type for an OAuth1 + // token. + OAuth1TokenContentType = "application/x-www-form-urlencoded" +) + +// AuthOptions represents options for authenticating a user using OAuth1 tokens. +type AuthOptions struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthToken is the OAuth1 Request Token. + OAuthToken string `q:"oauth_token" required:"true"` + + // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate + // an OAuth1 request signature. + OAuthTokenSecret string `required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` + + // AllowReauth allows Gophercloud to re-authenticate automatically + // if/when your token expires. + AllowReauth bool +} + +// ToTokenV3HeadersMap builds the headers required for an OAuth1-based create +// request. +func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]any) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} + + method := headerOpts["method"].(string) + u := headerOpts["url"].(string) + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + + authHeader := buildAuthHeader(q.Query(), signature) + + headers := map[string]string{ + "Authorization": authHeader, + "X-Auth-Token": "", + } + + return headers, nil +} + +// ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder +// interface. +func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { + return nil, nil +} + +// CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder +// interface. +func (opts AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +// ToTokenV3CreateMap builds a create request body. +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 { + Methods []string `json:"methods"` + OAuth1 struct{} `json:"oauth1"` + } + + // authReq defines the "auth" portion of an OAuth1-based authentication + // create request body. + type authReq struct { + Identity identityReq `json:"identity"` + } + + // oauth1Request defines how an OAuth1-based authentication create + // request body looks. + type oauth1Request struct { + Auth authReq `json:"auth"` + } + + var req oauth1Request + + req.Auth.Identity.Methods = []string{"oauth1"} + return gophercloud.BuildRequestBody(req, "") +} + +// 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]any{ + "method": "POST", + "url": authURL(client), + } + + h, err := opts.ToTokenV3HeadersMap(headerOpts) + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, authURL(client), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateConsumerOptsBuilder allows extensions to add additional parameters to +// the CreateConsumer request. +type CreateConsumerOptsBuilder interface { + ToOAuth1CreateConsumerMap() (map[string]any, error) +} + +// CreateConsumerOpts provides options used to create a new Consumer. +type CreateConsumerOpts struct { + // Description is the consumer description. + Description string `json:"description"` +} + +// ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request. +func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "consumer") +} + +// 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(ctx, consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// 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 +} + +// List enumerates Consumers. +func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page { + return ConsumersPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetConsumer retrieves details on a single Consumer by ID. +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 +} + +// UpdateConsumerOpts provides options used to update a consumer. +type UpdateConsumerOpts struct { + // Description is the consumer description. + Description string `json:"description"` +} + +// ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update +// request. +func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "consumer") +} + +// UpdateConsumer updates an existing Consumer. +func UpdateConsumer(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { + b, err := opts.ToOAuth1UpdateConsumerMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(ctx, consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RequestTokenOptsBuilder allows extensions to add additional parameters to the +// RequestToken request. +type RequestTokenOptsBuilder interface { + ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error) +} + +// RequestTokenOpts provides options used to get a consumer unauthorized +// request token. +type RequestTokenOpts struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` + + // RequestedProjectID is a Project ID a consumer user requested an + // access to. + RequestedProjectID string `h:"Requested-Project-Id"` +} + +// ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request +// headers. +func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob") + if err != nil { + return nil, err + } + + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret} + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + authHeader := buildAuthHeader(q.Query(), signature) + + h["Authorization"] = authHeader + + return h, nil +} + +// RequestToken requests an unauthorized OAuth1 Token. +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(ctx, requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + 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 != OAuth1TokenContentType { + r.Err = fmt.Errorf("unsupported Content-Type: %q", v) + return + } + 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]any, error) +} + +// AuthorizeTokenOpts provides options used to authorize a request token. +type AuthorizeTokenOpts struct { + Roles []Role `json:"roles"` +} + +// Role is a struct representing a role object in a AuthorizeTokenOpts struct. +type Role struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +// ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token +// request. +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") + } + } + return gophercloud.BuildRequestBody(opts, "") +} + +// AuthorizeToken authorizes an unauthorized consumer token. +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(ctx, authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateAccessTokenOptsBuilder allows extensions to add additional parameters +// to the CreateAccessToken request. +type CreateAccessTokenOptsBuilder interface { + ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error) +} + +// CreateAccessTokenOpts provides options used to create an OAuth1 token. +type CreateAccessTokenOpts struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthToken is the OAuth1 Request Token. + OAuthToken string `q:"oauth_token" required:"true"` + + // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate + // an OAuth1 request signature. + OAuthTokenSecret string `required:"true"` + + // OAuthVerifier is the OAuth1 verification code. + OAuthVerifier string `q:"oauth_verifier" required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` +} + +// ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of +// request headers. +func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + authHeader := buildAuthHeader(q.Query(), signature) + + headers := map[string]string{ + "Authorization": authHeader, + } + + return headers, nil +} + +// CreateAccessToken creates a new OAuth1 Access Token +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(ctx, createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + 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 != OAuth1TokenContentType { + r.Err = fmt.Errorf("unsupported Content-Type: %q", v) + return + } + r.Body, r.Err = io.ReadAll(resp.Body) + return +} + +// GetAccessToken retrieves details on a single OAuth1 access token by an ID. +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(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 +} + +// ListAccessTokens enumerates authorized access tokens. +func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := userAccessTokensURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ListAccessTokenRoles enumerates authorized access token roles. +func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager { + url := userAccessTokenRolesURL(client, userID, id) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetAccessTokenRole retrieves details on a single OAuth1 access token role by +// an ID. +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 +} + +// The following are small helper functions used to help build the signature. + +// buildOAuth1QueryString builds a URLEncoded parameters string specific for +// OAuth1-based requests. +func buildOAuth1QueryString(opts any, timestamp *time.Time, callback string) (*url.URL, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return nil, err + } + + query := q.Query() + + if timestamp != nil { + // use provided timestamp + query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10)) + } else { + // use current timestamp + query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10)) + } + + if query.Get("oauth_nonce") == "" { + // when nonce is not set, generate a random one + query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp")) + } + + if callback != "" { + query.Set("oauth_callback", callback) + } + query.Set("oauth_version", "1.0") + + return &url.URL{RawQuery: query.Encode()}, nil +} + +// buildStringToSign builds a string to be signed. +func buildStringToSign(method string, u string, query url.Values) []byte { + parsedURL, _ := url.Parse(u) + p := parsedURL.Port() + s := parsedURL.Scheme + + // Default scheme port must be stripped + if s == "http" && p == "80" || s == "https" && p == "443" { + parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p) + } + + // Ensure that URL doesn't contain queries + parsedURL.RawQuery = "" + + v := strings.Join( + []string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&") + + return []byte(v) +} + +// signString signs a string using an OAuth1 signature method. +func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string { + var key []byte + for i, k := range signatureKeys { + key = append(key, []byte(url.QueryEscape(k))...) + if i == 0 { + key = append(key, '&') + } + } + + var signedString string + switch signatureMethod { + case PLAINTEXT: + signedString = string(key) + default: + h := hmac.New(sha1.New, key) + h.Write(strToSign) + signedString = base64.StdEncoding.EncodeToString(h.Sum(nil)) + } + + return signedString +} + +// buildAuthHeader generates an OAuth1 Authorization header with a signature +// calculated using an OAuth1 signature method. +func buildAuthHeader(query url.Values, signature string) string { + var authHeader []string + var keys []string + for k := range query { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + for _, v := range query[k] { + authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v))) + } + } + + authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature)) + + return "OAuth " + strings.Join(authHeader, ", ") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go new file mode 100644 index 000000000..2ed75bd74 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/results.go @@ -0,0 +1,317 @@ +package oauth1 + +import ( + "encoding/json" + "net/url" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Consumer represents a delegated authorization request between two +// identities. +type Consumer struct { + ID string `json:"id"` + Secret string `json:"secret"` + Description string `json:"description"` +} + +type consumerResult struct { + gophercloud.Result +} + +// CreateConsumerResult is the response from a Create operation. Call its +// Extract method to interpret it as a Consumer. +type CreateConsumerResult struct { + consumerResult +} + +// UpdateConsumerResult is the response from a Create operation. Call its +// Extract method to interpret it as a Consumer. +type UpdateConsumerResult struct { + consumerResult +} + +// DeleteConsumerResult is the response from a Delete operation. Call its +// ExtractErr to determine if the request succeeded or failed. +type DeleteConsumerResult struct { + gophercloud.ErrResult +} + +// ConsumersPage is a single page of Region results. +type ConsumersPage struct { + pagination.LinkedPageBase +} + +// GetConsumerResult is the response from a Get operation. Call its Extract +// method to interpret it as a Consumer. +type GetConsumerResult struct { + consumerResult +} + +// IsEmpty determines whether or not a page of Consumers contains any results. +func (c ConsumersPage) IsEmpty() (bool, error) { + if c.StatusCode == 204 { + return true, nil + } + + consumers, err := ExtractConsumers(c) + return len(consumers) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (c ConsumersPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := c.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractConsumers returns a slice of Consumers contained in a single page of +// results. +func ExtractConsumers(r pagination.Page) ([]Consumer, error) { + var s struct { + Consumers []Consumer `json:"consumers"` + } + err := (r.(ConsumersPage)).ExtractInto(&s) + return s.Consumers, err +} + +// Extract interprets any consumer result as a Consumer. +func (c consumerResult) Extract() (*Consumer, error) { + var s struct { + Consumer *Consumer `json:"consumer"` + } + err := c.ExtractInto(&s) + return s.Consumer, err +} + +// Token contains an OAuth1 token. +type Token struct { + // OAuthToken is the key value for the oauth token that the Identity API returns. + OAuthToken string `q:"oauth_token"` + // OAuthTokenSecret is the secret value associated with the OAuth Token. + OAuthTokenSecret string `q:"oauth_token_secret"` + // OAuthExpiresAt is the date and time when an OAuth token expires. + OAuthExpiresAt *time.Time `q:"-"` +} + +// TokenResult is a struct to handle +// "Content-Type: application/x-www-form-urlencoded" response. +type TokenResult struct { + gophercloud.Result + Body []byte +} + +// Extract interprets any OAuth1 token result as a Token. +func (r TokenResult) Extract() (*Token, error) { + if r.Err != nil { + return nil, r.Err + } + + values, err := url.ParseQuery(string(r.Body)) + if err != nil { + return nil, err + } + + token := &Token{ + OAuthToken: values.Get("oauth_token"), + OAuthTokenSecret: values.Get("oauth_token_secret"), + } + + if v := values.Get("oauth_expires_at"); v != "" { + if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil { + return nil, err + } else { + token.OAuthExpiresAt = &t + } + } + + return token, nil +} + +// AuthorizedToken contains an OAuth1 authorized token info. +type AuthorizedToken struct { + // OAuthVerifier is the ID of the token verifier. + OAuthVerifier string `json:"oauth_verifier"` +} + +type AuthorizeTokenResult struct { + gophercloud.Result +} + +// Extract interprets AuthorizeTokenResult result as a AuthorizedToken. +func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) { + var s struct { + AuthorizedToken *AuthorizedToken `json:"token"` + } + err := r.ExtractInto(&s) + return s.AuthorizedToken, err +} + +// AccessToken represents an AccessToken response as a struct. +type AccessToken struct { + ID string `json:"id"` + ConsumerID string `json:"consumer_id"` + ProjectID string `json:"project_id"` + AuthorizingUserID string `json:"authorizing_user_id"` + ExpiresAt *time.Time `json:"-"` +} + +func (r *AccessToken) UnmarshalJSON(b []byte) error { + type tmp AccessToken + var s struct { + tmp + ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = AccessToken(s.tmp) + + if s.ExpiresAt != nil { + t := time.Time(*s.ExpiresAt) + r.ExpiresAt = &t + } + + return nil +} + +type GetAccessTokenResult struct { + gophercloud.Result +} + +// Extract interprets any GetAccessTokenResult result as an AccessToken. +func (r GetAccessTokenResult) Extract() (*AccessToken, error) { + var s struct { + AccessToken *AccessToken `json:"access_token"` + } + err := r.ExtractInto(&s) + return s.AccessToken, err +} + +// RevokeAccessTokenResult is the response from a Delete operation. Call its +// ExtractErr to determine if the request succeeded or failed. +type RevokeAccessTokenResult struct { + gophercloud.ErrResult +} + +// AccessTokensPage is a single page of Access Tokens results. +type AccessTokensPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessTokensPage contains any results. +func (r AccessTokensPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + accessTokens, err := ExtractAccessTokens(r) + return len(accessTokens) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessTokensPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractAccessTokens returns a slice of AccessTokens contained in a single +// page of results. +func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) { + var s struct { + AccessTokens []AccessToken `json:"access_tokens"` + } + err := (r.(AccessTokensPage)).ExtractInto(&s) + return s.AccessTokens, err +} + +// AccessTokenRole represents an Access Token Role struct. +type AccessTokenRole struct { + ID string `json:"id"` + Name string `json:"name"` + DomainID string `json:"domain_id"` +} + +// AccessTokenRolesPage is a single page of Access Token roles results. +type AccessTokenRolesPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessTokensPage contains any results. +func (r AccessTokenRolesPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + accessTokenRoles, err := ExtractAccessTokenRoles(r) + return len(accessTokenRoles) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessTokenRolesPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a +// single page of results. +func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) { + var s struct { + AccessTokenRoles []AccessTokenRole `json:"roles"` + } + err := (r.(AccessTokenRolesPage)).ExtractInto(&s) + return s.AccessTokenRoles, err +} + +type GetAccessTokenRoleResult struct { + gophercloud.Result +} + +// Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole. +func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) { + var s struct { + AccessTokenRole *AccessTokenRole `json:"role"` + } + err := r.ExtractInto(&s) + return s.AccessTokenRole, err +} + +// OAuth1 is an OAuth1 object, returned in OAuth1 token result. +type OAuth1 struct { + AccessTokenID string `json:"access_token_id"` + ConsumerID string `json:"consumer_id"` +} + +// TokenExt represents an extension of the base token result. +type TokenExt struct { + OAuth1 OAuth1 `json:"OS-OAUTH1"` +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go new file mode 100644 index 000000000..c8dc02e5d --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1/urls.go @@ -0,0 +1,43 @@ +package oauth1 + +import "github.com/gophercloud/gophercloud/v2" + +func consumersURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "consumers") +} + +func consumerURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("OS-OAUTH1", "consumers", id) +} + +func requestTokenURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "request_token") +} + +func authorizeTokenURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("OS-OAUTH1", "authorize", id) +} + +func createAccessTokenURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "access_token") +} + +func userAccessTokensURL(c *gophercloud.ServiceClient, userID string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens") +} + +func userAccessTokenURL(c *gophercloud.ServiceClient, userID string, id string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id) +} + +func userAccessTokenRolesURL(c *gophercloud.ServiceClient, userID string, id string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles") +} + +func userAccessTokenRoleURL(c *gophercloud.ServiceClient, userID string, id string, roleID string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles", roleID) +} + +func authURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("auth", "tokens") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/BUILD.bazel new file mode 100644 index 000000000..25b921049 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tokens", + srcs = [ + "doc.go", + "requests.go", + "results.go", + "urls.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens", + visibility = ["//visibility:public"], + deps = ["//vendor/github.com/gophercloud/gophercloud/v2:gophercloud"], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go new file mode 100644 index 000000000..c711d0c28 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/doc.go @@ -0,0 +1,107 @@ +/* +Package tokens provides information and interaction with the token API +resource for the OpenStack Identity service. + +For more information, see: +http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3 + +Example to Create a Token From a Username and Password + + authOptions := tokens.AuthOptions{ + UserID: "username", + Password: "password", + } + + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token From a Username, Password, and Domain + + authOptions := tokens.AuthOptions{ + UserID: "username", + Password: "password", + DomainID: "default", + } + + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + + authOptions = tokens.AuthOptions{ + UserID: "username", + Password: "password", + DomainName: "default", + } + + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token From a Token + + authOptions := tokens.AuthOptions{ + TokenID: "token_id", + } + + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token from a Username and Password with Project ID Scope + + scope := tokens.Scope{ + ProjectID: "0fe36e73809d46aeae6705c39077b1b3", + } + + authOptions := tokens.AuthOptions{ + Scope: &scope, + UserID: "username", + Password: "password", + } + + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token from a Username and Password with Domain ID Scope + + scope := tokens.Scope{ + DomainID: "default", + } + + authOptions := tokens.AuthOptions{ + Scope: &scope, + UserID: "username", + Password: "password", + } + + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to Create a Token from a Username and Password with Project Name Scope + + scope := tokens.Scope{ + ProjectName: "project_name", + DomainID: "default", + } + + authOptions := tokens.AuthOptions{ + Scope: &scope, + UserID: "username", + Password: "password", + } + + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } +*/ +package tokens diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go new file mode 100644 index 000000000..fa8b925d0 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/requests.go @@ -0,0 +1,179 @@ +package tokens + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// Scope allows a created token to be limited to a specific domain or project. +type Scope struct { + ProjectID string + ProjectName string + DomainID string + DomainName string + System bool + TrustID string +} + +// AuthOptionsBuilder provides the ability for extensions to add additional +// parameters to AuthOptions. Extensions must satisfy all required methods. +type AuthOptionsBuilder interface { + // ToTokenV3CreateMap assembles the Create request body, returning an error + // if parameters are missing or inconsistent. + ToTokenV3CreateMap(map[string]any) (map[string]any, error) + ToTokenV3HeadersMap(map[string]any) (map[string]string, error) + ToTokenV3ScopeMap() (map[string]any, error) + CanReauth() bool +} + +// AuthOptions represents options for authenticating a user. +type AuthOptions struct { + // IdentityEndpoint specifies the HTTP endpoint that is required to work with + // the Identity API of the appropriate version. While it's ultimately needed + // by all of the identity services, it will often be populated by a + // provider-level function. + IdentityEndpoint string `json:"-"` + + // Username is required if using Identity V2 API. Consult with your provider's + // control panel to discover your account's username. In Identity V3, either + // UserID or a combination of Username and DomainID or DomainName are needed. + Username string `json:"username,omitempty"` + UserID string `json:"id,omitempty"` + + Password string `json:"password,omitempty"` + + // Passcode is used in TOTP authentication method + Passcode string `json:"passcode,omitempty"` + + // At most one of DomainID and DomainName must be provided if using Username + // with Identity V3. Otherwise, either are optional. + DomainID string `json:"-"` + DomainName string `json:"name,omitempty"` + + // AllowReauth should be set to true if you grant permission for Gophercloud + // to cache your credentials in memory, and to allow Gophercloud to attempt + // to re-authenticate automatically if/when your token expires. If you set + // it to false, it will not cache these settings, but re-authentication will + // not be possible. This setting defaults to false. + AllowReauth bool `json:"-"` + + // TokenID allows users to authenticate (possibly as another user) with an + // authentication token ID. + TokenID string `json:"-"` + + // Authentication through Application Credentials requires supplying name, project and secret + // For project we can use TenantID + ApplicationCredentialID string `json:"-"` + ApplicationCredentialName string `json:"-"` + ApplicationCredentialSecret string `json:"-"` + + Scope Scope `json:"-"` +} + +// ToTokenV3CreateMap builds a request body from AuthOptions. +func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]any) (map[string]any, error) { + gophercloudAuthOpts := gophercloud.AuthOptions{ + Username: opts.Username, + UserID: opts.UserID, + Password: opts.Password, + Passcode: opts.Passcode, + DomainID: opts.DomainID, + DomainName: opts.DomainName, + AllowReauth: opts.AllowReauth, + TokenID: opts.TokenID, + ApplicationCredentialID: opts.ApplicationCredentialID, + ApplicationCredentialName: opts.ApplicationCredentialName, + ApplicationCredentialSecret: opts.ApplicationCredentialSecret, + } + + return gophercloudAuthOpts.ToTokenV3CreateMap(scope) +} + +// ToTokenV3ScopeMap builds a scope request body from AuthOptions. +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { + scope := gophercloud.AuthScope(opts.Scope) + + gophercloudAuthOpts := gophercloud.AuthOptions{ + Scope: &scope, + DomainID: opts.DomainID, + DomainName: opts.DomainName, + } + + return gophercloudAuthOpts.ToTokenV3ScopeMap() +} + +func (opts *AuthOptions) CanReauth() bool { + if opts.Passcode != "" { + // cannot reauth using TOTP passcode + return false + } + + return opts.AllowReauth +} + +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { + return nil, nil +} + +func subjectTokenHeaders(subjectToken string) map[string]string { + return map[string]string{ + "X-Subject-Token": subjectToken, + } +} + +// Create authenticates and either generates a new token, or changes the Scope +// of an existing token. +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { + scope, err := opts.ToTokenV3ScopeMap() + if err != nil { + r.Err = err + return + } + + b, err := opts.ToTokenV3CreateMap(scope) + if err != nil { + r.Err = err + return + } + + 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) + return +} + +// Get validates and retrieves information about another token. +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}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Validate determines if a specified token is valid or not. +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}, + }) + if err != nil { + return false, err + } + + return resp.StatusCode == 200 || resp.StatusCode == 204, nil +} + +// Revoke immediately makes specified token invalid. +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) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go new file mode 100644 index 000000000..bd61a9a67 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/results.go @@ -0,0 +1,215 @@ +package tokens + +import ( + "time" + + "github.com/gophercloud/gophercloud/v2" +) + +// Endpoint represents a single API endpoint offered by a service. +// It matches either a public, internal or admin URL. +// If supported, it contains a region specifier, again if provided. +// The significance of the Region field will depend upon your provider. +type Endpoint struct { + ID string `json:"id"` + Region string `json:"region"` + RegionID string `json:"region_id"` + Interface string `json:"interface"` + URL string `json:"url"` +} + +// CatalogEntry provides a type-safe interface to an Identity API V3 service +// catalog listing. Each class of service, such as cloud DNS or block storage +// services, could have multiple CatalogEntry representing it (one by interface +// type, e.g public, admin or internal). +// +// Note: when looking for the desired service, try, whenever possible, to key +// off the type field. Otherwise, you'll tie the representation of the service +// to a specific provider. +type CatalogEntry struct { + // Service ID + ID string `json:"id"` + + // Name will contain the provider-specified name for the service. + Name string `json:"name"` + + // Type will contain a type string if OpenStack defines a type for the + // service. Otherwise, for provider-specific services, the provider may + // assign their own type strings. + Type string `json:"type"` + + // Endpoints will let the caller iterate over all the different endpoints that + // may exist for the service. + Endpoints []Endpoint `json:"endpoints"` +} + +// ServiceCatalog provides a view into the service catalog from a previous, +// successful authentication. +type ServiceCatalog struct { + Entries []CatalogEntry `json:"catalog"` +} + +// Domain provides information about the domain to which this token grants +// access. +type Domain struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// User represents a user resource that exists in the Identity Service. +type User struct { + Domain Domain `json:"domain"` + ID string `json:"id"` + Name string `json:"name"` +} + +// Role provides information about roles to which User is authorized. +type Role struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// Project provides information about project to which User is authorized. +type Project struct { + Domain Domain `json:"domain"` + ID string `json:"id"` + 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 { + gophercloud.Result +} + +// Extract is a shortcut for ExtractToken. +// This function is deprecated and still present for backward compatibility. +func (r commonResult) Extract() (*Token, error) { + return r.ExtractToken() +} + +// ExtractToken interprets a commonResult as a Token. +func (r commonResult) ExtractToken() (*Token, error) { + var s Token + err := r.ExtractInto(&s) + if err != nil { + return nil, err + } + + // Parse the token itself from the stored headers. + s.ID = r.Header.Get("X-Subject-Token") + + return &s, err +} + +// ExtractTokenID implements the gophercloud.AuthResult interface. The returned +// string is the same as the ID field of the Token struct returned from +// ExtractToken(). +func (r CreateResult) ExtractTokenID() (string, error) { + return r.Header.Get("X-Subject-Token"), r.Err +} + +// ExtractTokenID implements the gophercloud.AuthResult interface. The returned +// string is the same as the ID field of the Token struct returned from +// ExtractToken(). +func (r GetResult) ExtractTokenID() (string, error) { + return r.Header.Get("X-Subject-Token"), r.Err +} + +// ExtractServiceCatalog returns the ServiceCatalog that was generated along +// with the user's Token. +func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { + var s ServiceCatalog + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractUser returns the User that is the owner of the Token. +func (r commonResult) ExtractUser() (*User, error) { + var s struct { + User *User `json:"user"` + } + err := r.ExtractInto(&s) + return s.User, err +} + +// ExtractRoles returns Roles to which User is authorized. +func (r commonResult) ExtractRoles() ([]Role, error) { + var s struct { + Roles []Role `json:"roles"` + } + err := r.ExtractInto(&s) + return s.Roles, err +} + +// ExtractProject returns Project to which User is authorized. +func (r commonResult) ExtractProject() (*Project, error) { + var s struct { + Project *Project `json:"project"` + } + err := r.ExtractInto(&s) + return s.Project, err +} + +// ExtractDomain returns Domain to which User is authorized. +func (r commonResult) ExtractDomain() (*Domain, error) { + var s struct { + Domain *Domain `json:"domain"` + } + err := r.ExtractInto(&s) + 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. +type CreateResult struct { + commonResult +} + +// GetResult is the response from a Get request. Use ExtractToken() +// to interpret it as a Token, or ExtractServiceCatalog() to interpret it +// as a service catalog. +type GetResult struct { + commonResult +} + +// RevokeResult is response from a Revoke request. +type RevokeResult struct { + commonResult +} + +// Token is a string that grants a user access to a controlled set of services +// in an OpenStack provider. Each Token is valid for a set length of time. +type Token struct { + // ID is the issued token. + ID string `json:"id"` + + // ExpiresAt is the timestamp at which this token will no longer be accepted. + ExpiresAt time.Time `json:"expires_at"` +} + +func (r commonResult) ExtractInto(v any) error { + return r.ExtractIntoStructPtr(v, "token") +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go new file mode 100644 index 000000000..2218c107f --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/urls.go @@ -0,0 +1,7 @@ +package tokens + +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/v2/openstack/utils/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/BUILD.bazel new file mode 100644 index 000000000..d4acf2858 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "utils", + srcs = [ + "base_endpoint.go", + "choose_version.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils", + importpath = "github.com/gophercloud/gophercloud/v2/openstack/utils", + visibility = ["//visibility:public"], + deps = ["//vendor/github.com/gophercloud/gophercloud/v2:gophercloud"], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go new file mode 100644 index 000000000..40080f7af --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/base_endpoint.go @@ -0,0 +1,28 @@ +package utils + +import ( + "net/url" + "regexp" + "strings" +) + +// BaseEndpoint will return a URL without the /vX.Y +// portion of the URL. +func BaseEndpoint(endpoint string) (string, error) { + u, err := url.Parse(endpoint) + if err != nil { + return "", err + } + + u.RawQuery, u.Fragment = "", "" + + path := u.Path + versionRe := regexp.MustCompile("v[0-9.]+/?") + + if version := versionRe.FindString(path); version != "" { + versionIndex := strings.Index(path, version) + u.Path = path[:versionIndex] + } + + return u.String(), nil +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go new file mode 100644 index 000000000..6c720e57e --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/openstack/utils/choose_version.go @@ -0,0 +1,236 @@ +package utils + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/gophercloud/gophercloud/v2" +) + +// Version is a supported API version, corresponding to a vN package within the appropriate service. +type Version struct { + ID string + Suffix string + Priority int +} + +var goodStatus = map[string]bool{ + "current": true, + "supported": true, + "stable": true, +} + +// 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) { + type linkResp struct { + Href string `json:"href"` + Rel string `json:"rel"` + } + + type valueResp struct { + ID string `json:"id"` + Status string `json:"status"` + Links []linkResp `json:"links"` + } + + type versionsResp struct { + Values []valueResp `json:"values"` + } + + type response struct { + Versions versionsResp `json:"versions"` + } + + normalize := func(endpoint string) string { + if !strings.HasSuffix(endpoint, "/") { + return endpoint + "/" + } + return endpoint + } + identityEndpoint := normalize(client.IdentityEndpoint) + + // If a full endpoint is specified, check version suffixes for a match first. + for _, v := range recognized { + if strings.HasSuffix(identityEndpoint, v.Suffix) { + return v, identityEndpoint, nil + } + } + + var resp response + _, err := client.Request(ctx, "GET", client.IdentityBase, &gophercloud.RequestOpts{ + JSONResponse: &resp, + OkCodes: []int{200, 300}, + }) + + if err != nil { + return nil, "", err + } + + var highest *Version + var endpoint string + + for _, value := range resp.Versions.Values { + href := "" + for _, link := range value.Links { + if link.Rel == "self" { + href = normalize(link.Href) + } + } + + for _, version := range recognized { + if strings.Contains(value.ID, version.ID) { + // 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 version, href, nil + } + + // Otherwise, find the highest-priority version with a whitelisted status. + if goodStatus[strings.ToLower(value.Status)] { + if highest == nil || version.Priority > highest.Priority { + highest = version + endpoint = href + } + } + } + } + } + + if highest == nil { + 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 highest, endpoint, nil +} + +type SupportedMicroversions struct { + MaxMajor int + MaxMinor int + MinMajor int + MinMinor int +} + +// GetSupportedMicroversions returns the minimum and maximum microversion that is supported by the ServiceClient Endpoint. +func GetSupportedMicroversions(ctx context.Context, client *gophercloud.ServiceClient) (SupportedMicroversions, error) { + type valueResp struct { + ID string `json:"id"` + Status string `json:"status"` + Version string `json:"version"` + MinVersion string `json:"min_version"` + } + + type response struct { + Version valueResp `json:"version"` + Versions []valueResp `json:"versions"` + } + var minVersion, maxVersion string + var supportedMicroversions SupportedMicroversions + var resp response + _, err := client.Get(ctx, client.Endpoint, &resp, &gophercloud.RequestOpts{ + OkCodes: []int{200, 300}, + }) + + if err != nil { + return supportedMicroversions, err + } + + if len(resp.Versions) > 0 { + // We are dealing with an unversioned endpoint + // We only handle the case when there is exactly one, and assume it is the correct one + if len(resp.Versions) > 1 { + return supportedMicroversions, fmt.Errorf("unversioned endpoint with multiple alternatives not supported") + } + minVersion = resp.Versions[0].MinVersion + maxVersion = resp.Versions[0].Version + } else { + minVersion = resp.Version.MinVersion + maxVersion = resp.Version.Version + } + + // Return early if the endpoint does not support microversions + if minVersion == "" && maxVersion == "" { + return supportedMicroversions, fmt.Errorf("microversions not supported by ServiceClient Endpoint") + } + + supportedMicroversions.MinMajor, supportedMicroversions.MinMinor, err = ParseMicroversion(minVersion) + if err != nil { + return supportedMicroversions, err + } + + supportedMicroversions.MaxMajor, supportedMicroversions.MaxMinor, err = ParseMicroversion(maxVersion) + if err != nil { + return supportedMicroversions, err + } + + return supportedMicroversions, nil +} + +// 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 +} + +// 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 +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/pagination/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/v2/pagination/BUILD.bazel new file mode 100644 index 000000000..ebf9af237 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "pagination", + srcs = [ + "http.go", + "linked.go", + "marker.go", + "pager.go", + "pkg.go", + "single.go", + ], + importmap = "sigs.k8s.io/etcd-manager/vendor/github.com/gophercloud/gophercloud/v2/pagination", + importpath = "github.com/gophercloud/gophercloud/v2/pagination", + visibility = ["//visibility:public"], + deps = ["//vendor/github.com/gophercloud/gophercloud/v2:gophercloud"], +) diff --git a/vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go new file mode 100644 index 000000000..cf188b89b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/http.go @@ -0,0 +1,63 @@ +package pagination + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/url" + "strings" + + "github.com/gophercloud/gophercloud/v2" +) + +// PageResult stores the HTTP response that returned the current page of results. +type PageResult struct { + gophercloud.Result + url.URL +} + +// 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 any + + defer resp.Body.Close() + rawBody, err := io.ReadAll(resp.Body) + if err != nil { + return PageResult{}, err + } + + if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") { + err = json.Unmarshal(rawBody, &parsedBody) + if err != nil { + return PageResult{}, err + } + } else { + parsedBody = rawBody + } + + return PageResultFromParsed(resp, parsedBody), err +} + +// 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 any) PageResult { + return PageResult{ + Result: gophercloud.Result{ + Body: body, + StatusCode: resp.StatusCode, + Header: resp.Header, + }, + URL: *resp.Request.URL, + } +} + +// Request performs an HTTP request and extracts the http.Response from the result. +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/v2/pagination/linked.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/linked.go new file mode 100644 index 000000000..7e4de4f7a --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/linked.go @@ -0,0 +1,92 @@ +package pagination + +import ( + "fmt" + "reflect" + + "github.com/gophercloud/gophercloud/v2" +) + +// LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. +type LinkedPageBase struct { + PageResult + + // LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer. + // If any link along the path is missing, an empty URL will be returned. + // If any link results in an unexpected value type, an error will be returned. + // When left as "nil", []string{"links", "next"} will be used as a default. + LinkPath []string +} + +// NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present. +// It assumes that the links are available in a "links" element of the top-level response object. +// If this is not the case, override NextPageURL on your result type. +func (current LinkedPageBase) NextPageURL() (string, error) { + var path []string + var key string + + if current.LinkPath == nil { + path = []string{"links", "next"} + } else { + path = current.LinkPath + } + + submap, ok := current.Body.(map[string]any) + if !ok { + err := gophercloud.ErrUnexpectedType{} + err.Expected = "map[string]any" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) + return "", err + } + + for { + key, path = path[0], path[1:] + + value, ok := submap[key] + if !ok { + return "", nil + } + + if len(path) > 0 { + submap, ok = value.(map[string]any) + if !ok { + err := gophercloud.ErrUnexpectedType{} + err.Expected = "map[string]any" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) + return "", err + } + } else { + if value == nil { + // Actual null element. + return "", nil + } + + url, ok := value.(string) + if !ok { + err := gophercloud.ErrUnexpectedType{} + err.Expected = "string" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) + return "", err + } + + return url, nil + } + } +} + +// IsEmpty satisifies the IsEmpty method of the Page interface +func (current LinkedPageBase) IsEmpty() (bool, error) { + if b, ok := current.Body.([]any); ok { + return len(b) == 0, nil + } + err := gophercloud.ErrUnexpectedType{} + 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() any { + return current.Body +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go new file mode 100644 index 000000000..1d101fe2d --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/marker.go @@ -0,0 +1,58 @@ +package pagination + +import ( + "fmt" + "reflect" + + "github.com/gophercloud/gophercloud/v2" +) + +// MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. +// For convenience, embed the MarkedPageBase struct. +type MarkerPage interface { + Page + + // LastMarker returns the last "marker" value on this page. + LastMarker() (string, error) +} + +// MarkerPageBase is a page in a collection that's paginated by "limit" and "marker" query parameters. +type MarkerPageBase struct { + PageResult + + // Owner is a reference to the embedding struct. + Owner MarkerPage +} + +// NextPageURL generates the URL for the page of results after this one. +func (current MarkerPageBase) NextPageURL() (string, error) { + currentURL := current.URL + + mark, err := current.Owner.LastMarker() + if err != nil { + return "", err + } + + q := currentURL.Query() + q.Set("marker", mark) + currentURL.RawQuery = q.Encode() + + return currentURL.String(), nil +} + +// IsEmpty satisifies the IsEmpty method of the Page interface +func (current MarkerPageBase) IsEmpty() (bool, error) { + if b, ok := current.Body.([]any); ok { + return len(b) == 0, nil + } + err := gophercloud.ErrUnexpectedType{} + 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() any { + return current.Body +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go new file mode 100644 index 000000000..358101256 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pager.go @@ -0,0 +1,256 @@ +package pagination + +import ( + "context" + "errors" + "fmt" + "net/http" + "reflect" + "strings" + + "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.") +) + +// Page must be satisfied by the result type of any resource collection. +// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated. +// Generally, rather than implementing this interface directly, implementors should embed one of the concrete PageBase structs, +// instead. +// Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type +// will need to implement. +type Page interface { + // NextPageURL generates the URL for the page of data that follows this collection. + // Return "" if no such page exists. + NextPageURL() (string, error) + + // IsEmpty returns true if this Page has no items in it. + IsEmpty() (bool, error) + + // GetBody returns the Page Body. This is used in the `AllPages` method. + GetBody() any +} + +// Pager knows how to advance through a specific resource collection, one page at a time. +type Pager struct { + client *gophercloud.ServiceClient + + initialURL string + + createPage func(r PageResult) Page + + firstPage Page + + Err error + + // Headers supplies additional HTTP headers to populate on each paged request. + Headers map[string]string +} + +// NewPager constructs a manually-configured pager. +// Supply the URL for the first page, a function that requests a specific page given a URL, and a function that counts a page. +func NewPager(client *gophercloud.ServiceClient, initialURL string, createPage func(r PageResult) Page) Pager { + return Pager{ + client: client, + initialURL: initialURL, + createPage: createPage, + } +} + +// WithPageCreator returns a new Pager that substitutes a different page creation function. This is +// useful for overriding List functions in delegation. +func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { + return Pager{ + client: p.client, + initialURL: p.initialURL, + createPage: createPage, + } +} + +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 + } + + remembered, err := PageResultFrom(resp) + if err != nil { + return nil, err + } + + 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(ctx context.Context, handler func(context.Context, Page) (bool, error)) error { + if p.Err != nil { + return p.Err + } + currentURL := p.initialURL + for { + var currentPage Page + + // if first page has already been fetched, no need to fetch it again + if p.firstPage != nil { + currentPage = p.firstPage + p.firstPage = nil + } else { + var err error + currentPage, err = p.fetchNextPage(ctx, currentURL) + if err != nil { + return err + } + } + + empty, err := currentPage.IsEmpty() + if err != nil { + return err + } + if empty { + return nil + } + + ok, err := handler(ctx, currentPage) + if err != nil { + return err + } + if !ok { + return nil + } + + currentURL, err = currentPage.NextPageURL() + if err != nil { + return err + } + if currentURL == "" { + return nil + } + } +} + +// 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(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 []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(ctx, p.initialURL) + if err != nil { + return nil, err + } + // Store the page type so we can use reflection to create a new mega-page of + // that type. + pageType := reflect.TypeOf(firstPage) + + // if it's a single page, just return the firstPage (first page) + if _, found := pageType.FieldByName("SinglePageBase"); found { + return firstPage, nil + } + + // store the first page to avoid getting it twice + p.firstPage = firstPage + + // Switch on the page body type. Recognized types are `map[string]any`, + // `[]byte`, and `[]any`. + switch pb := firstPage.GetBody().(type) { + 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(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 []any (which is really []map[string]any) + switch vt := v.(type) { + case []any: + key = k + pagesSlice = append(pagesSlice, vt...) + } + } + } + return true, nil + }) + if err != nil { + return nil, err + } + // 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(ctx, func(_ context.Context, page Page) (bool, error) { + b := page.GetBody().([]byte) + pagesSlice = append(pagesSlice, b) + // seperate pages with a comma + pagesSlice = append(pagesSlice, []byte{10}) + return true, nil + }) + if err != nil { + return nil, err + } + if len(pagesSlice) > 0 { + // Remove the trailing comma. + pagesSlice = pagesSlice[:len(pagesSlice)-1] + } + var b []byte + // Combine the slice of slices in to a single slice. + for _, slice := range pagesSlice { + b = append(b, slice.([]byte)...) + } + // Set body to value of type `bytes`. + body = reflect.New(reflect.TypeOf(b)).Elem() + body.SetBytes(b) + case []any: + // Iterate over the pages to concatenate the bodies. + 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 `[]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]any/[]byte/[]any" + err.Actual = fmt.Sprintf("%T", pb) + return nil, err + } + + // Each `Extract*` function is expecting a specific type of page coming back, + // otherwise the type assertion in those functions will fail. pageType is needed + // to create a type in this method that has the same type that the `Extract*` + // function is expecting and set the Body of that object to the concatenated + // pages. + page := reflect.New(pageType) + // Set the page body to be the concatenated pages. + page.Elem().FieldByName("Body").Set(body) + // Set any additional headers that were pass along. The `objectstorage` pacakge, + // for example, passes a Content-Type header. + h := make(http.Header) + for k, v := range p.Headers { + h.Add(k, v) + } + page.Elem().FieldByName("Header").Set(reflect.ValueOf(h)) + // Type assert the page to a Page interface so that the type assertion in the + // `Extract*` methods will work. + return page.Elem().Interface().(Page), err +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/pagination/pkg.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pkg.go new file mode 100644 index 000000000..912daea36 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/pkg.go @@ -0,0 +1,4 @@ +/* +Package pagination contains utilities and convenience structs that implement common pagination idioms within OpenStack APIs. +*/ +package pagination diff --git a/vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go b/vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go new file mode 100644 index 000000000..416621121 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/pagination/single.go @@ -0,0 +1,33 @@ +package pagination + +import ( + "fmt" + "reflect" + + "github.com/gophercloud/gophercloud/v2" +) + +// SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. +type SinglePageBase PageResult + +// NextPageURL always returns "" to indicate that there are no more pages to return. +func (current SinglePageBase) NextPageURL() (string, error) { + return "", nil +} + +// IsEmpty satisifies the IsEmpty method of the Page interface +func (current SinglePageBase) IsEmpty() (bool, error) { + if b, ok := current.Body.([]any); ok { + return len(b) == 0, nil + } + err := gophercloud.ErrUnexpectedType{} + 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() any { + return current.Body +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/params.go b/vendor/github.com/gophercloud/gophercloud/v2/params.go new file mode 100644 index 000000000..09b322a6a --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/params.go @@ -0,0 +1,506 @@ +package gophercloud + +import ( + "encoding/json" + "fmt" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +/* +BuildRequestBody builds a map[string]interface from the given `struct`. If +parent is not an empty string, the final map[string]interface returned will +encapsulate the built one. For example: + + disk := 1 + createOpts := flavors.CreateOpts{ + ID: "1", + Name: "m1.tiny", + Disk: &disk, + RAM: 512, + VCPUs: 1, + RxTxFactor: 1.0, + } + + body, err := gophercloud.BuildRequestBody(createOpts, "flavor") + +The above example can be run as-is, however it is recommended to look at how +BuildRequestBody is used within Gophercloud to more fully understand how it +fits within the request process as a whole rather than use it directly as shown +above. +*/ +func BuildRequestBody(opts any, parent string) (map[string]any, error) { + optsValue := reflect.ValueOf(opts) + if optsValue.Kind() == reflect.Ptr { + optsValue = optsValue.Elem() + } + + optsType := reflect.TypeOf(opts) + if optsType.Kind() == reflect.Ptr { + optsType = optsType.Elem() + } + + optsMap := make(map[string]any) + if optsValue.Kind() == 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) + f := optsType.Field(i) + + if f.Name != strings.Title(f.Name) { + //fmt.Printf("Skipping field: %s...\n", f.Name) + continue + } + + //fmt.Printf("Starting on field: %s...\n", f.Name) + + zero := isZero(v) + //fmt.Printf("v is zero?: %v\n", zero) + + // if the field has a required tag that's set to "true" + if requiredTag := f.Tag.Get("required"); requiredTag == "true" { + //fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero) + // if the field's value is zero, return a missing-argument error + if zero { + // if the field has a 'required' tag, it can't have a zero-value + err := ErrMissingInput{} + err.Argument = f.Name + return nil, err + } + } + + if xorTag := f.Tag.Get("xor"); xorTag != "" { + //fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag) + xorField := optsValue.FieldByName(xorTag) + var xorFieldIsZero bool + if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) { + xorFieldIsZero = true + } else { + if xorField.Kind() == reflect.Ptr { + xorField = xorField.Elem() + } + xorFieldIsZero = isZero(xorField) + } + 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) + return nil, err + } + } + + if orTag := f.Tag.Get("or"); orTag != "" { + //fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag) + //fmt.Printf("field is zero?: %v\n", zero) + if zero { + orField := optsValue.FieldByName(orTag) + var orFieldIsZero bool + if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) { + orFieldIsZero = true + } else { + if orField.Kind() == reflect.Ptr { + orField = orField.Elem() + } + orFieldIsZero = isZero(orField) + } + if orFieldIsZero { + err := ErrMissingInput{} + err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag) + err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag) + return nil, err + } + } + } + + jsonTag := f.Tag.Get("json") + if jsonTag == "-" { + continue + } + + if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) { + sliceValue := v + if sliceValue.Kind() == reflect.Ptr { + sliceValue = sliceValue.Elem() + } + + for i := 0; i < sliceValue.Len(); i++ { + element := sliceValue.Index(i) + if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) { + _, err := BuildRequestBody(element.Interface(), "") + if err != nil { + return nil, err + } + } + } + } + if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { + if zero { + //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) + if jsonTag != "" { + jsonTagPieces := strings.Split(jsonTag, ",") + if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { + if v.CanSet() { + if !v.IsNil() { + if v.Kind() == reflect.Ptr { + v.Set(reflect.Zero(v.Type())) + } + } + //fmt.Printf("value after change: %+v\n", optsValue.Field(i)) + } + } + } + continue + } + + //fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name) + _, err := BuildRequestBody(v.Interface(), f.Name) + if err != nil { + return nil, err + } + } + } + + //fmt.Printf("opts: %+v \n", opts) + + b, err := json.Marshal(opts) + if err != nil { + return nil, err + } + + //fmt.Printf("string(b): %s\n", string(b)) + + err = json.Unmarshal(b, &optsMap) + if err != nil { + return nil, err + } + + //fmt.Printf("optsMap: %+v\n", optsMap) + + if parent != "" { + optsMap = map[string]any{parent: optsMap} + } + //fmt.Printf("optsMap after parent added: %+v\n", optsMap) + return optsMap, nil + } + // Return an error if the underlying type of 'opts' isn't a struct. + return nil, fmt.Errorf("Options type is not a struct.") +} + +// EnabledState is a convenience type, mostly used in Create and Update +// operations. Because the zero value of a bool is FALSE, we need to use a +// pointer instead to indicate zero-ness. +type EnabledState *bool + +// Convenience vars for EnabledState values. +var ( + iTrue = true + iFalse = false + + Enabled EnabledState = &iTrue + Disabled EnabledState = &iFalse +) + +// IPVersion is a type for the possible IP address versions. Valid instances +// are IPv4 and IPv6 +type IPVersion int + +const ( + // IPv4 is used for IP version 4 addresses + IPv4 IPVersion = 4 + // IPv6 is used for IP version 6 addresses + IPv6 IPVersion = 6 +) + +// IntToPointer is a function for converting integers into integer pointers. +// This is useful when passing in options to operations. +func IntToPointer(i int) *int { + return &i +} + +/* +MaybeString is an internal function to be used by request methods in individual +resource packages. + +It takes a string that might be a zero value and returns either a pointer to its +address or nil. This is useful for allowing users to conveniently omit values +from an options struct by leaving them zeroed, but still pass nil to the JSON +serializer so they'll be omitted from the request body. +*/ +func MaybeString(original string) *string { + if original != "" { + return &original + } + return nil +} + +/* +MaybeInt is an internal function to be used by request methods in individual +resource packages. + +Like MaybeString, it accepts an int that may or may not be a zero value, and +returns either a pointer to its address or nil. It's intended to hint that the +JSON serializer should omit its field. +*/ +func MaybeInt(original int) *int { + if original != 0 { + return &original + } + return nil +} + +/* +func isUnderlyingStructZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Ptr: + return isUnderlyingStructZero(v.Elem()) + default: + return isZero(v) + } +} +*/ + +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: + if v.IsNil() { + return true + } + return false + case reflect.Func, reflect.Map, reflect.Slice: + return v.IsNil() + case reflect.Array: + z := true + for i := 0; i < v.Len(); i++ { + z = z && isZero(v.Index(i)) + } + return z + case reflect.Struct: + if v.Type() == reflect.TypeOf(t) { + return v.Interface().(time.Time).IsZero() + } + z := true + for i := 0; i < v.NumField(); i++ { + z = z && isZero(v.Field(i)) + } + return z + } + // Compare other types directly: + z := reflect.Zero(v.Type()) + //fmt.Printf("zero type for value: %+v\n\n\n", z) + return v.Interface() == z.Interface() +} + +/* +BuildQueryString is an internal function to be used by request methods in +individual resource packages. + +It accepts a tagged structure and expands it into a URL struct. Field names are +converted into query parameters based on a "q" tag. For example: + + type struct Something { + Bar string `q:"x_bar"` + Baz int `q:"lorem_ipsum"` + } + + instance := Something{ + Bar: "AAA", + Baz: "BBB", + } + +will be converted into "?x_bar=AAA&lorem_ipsum=BBB". + +The struct's fields may be strings, integers, slices, or boolean values. Fields +left at their type's zero value will be omitted from the query. + +Slice are handled in one of two ways: + + type struct Something { + Bar []string `q:"bar"` // E.g. ?bar=1&bar=2 + Baz []int `q:"baz" format="comma-separated"` // E.g. ?baz=1,2 + } +*/ +func BuildQueryString(opts any) (*url.URL, error) { + optsValue := reflect.ValueOf(opts) + if optsValue.Kind() == reflect.Ptr { + optsValue = optsValue.Elem() + } + + optsType := reflect.TypeOf(opts) + if optsType.Kind() == reflect.Ptr { + optsType = optsType.Elem() + } + + params := url.Values{} + + if optsValue.Kind() == reflect.Struct { + for i := 0; i < optsValue.NumField(); i++ { + v := optsValue.Field(i) + f := optsType.Field(i) + qTag := f.Tag.Get("q") + + // if the field has a 'q' tag, it goes in the query string + if qTag != "" { + tags := strings.Split(qTag, ",") + + // if the field is set, add it to the slice of query pieces + if !isZero(v) { + loop: + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + goto loop + case reflect.String: + params.Add(tags[0], v.String()) + case reflect.Int: + params.Add(tags[0], strconv.FormatInt(v.Int(), 10)) + case reflect.Bool: + params.Add(tags[0], strconv.FormatBool(v.Bool())) + case reflect.Slice: + var values []string + switch v.Type().Elem() { + case reflect.TypeOf(0): + for i := 0; i < v.Len(); i++ { + values = append(values, strconv.FormatInt(v.Index(i).Int(), 10)) + } + default: + for i := 0; i < v.Len(); i++ { + values = append(values, v.Index(i).String()) + } + } + if sliceFormat := f.Tag.Get("format"); sliceFormat == "comma-separated" { + params.Add(tags[0], strings.Join(values, ",")) + } else { + params[tags[0]] = append(params[tags[0]], values...) + } + case reflect.Map: + if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String { + var s []string + for _, k := range v.MapKeys() { + value := v.MapIndex(k).String() + s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value)) + } + params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", "))) + } + } + } 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{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.") +} + +/* +BuildHeaders is an internal function to be used by request methods in +individual resource packages. + +It accepts an arbitrary tagged structure and produces a string map that's +suitable for use as the HTTP headers of an outgoing request. Field names are +mapped to header names based in "h" tags. + + type struct Something { + Bar string `h:"x_bar"` + Baz int `h:"lorem_ipsum"` + } + + instance := Something{ + Bar: "AAA", + Baz: "BBB", + } + +will be converted into: + + map[string]string{ + "x_bar": "AAA", + "lorem_ipsum": "BBB", + } + +Untagged fields and fields left at their zero values are skipped. Integers, +booleans and string values are supported. +*/ +func BuildHeaders(opts any) (map[string]string, error) { + optsValue := reflect.ValueOf(opts) + if optsValue.Kind() == reflect.Ptr { + optsValue = optsValue.Elem() + } + + optsType := reflect.TypeOf(opts) + if optsType.Kind() == reflect.Ptr { + optsType = optsType.Elem() + } + + optsMap := make(map[string]string) + if optsValue.Kind() == reflect.Struct { + for i := 0; i < optsValue.NumField(); i++ { + v := optsValue.Field(i) + f := optsType.Field(i) + hTag := f.Tag.Get("h") + + // if the field has a 'h' tag, it goes in the header + if hTag != "" { + tags := strings.Split(hTag, ",") + + // if the field is set, add it to the slice of query pieces + if !isZero(v) { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + switch v.Kind() { + case reflect.String: + optsMap[tags[0]] = v.String() + case reflect.Int: + optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) + case reflect.Int64: + optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) + case reflect.Bool: + optsMap[tags[0]] = strconv.FormatBool(v.Bool()) + } + } 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, nil + } + // Return an error if the underlying type of 'opts' isn't a struct. + return optsMap, fmt.Errorf("Options type is not a struct.") +} + +// IDSliceToQueryString takes a slice of elements and converts them into a query +// string. For example, if name=foo and slice=[]int{20, 40, 60}, then the +// result would be `?name=20&name=40&name=60' +func IDSliceToQueryString(name string, ids []int) string { + str := "" + for k, v := range ids { + if k == 0 { + str += "?" + } else { + str += "&" + } + str += fmt.Sprintf("%s=%s", name, strconv.Itoa(v)) + } + return str +} + +// IntWithinRange returns TRUE if an integer falls within a defined range, and +// FALSE if not. +func IntWithinRange(val, min, max int) bool { + return val > min && val < max +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/provider_client.go b/vendor/github.com/gophercloud/gophercloud/v2/provider_client.go new file mode 100644 index 000000000..007266f63 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/provider_client.go @@ -0,0 +1,583 @@ +package gophercloud + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + "sync" +) + +// DefaultUserAgent is the default User-Agent string set in the request header. +const ( + DefaultUserAgent = "gophercloud/v2.2.0" + DefaultMaxBackoffRetries = 60 +) + +// UserAgent represents a User-Agent header. +type UserAgent struct { + // prepend is the slice of User-Agent strings to prepend to DefaultUserAgent. + // All the strings to prepend are accumulated and prepended in the Join method. + prepend []string +} + +type RetryBackoffFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error + +// RetryFunc is a catch-all function for retrying failed API requests. +// If it returns nil, the request will be retried. If it returns an error, +// the request method will exit with that error. failCount is the number of +// times the request has failed (starting at 1). +type RetryFunc func(context context.Context, method, url string, options *RequestOpts, err error, failCount uint) error + +// Prepend prepends a user-defined string to the default User-Agent string. Users +// may pass in one or more strings to prepend. +func (ua *UserAgent) Prepend(s ...string) { + ua.prepend = append(s, ua.prepend...) +} + +// Join concatenates all the user-defined User-Agend strings with the default +// Gophercloud User-Agent string. +func (ua *UserAgent) Join() string { + uaSlice := append(ua.prepend, DefaultUserAgent) + return strings.Join(uaSlice, " ") +} + +// ProviderClient stores details that are required to interact with any +// services within a specific provider's API. +// +// Generally, you acquire a ProviderClient by calling the NewClient method in +// the appropriate provider's child package, providing whatever authentication +// credentials are required. +type ProviderClient struct { + // IdentityBase is the base URL used for a particular provider's identity + // service - it will be used when issuing authenticatation requests. It + // should point to the root resource of the identity service, not a specific + // identity version. + IdentityBase string + + // IdentityEndpoint is the identity endpoint. This may be a specific version + // of the identity service. If this is the case, this endpoint is used rather + // than querying versions first. + IdentityEndpoint string + + // TokenID is the ID of the most recently issued valid token. + // NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application. + // To safely read or write this value, call `Token` or `SetToken`, respectively + TokenID string + + // EndpointLocator describes how this provider discovers the endpoints for + // its constituent services. + EndpointLocator EndpointLocator + + // HTTPClient allows users to interject arbitrary http, https, or other transit behaviors. + HTTPClient http.Client + + // UserAgent represents the User-Agent header in the HTTP request. + UserAgent UserAgent + + // 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(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 + + // Retry backoff func is called when rate limited. + RetryBackoffFunc RetryBackoffFunc + + // MaxBackoffRetries set the maximum number of backoffs. When not set, defaults to DefaultMaxBackoffRetries + MaxBackoffRetries uint + + // A general failed request handler method - this is always called in the end if a request failed. Leave as nil + // to abort when an error is encountered. + RetryFunc RetryFunc + + // mut is a mutex for the client. It protects read and write access to client attributes such as getting + // and setting the TokenID. + mut *sync.RWMutex + + // reauthmut is a mutex for reauthentication it attempts to ensure that only one reauthentication + // attempt happens at one time. + reauthmut *reauthlock + + authResult AuthResult +} + +// reauthlock represents a set of attributes used to help in the reauthentication process. +type reauthlock struct { + sync.RWMutex + ongoing *reauthFuture +} + +// reauthFuture represents future result of the reauthentication process. +// while done channel is not closed, reauthentication is in progress. +// when done channel is closed, err contains the result of reauthentication. +type reauthFuture struct { + done chan struct{} + err error +} + +func newReauthFuture() *reauthFuture { + return &reauthFuture{ + make(chan struct{}), + nil, + } +} + +func (f *reauthFuture) Set(err error) { + f.err = err + close(f.done) +} + +func (f *reauthFuture) Get() error { + <-f.done + return f.err +} + +// AuthenticatedHeaders returns a map of HTTP headers that are common for all +// authenticated service requests. Blocks if Reauthenticate is in progress. +func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { + if client.IsThrowaway() { + return + } + if client.reauthmut != nil { + // If a Reauthenticate is in progress, wait for it to complete. + client.reauthmut.Lock() + ongoing := client.reauthmut.ongoing + client.reauthmut.Unlock() + if ongoing != nil { + _ = ongoing.Get() + } + } + t := client.Token() + if t == "" { + return + } + return map[string]string{"X-Auth-Token": t} +} + +// UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token. +// If the application's ProviderClient is not used concurrently, this doesn't need to be called. +func (client *ProviderClient) UseTokenLock() { + client.mut = new(sync.RWMutex) + client.reauthmut = new(reauthlock) +} + +// GetAuthResult returns the result from the request that was used to obtain a +// provider client's Keystone token. +// +// The result is nil when authentication has not yet taken place, when the token +// was set manually with SetToken(), or when a ReauthFunc was used that does not +// record the AuthResult. +func (client *ProviderClient) GetAuthResult() AuthResult { + if client.mut != nil { + client.mut.RLock() + defer client.mut.RUnlock() + } + return client.authResult +} + +// Token safely reads the value of the auth token from the ProviderClient. Applications should +// call this method to access the token instead of the TokenID field +func (client *ProviderClient) Token() string { + if client.mut != nil { + client.mut.RLock() + defer client.mut.RUnlock() + } + return client.TokenID +} + +// SetToken safely sets the value of the auth token in the ProviderClient. Applications may +// use this method in a custom ReauthFunc. +// +// WARNING: This function is deprecated. Use SetTokenAndAuthResult() instead. +func (client *ProviderClient) SetToken(t string) { + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + client.TokenID = t + client.authResult = nil +} + +// SetTokenAndAuthResult safely sets the value of the auth token in the +// ProviderClient and also records the AuthResult that was returned from the +// token creation request. Applications may call this in a custom ReauthFunc. +func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error { + tokenID := "" + var err error + if r != nil { + tokenID, err = r.ExtractTokenID() + if err != nil { + return err + } + } + + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + client.TokenID = tokenID + client.authResult = r + return nil +} + +// CopyTokenFrom safely copies the token from another ProviderClient into the +// this one. +func (client *ProviderClient) CopyTokenFrom(other *ProviderClient) { + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + if other.mut != nil && other.mut != client.mut { + other.mut.RLock() + defer other.mut.RUnlock() + } + client.TokenID = other.TokenID + client.authResult = other.authResult +} + +// IsThrowaway safely reads the value of the client Throwaway field. +func (client *ProviderClient) IsThrowaway() bool { + if client.reauthmut != nil { + client.reauthmut.RLock() + defer client.reauthmut.RUnlock() + } + return client.Throwaway +} + +// SetThrowaway safely sets the value of the client Throwaway field. +func (client *ProviderClient) SetThrowaway(v bool) { + if client.reauthmut != nil { + client.reauthmut.Lock() + defer client.reauthmut.Unlock() + } + client.Throwaway = v +} + +// Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is +// called because of a 401 response, the caller may pass the previous token. In +// this case, the reauthentication can be skipped if another thread has already +// reauthenticated in the meantime. If no previous token is known, an empty +// string should be passed instead to force unconditional reauthentication. +func (client *ProviderClient) Reauthenticate(ctx context.Context, previousToken string) error { + if client.ReauthFunc == nil { + return nil + } + + if client.reauthmut == nil { + return client.ReauthFunc(ctx) + } + + future := newReauthFuture() + + // Check if a Reauthenticate is in progress, or start one if not. + client.reauthmut.Lock() + ongoing := client.reauthmut.ongoing + if ongoing == nil { + client.reauthmut.ongoing = future + } + client.reauthmut.Unlock() + + // If Reauthenticate is running elsewhere, wait for its result. + if ongoing != nil { + return ongoing.Get() + } + + // Perform the actual reauthentication. + var err error + if previousToken == "" || client.TokenID == previousToken { + err = client.ReauthFunc(ctx) + } else { + err = nil + } + + // Mark Reauthenticate as finished. + client.reauthmut.Lock() + client.reauthmut.ongoing.Set(err) + client.reauthmut.ongoing = nil + client.reauthmut.Unlock() + + return err +} + +// RequestOpts customizes the behavior of the provider.Request() method. +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 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 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 + // MoreHeaders specifies additional HTTP headers to be provided on the request. + // MoreHeaders will be overridden by OmitHeaders + MoreHeaders map[string]string + // OmitHeaders specifies the HTTP headers which should be omitted. + // OmitHeaders will override MoreHeaders + OmitHeaders []string + // 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 +} + +// requestState contains temporary state for a single ProviderClient.Request() call. +type requestState struct { + // This flag indicates if we have reauthenticated during this request because of a 401 response. + // It ensures that we don't reauthenticate multiple times for a single request. If we + // reauthenticate, but keep getting 401 responses with the fresh token, reauthenticating some more + // will just get us into an infinite loop. + hasReauthenticated bool + // Retry-After backoff counter, increments during each backoff call + retries uint +} + +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(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(ctx context.Context, method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { + var body io.Reader + var contentType *string + + // Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided + // io.ReadSeeker as-is. Default the content-type to application/json. + if options.JSONBody != nil { + if options.RawBody != nil { + return nil, errors.New("please provide only one of JSONBody or RawBody to gophercloud.Request()") + } + + rendered, err := json.Marshal(options.JSONBody) + if err != nil { + return nil, err + } + + body = bytes.NewReader(rendered) + contentType = &applicationJSON + } + + // Return an error, when "KeepResponseBody" is true and "JSONResponse" is not nil + if options.KeepResponseBody && options.JSONResponse != nil { + return nil, errors.New("cannot use KeepResponseBody when JSONResponse is not nil") + } + + if options.RawBody != nil { + body = options.RawBody + } + + req, err := http.NewRequestWithContext(ctx, method, url, body) + if err != nil { + return nil, err + } + + // Populate the request headers. + // Apply options.MoreHeaders and options.OmitHeaders, to give the caller the chance to + // modify or omit any header. + if contentType != nil { + req.Header.Set("Content-Type", *contentType) + } + req.Header.Set("Accept", applicationJSON) + + // Set the User-Agent header + req.Header.Set("User-Agent", client.UserAgent.Join()) + + if options.MoreHeaders != nil { + for k, v := range options.MoreHeaders { + req.Header.Set(k, v) + } + } + + for _, v := range options.OmitHeaders { + req.Header.Del(v) + } + + // get latest token from client + for k, v := range client.AuthenticatedHeaders() { + req.Header.Set(k, v) + } + + prereqtok := req.Header.Get("X-Auth-Token") + + // Issue the request. + resp, err := client.HTTPClient.Do(req) + if err != nil { + if client.RetryFunc != nil { + var e error + state.retries = state.retries + 1 + e = client.RetryFunc(ctx, method, url, options, err, state.retries) + if e != nil { + return nil, e + } + + return client.doRequest(ctx, method, url, options, state) + } + return nil, err + } + + // Allow default OkCodes if none explicitly set + okc := options.OkCodes + if okc == nil { + 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, _ := io.ReadAll(resp.Body) + resp.Body.Close() + respErr := ErrUnexpectedResponseCode{ + URL: url, + Method: method, + Expected: okc, + Actual: resp.StatusCode, + Body: body, + ResponseHeader: resp.Header, + } + + switch resp.StatusCode { + case http.StatusUnauthorized: + if client.ReauthFunc != nil && !state.hasReauthenticated { + err = client.Reauthenticate(ctx, prereqtok) + if err != nil { + e := &ErrUnableToReauthenticate{} + e.ErrOriginal = respErr + e.ErrReauth = err + return nil, e + } + if options.RawBody != nil { + if seeker, ok := options.RawBody.(io.Seeker); ok { + if _, err := seeker.Seek(0, 0); err != nil { + return nil, err + } + } + } + state.hasReauthenticated = true + resp, err = client.doRequest(ctx, method, url, options, state) + if err != nil { + switch e := err.(type) { + case *ErrUnexpectedResponseCode: + err := &ErrErrorAfterReauthentication{} + err.ErrOriginal = e + return nil, err + default: + err := &ErrErrorAfterReauthentication{} + err.ErrOriginal = e + return nil, err + } + } + return resp, nil + } + case http.StatusTooManyRequests, 498: + maxTries := client.MaxBackoffRetries + if maxTries == 0 { + maxTries = DefaultMaxBackoffRetries + } + + if f := client.RetryBackoffFunc; f != nil && state.retries < maxTries { + var e error + + state.retries = state.retries + 1 + e = f(ctx, &respErr, err, state.retries) + + if e != nil { + return resp, e + } + + return client.doRequest(ctx, method, url, options, state) + } + } + + if err == nil { + err = respErr + } + + if err != nil && client.RetryFunc != nil { + var e error + state.retries = state.retries + 1 + e = client.RetryFunc(ctx, method, url, options, err, state.retries) + if e != nil { + return resp, e + } + + return client.doRequest(ctx, method, url, options, state) + } + + return resp, err + } + + // Parse the response body as JSON, if requested to do so. + if options.JSONResponse != nil { + defer resp.Body.Close() + // 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(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(ctx, method, url, options, err, state.retries) + if e != nil { + return resp, e + } + + return client.doRequest(ctx, method, url, options, state) + } + return nil, err + } + } + + // Close unused body to allow the HTTP connection to be reused + 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(io.Discard, resp.Body); err != nil { + return nil, err + } + } + + return resp, nil +} + +func defaultOkCodes(method string) []int { + switch method { + case "GET", "HEAD": + return []int{200} + case "POST": + return []int{201, 202} + case "PUT": + return []int{201, 202} + case "PATCH": + return []int{200, 202, 204} + case "DELETE": + return []int{202, 204} + } + + return []int{} +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/results.go b/vendor/github.com/gophercloud/gophercloud/v2/results.go new file mode 100644 index 000000000..9e6f630ab --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/results.go @@ -0,0 +1,465 @@ +package gophercloud + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "strconv" + "time" +) + +/* +Result is an internal type to be used by individual resource packages, but its +methods will be available on a wide variety of user-facing embedding types. + +It acts as a base struct that other Result types, returned from request +functions, can embed for convenience. All Results capture basic information +from the HTTP transaction that was performed, including the response body, +HTTP headers, and any errors that happened. + +Generally, each Result type will have an Extract method that can be used to +further interpret the result's payload in a specific context. Extensions or +providers can then provide additional extraction functions to pull out +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 any + + // StatusCode is the HTTP status code of the original response. Will be + // one of the OkCodes defined on the gophercloud.RequestOpts that was + // used in the request. + StatusCode int + + // Header contains the HTTP header structure from the original response. + Header http.Header + + // Err is an error that occurred during the operation. It's deferred until + // extraction to make it easier to chain the Extract call. + Err error +} + +// 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 any) error { + if r.Err != nil { + return r.Err + } + + if reader, ok := r.Body.(io.Reader); ok { + if readCloser, ok := reader.(io.Closer); ok { + defer readCloser.Close() + } + return json.NewDecoder(reader).Decode(to) + } + + b, err := json.Marshal(r.Body) + if err != nil { + return err + } + err = json.Unmarshal(b, to) + + return err +} + +func (r Result) extractIntoPtr(to any, label string) error { + if label == "" { + return r.ExtractInto(&to) + } + + var m map[string]any + err := r.ExtractInto(&m) + if err != nil { + return err + } + + b, err := json.Marshal(m[label]) + if err != nil { + return err + } + + toValue := reflect.ValueOf(to) + if toValue.Kind() == reflect.Ptr { + toValue = toValue.Elem() + } + + switch toValue.Kind() { + case reflect.Slice: + typeOfV := toValue.Type().Elem() + if typeOfV.Kind() == reflect.Struct { + if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { + newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) + + 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 + // are reused and not overwritten when the same copy of the + // struct is used: + // + // https://github.com/golang/go/issues/21092 + // https://github.com/golang/go/issues/24155 + // https://play.golang.org/p/NHo3ywlPZli + newType := reflect.New(typeOfV).Elem() + + b, err := json.Marshal(v) + if err != nil { + return err + } + + // This is needed for structs with an UnmarshalJSON method. + // Technically this is just unmarshalling the response into + // a struct that is never used, but it's good enough to + // trigger the UnmarshalJSON method. + for i := 0; i < newType.NumField(); i++ { + s := newType.Field(i).Addr().Interface() + + // Unmarshal is used rather than NewDecoder to also work + // around the above-mentioned bug. + err = json.Unmarshal(b, s) + if err != nil { + return err + } + } + + newSlice = reflect.Append(newSlice, newType) + } + } + + // "to" should now be properly modeled to receive the + // JSON response body and unmarshal into all the correct + // fields of the struct or composed extension struct + // at the end of this method. + toValue.Set(newSlice) + + // jtopjian: This was put into place to resolve the issue + // described at + // https://github.com/gophercloud/gophercloud/issues/1963 + // + // This probably isn't the best fix, but it appears to + // be resolving the issue, so I'm going to implement it + // for now. + // + // For future readers, this entire case statement could + // use a review. + return nil + } + } + case reflect.Struct: + typeOfV := toValue.Type() + if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { + for i := 0; i < toValue.NumField(); i++ { + toField := toValue.Field(i) + if toField.Kind() == reflect.Struct { + s := toField.Addr().Interface() + err = json.NewDecoder(bytes.NewReader(b)).Decode(s) + if err != nil { + return err + } + } + } + } + } + + err = json.Unmarshal(b, &to) + return err +} + +// ExtractIntoStructPtr will unmarshal the Result (r) into the provided +// any (to). +// +// NOTE: For internal use only +// +// `to` must be a pointer to an underlying struct type +// +// If provided, `label` will be filtered out of the response +// body prior to `r` being unmarshalled into `to`. +func (r Result) ExtractIntoStructPtr(to any, label string) error { + if r.Err != nil { + return r.Err + } + + t := reflect.TypeOf(to) + if k := t.Kind(); k != reflect.Ptr { + return fmt.Errorf("Expected pointer, got %v", k) + } + switch t.Elem().Kind() { + case reflect.Struct: + return r.extractIntoPtr(to, label) + default: + return fmt.Errorf("Expected pointer to struct, got: %v", t) + } +} + +// ExtractIntoSlicePtr will unmarshal the Result (r) into the provided +// any (to). +// +// NOTE: For internal use only +// +// `to` must be a pointer to an underlying slice type +// +// If provided, `label` will be filtered out of the response +// body prior to `r` being unmarshalled into `to`. +func (r Result) ExtractIntoSlicePtr(to any, label string) error { + if r.Err != nil { + return r.Err + } + + t := reflect.TypeOf(to) + if k := t.Kind(); k != reflect.Ptr { + return fmt.Errorf("Expected pointer, got %v", k) + } + switch t.Elem().Kind() { + case reflect.Slice: + return r.extractIntoPtr(to, label) + default: + return fmt.Errorf("Expected pointer to slice, got: %v", t) + } +} + +// PrettyPrintJSON creates a string containing the full response body as +// pretty-printed JSON. It's useful for capturing test fixtures and for +// debugging extraction bugs. If you include its output in an issue related to +// a buggy extraction function, we will all love you forever. +func (r Result) PrettyPrintJSON() string { + pretty, err := json.MarshalIndent(r.Body, "", " ") + if err != nil { + panic(err.Error()) + } + return string(pretty) +} + +// ErrResult is an internal type to be used by individual resource packages, but +// its methods will be available on a wide variety of user-facing embedding +// types. +// +// It represents results that only contain a potential error and +// nothing else. Usually, if the operation executed successfully, the Err field +// will be nil; otherwise it will be stocked with a relevant error. Use the +// ExtractErr method +// to cleanly pull it out. +type ErrResult struct { + Result +} + +// ExtractErr is a function that extracts error information, or nil, from a result. +func (r ErrResult) ExtractErr() error { + return r.Err +} + +/* +HeaderResult is an internal type to be used by individual resource packages, but +its methods will be available on a wide variety of user-facing embedding types. + +It represents a result that only contains an error (possibly nil) and an +http.Header. This is used, for example, by the objectstorage packages in +openstack, because most of the operations don't return response bodies, but do +have relevant information in headers. +*/ +type HeaderResult struct { + Result +} + +// ExtractInto allows users to provide an object into which `Extract` will +// extract the http.Header headers of the result. +func (r HeaderResult) ExtractInto(to any) error { + if r.Err != nil { + return r.Err + } + + tmpHeaderMap := map[string]string{} + for k, v := range r.Header { + if len(v) > 0 { + tmpHeaderMap[k] = v[0] + } + } + + b, err := json.Marshal(tmpHeaderMap) + if err != nil { + return err + } + err = json.Unmarshal(b, to) + + return err +} + +// RFC3339Milli describes a common time format used by some API responses. +const RFC3339Milli = "2006-01-02T15:04:05.999999Z" + +type JSONRFC3339Milli time.Time + +func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error { + b := bytes.NewBuffer(data) + dec := json.NewDecoder(b) + var s string + if err := dec.Decode(&s); err != nil { + return err + } + t, err := time.Parse(RFC3339Milli, s) + if err != nil { + return err + } + *jt = JSONRFC3339Milli(t) + return nil +} + +const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999" + +type JSONRFC3339MilliNoZ time.Time + +func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339MilliNoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339MilliNoZ(t) + return nil +} + +type JSONRFC1123 time.Time + +func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(time.RFC1123, s) + if err != nil { + return err + } + *jt = JSONRFC1123(t) + return nil +} + +type JSONUnix time.Time + +func (jt *JSONUnix) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + unix, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return err + } + t = time.Unix(unix, 0) + *jt = JSONUnix(t) + return nil +} + +// RFC3339NoZ is the time format used in Heat (Orchestration). +const RFC3339NoZ = "2006-01-02T15:04:05" + +type JSONRFC3339NoZ time.Time + +func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339NoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339NoZ(t) + return nil +} + +// RFC3339ZNoT is the time format used in Zun (Containers Service). +const RFC3339ZNoT = "2006-01-02 15:04:05-07:00" + +type JSONRFC3339ZNoT time.Time + +func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339ZNoT, s) + if err != nil { + return err + } + *jt = JSONRFC3339ZNoT(t) + return nil +} + +// RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). +const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" + +type JSONRFC3339ZNoTNoZ time.Time + +func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339ZNoTNoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339ZNoTNoZ(t) + return nil +} + +/* +Link is an internal type to be used in packages of collection resources that are +paginated in a certain way. + +It's a response substructure common to many paginated collection results that is +used to point to related pages. Usually, the one we care about is the one with +Rel field set to "next". +*/ +type Link struct { + Href string `json:"href"` + Rel string `json:"rel"` +} + +/* +ExtractNextURL is an internal function useful for packages of collection +resources that are paginated in a certain way. + +It attempts to extract the "next" URL from slice of Link structs, or +"" if no such URL is present. +*/ +func ExtractNextURL(links []Link) (string, error) { + var url string + + for _, l := range links { + if l.Rel == "next" { + url = l.Href + } + } + + if url == "" { + return "", nil + } + + return url, nil +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/service_client.go b/vendor/github.com/gophercloud/gophercloud/v2/service_client.go new file mode 100644 index 000000000..11b80108c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/service_client.go @@ -0,0 +1,164 @@ +package gophercloud + +import ( + "context" + "io" + "net/http" + "strings" +) + +// ServiceClient stores details required to interact with a specific service API implemented by a provider. +// Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient. +type ServiceClient struct { + // ProviderClient is a reference to the provider that implements this service. + *ProviderClient + + // Endpoint is the base URL of the service's API, acquired from a service catalog. + // It MUST end with a /. + Endpoint string + + // ResourceBase is the base URL shared by the resources within a service's API. It should include + // the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used + // as-is, instead. + ResourceBase string + + // This is the service client type (e.g. compute, sharev2). + // NOTE: FOR INTERNAL USE ONLY. DO NOT SET. GOPHERCLOUD WILL SET THIS. + // It is only exported because it gets set in a different package. + Type string + + // The microversion of the service to use. Set this to use a particular microversion. + Microversion string + + // MoreHeaders allows users (or Gophercloud) to set service-wide headers on requests. Put another way, + // values set in this field will be set on all the HTTP requests the service client sends. + MoreHeaders map[string]string +} + +// ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. +func (client *ServiceClient) ResourceBaseURL() string { + if client.ResourceBase != "" { + return client.ResourceBase + } + return client.Endpoint +} + +// ServiceURL constructs a URL for a resource belonging to this provider. +func (client *ServiceClient) ServiceURL(parts ...string) string { + return client.ResourceBaseURL() + strings.Join(parts, "/") +} + +func (client *ServiceClient) initReqOpts(JSONBody any, JSONResponse any, opts *RequestOpts) { + if v, ok := (JSONBody).(io.Reader); ok { + opts.RawBody = v + } else if JSONBody != nil { + opts.JSONBody = JSONBody + } + + if JSONResponse != nil { + opts.JSONResponse = JSONResponse + } +} + +// Get calls `Request` with the "GET" HTTP verb. +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(ctx, "GET", url, opts) +} + +// Post calls `Request` with the "POST" HTTP verb. +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(ctx, "POST", url, opts) +} + +// Put calls `Request` with the "PUT" HTTP verb. +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(ctx, "PUT", url, opts) +} + +// Patch calls `Request` with the "PATCH" HTTP verb. +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(ctx, "PATCH", url, opts) +} + +// Delete calls `Request` with the "DELETE" HTTP verb. +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(ctx, "DELETE", url, opts) +} + +// Head calls `Request` with the "HEAD" HTTP verb. +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(ctx, "HEAD", url, opts) +} + +func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { + switch client.Type { + case "compute": + opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion + case "sharev2": + opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion + case "volume": + opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion + case "baremetal": + opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion + case "baremetal-introspection": + opts.MoreHeaders["X-OpenStack-Ironic-Inspector-API-Version"] = client.Microversion + } + + if client.Type != "" { + opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion + } +} + +// Request carries out the HTTP operation for the service client +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) + } + + if client.Microversion != "" { + client.setMicroversionHeader(options) + } + + if len(client.MoreHeaders) > 0 { + if options == nil { + options = new(RequestOpts) + } + + for k, v := range client.MoreHeaders { + options.MoreHeaders[k] = v + } + } + return client.ProviderClient.Request(ctx, method, url, options) +} + +// ParseResponse is a helper function to parse http.Response to constituents. +func ParseResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, error) { + if resp != nil { + return resp.Body, resp.Header, err + } + return nil, nil, err +} diff --git a/vendor/github.com/gophercloud/gophercloud/v2/util.go b/vendor/github.com/gophercloud/gophercloud/v2/util.go new file mode 100644 index 000000000..ad8a7dfaa --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/v2/util.go @@ -0,0 +1,109 @@ +package gophercloud + +import ( + "context" + "net/url" + "path/filepath" + "reflect" + "strings" + "time" +) + +// NormalizePathURL is used to convert rawPath to a fqdn, using basePath as +// a reference in the filesystem, if necessary. basePath is assumed to contain +// either '.' when first used, or the file:// type fqdn of the parent resource. +// e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml +func NormalizePathURL(basePath, rawPath string) (string, error) { + u, err := url.Parse(rawPath) + if err != nil { + return "", err + } + // if a scheme is defined, it must be a fqdn already + if u.Scheme != "" { + return u.String(), nil + } + // if basePath is a url, then child resources are assumed to be relative to it + bu, err := url.Parse(basePath) + if err != nil { + return "", err + } + var basePathSys, absPathSys string + if bu.Scheme != "" { + basePathSys = filepath.FromSlash(bu.Path) + absPathSys = filepath.Join(basePathSys, rawPath) + bu.Path = filepath.ToSlash(absPathSys) + return bu.String(), nil + } + + absPathSys = filepath.Join(basePath, rawPath) + u.Path = filepath.ToSlash(absPathSys) + if err != nil { + return "", err + } + u.Scheme = "file" + return u.String(), nil +} + +// NormalizeURL is an internal function to be used by provider clients. +// +// It ensures that each endpoint URL has a closing `/`, as expected by +// ServiceClient's methods. +func NormalizeURL(url string) string { + if !strings.HasSuffix(url, "/") { + return url + "/" + } + return url +} + +// RemainingKeys will inspect a struct and compare it to a map. Any struct +// field that does not have a JSON tag that matches a key in the map or +// a matching lower-case field in the map will be returned as an extra. +// +// This is useful for determining the extra fields returned in response bodies +// for resources that can contain an arbitrary or dynamic number of fields. +func RemainingKeys(s any, m map[string]any) (extras map[string]any) { + extras = make(map[string]any) + for k, v := range m { + extras[k] = v + } + + valueOf := reflect.ValueOf(s) + typeOf := reflect.TypeOf(s) + for i := 0; i < valueOf.NumField(); i++ { + field := typeOf.Field(i) + + lowerField := strings.ToLower(field.Name) + delete(extras, lowerField) + + if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" { + delete(extras, tagValue) + } + } + + return +} + +// 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. +// 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(ctx context.Context, predicate func(context.Context) (bool, error)) error { + if done, err := predicate(ctx); done || err != nil { + return err + } + + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if done, err := predicate(ctx); done || err != nil { + return err + } + + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go index 213bf204a..089895680 100644 --- a/vendor/golang.org/x/crypto/blowfish/cipher.go +++ b/vendor/golang.org/x/crypto/blowfish/cipher.go @@ -11,7 +11,7 @@ // Deprecated: any new system should use AES (from crypto/aes, if necessary in // an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from // golang.org/x/crypto/chacha20poly1305). -package blowfish // import "golang.org/x/crypto/blowfish" +package blowfish // The code is a port of Bruce Schneier's C implementation. // See https://www.schneier.com/blowfish.html. diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go index 93da7322b..8cf5d8112 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go @@ -5,7 +5,7 @@ // Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD and its // extended nonce variant XChaCha20-Poly1305, as specified in RFC 8439 and // draft-irtf-cfrg-xchacha-01. -package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305" +package chacha20poly1305 import ( "crypto/cipher" diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go index cda8e3edf..90ef6a241 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go @@ -4,7 +4,7 @@ // Package asn1 contains supporting types for parsing and building ASN.1 // messages with the cryptobyte package. -package asn1 // import "golang.org/x/crypto/cryptobyte/asn1" +package asn1 // Tag represents an ASN.1 identifier octet, consisting of a tag number // (indicating a type) and class (such as context-specific or constructed). diff --git a/vendor/golang.org/x/crypto/cryptobyte/string.go b/vendor/golang.org/x/crypto/cryptobyte/string.go index 10692a8a3..4b0f8097f 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/string.go +++ b/vendor/golang.org/x/crypto/cryptobyte/string.go @@ -15,7 +15,7 @@ // // See the documentation and examples for the Builder and String types to get // started. -package cryptobyte // import "golang.org/x/crypto/cryptobyte" +package cryptobyte // String represents a string of bytes. It provides methods for parsing // fixed-length and length-prefixed values from it. diff --git a/vendor/golang.org/x/crypto/curve25519/BUILD.bazel b/vendor/golang.org/x/crypto/curve25519/BUILD.bazel index 0b2035a08..8b33ef410 100644 --- a/vendor/golang.org/x/crypto/curve25519/BUILD.bazel +++ b/vendor/golang.org/x/crypto/curve25519/BUILD.bazel @@ -2,13 +2,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "curve25519", - srcs = [ - "curve25519.go", - "curve25519_compat.go", - "curve25519_go120.go", - ], + srcs = ["curve25519.go"], importmap = "sigs.k8s.io/etcd-manager/vendor/golang.org/x/crypto/curve25519", importpath = "golang.org/x/crypto/curve25519", visibility = ["//visibility:public"], - deps = ["//vendor/golang.org/x/crypto/curve25519/internal/field"], ) diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go index 00f963ea2..21ca3b2ee 100644 --- a/vendor/golang.org/x/crypto/curve25519/curve25519.go +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -6,9 +6,11 @@ // performs scalar multiplication on the elliptic curve known as Curve25519. // See RFC 7748. // -// Starting in Go 1.20, this package is a wrapper for the X25519 implementation +// This package is a wrapper for the X25519 implementation // in the crypto/ecdh package. -package curve25519 // import "golang.org/x/crypto/curve25519" +package curve25519 + +import "crypto/ecdh" // ScalarMult sets dst to the product scalar * point. // @@ -16,7 +18,13 @@ package curve25519 // import "golang.org/x/crypto/curve25519" // zeroes, irrespective of the scalar. Instead, use the X25519 function, which // will return an error. func ScalarMult(dst, scalar, point *[32]byte) { - scalarMult(dst, scalar, point) + if _, err := x25519(dst, scalar[:], point[:]); err != nil { + // The only error condition for x25519 when the inputs are 32 bytes long + // is if the output would have been the all-zero value. + for i := range dst { + dst[i] = 0 + } + } } // ScalarBaseMult sets dst to the product scalar * base where base is the @@ -25,7 +33,12 @@ func ScalarMult(dst, scalar, point *[32]byte) { // It is recommended to use the X25519 function with Basepoint instead, as // copying into fixed size arrays can lead to unexpected bugs. func ScalarBaseMult(dst, scalar *[32]byte) { - scalarBaseMult(dst, scalar) + curve := ecdh.X25519() + priv, err := curve.NewPrivateKey(scalar[:]) + if err != nil { + panic("curve25519: internal error: scalarBaseMult was not 32 bytes") + } + copy(dst[:], priv.PublicKey().Bytes()) } const ( @@ -57,3 +70,21 @@ func X25519(scalar, point []byte) ([]byte, error) { var dst [32]byte return x25519(&dst, scalar, point) } + +func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { + curve := ecdh.X25519() + pub, err := curve.NewPublicKey(point) + if err != nil { + return nil, err + } + priv, err := curve.NewPrivateKey(scalar) + if err != nil { + return nil, err + } + out, err := priv.ECDH(pub) + if err != nil { + return nil, err + } + copy(dst[:], out) + return dst[:], nil +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_compat.go b/vendor/golang.org/x/crypto/curve25519/curve25519_compat.go deleted file mode 100644 index ba647e8d7..000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_compat.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.20 - -package curve25519 - -import ( - "crypto/subtle" - "errors" - "strconv" - - "golang.org/x/crypto/curve25519/internal/field" -) - -func scalarMult(dst, scalar, point *[32]byte) { - var e [32]byte - - copy(e[:], scalar[:]) - e[0] &= 248 - e[31] &= 127 - e[31] |= 64 - - var x1, x2, z2, x3, z3, tmp0, tmp1 field.Element - x1.SetBytes(point[:]) - x2.One() - x3.Set(&x1) - z3.One() - - swap := 0 - for pos := 254; pos >= 0; pos-- { - b := e[pos/8] >> uint(pos&7) - b &= 1 - swap ^= int(b) - x2.Swap(&x3, swap) - z2.Swap(&z3, swap) - swap = int(b) - - tmp0.Subtract(&x3, &z3) - tmp1.Subtract(&x2, &z2) - x2.Add(&x2, &z2) - z2.Add(&x3, &z3) - z3.Multiply(&tmp0, &x2) - z2.Multiply(&z2, &tmp1) - tmp0.Square(&tmp1) - tmp1.Square(&x2) - x3.Add(&z3, &z2) - z2.Subtract(&z3, &z2) - x2.Multiply(&tmp1, &tmp0) - tmp1.Subtract(&tmp1, &tmp0) - z2.Square(&z2) - - z3.Mult32(&tmp1, 121666) - x3.Square(&x3) - tmp0.Add(&tmp0, &z3) - z3.Multiply(&x1, &z2) - z2.Multiply(&tmp1, &tmp0) - } - - x2.Swap(&x3, swap) - z2.Swap(&z3, swap) - - z2.Invert(&z2) - x2.Multiply(&x2, &z2) - copy(dst[:], x2.Bytes()) -} - -func scalarBaseMult(dst, scalar *[32]byte) { - checkBasepoint() - scalarMult(dst, scalar, &basePoint) -} - -func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { - var in [32]byte - if l := len(scalar); l != 32 { - return nil, errors.New("bad scalar length: " + strconv.Itoa(l) + ", expected 32") - } - if l := len(point); l != 32 { - return nil, errors.New("bad point length: " + strconv.Itoa(l) + ", expected 32") - } - copy(in[:], scalar) - if &point[0] == &Basepoint[0] { - scalarBaseMult(dst, &in) - } else { - var base, zero [32]byte - copy(base[:], point) - scalarMult(dst, &in, &base) - if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 { - return nil, errors.New("bad input point: low order point") - } - } - return dst[:], nil -} - -func checkBasepoint() { - if subtle.ConstantTimeCompare(Basepoint, []byte{ - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }) != 1 { - panic("curve25519: global Basepoint value was modified") - } -} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_go120.go b/vendor/golang.org/x/crypto/curve25519/curve25519_go120.go deleted file mode 100644 index 627df4972..000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_go120.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.20 - -package curve25519 - -import "crypto/ecdh" - -func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { - curve := ecdh.X25519() - pub, err := curve.NewPublicKey(point) - if err != nil { - return nil, err - } - priv, err := curve.NewPrivateKey(scalar) - if err != nil { - return nil, err - } - out, err := priv.ECDH(pub) - if err != nil { - return nil, err - } - copy(dst[:], out) - return dst[:], nil -} - -func scalarMult(dst, scalar, point *[32]byte) { - if _, err := x25519(dst, scalar[:], point[:]); err != nil { - // The only error condition for x25519 when the inputs are 32 bytes long - // is if the output would have been the all-zero value. - for i := range dst { - dst[i] = 0 - } - } -} - -func scalarBaseMult(dst, scalar *[32]byte) { - curve := ecdh.X25519() - priv, err := curve.NewPrivateKey(scalar[:]) - if err != nil { - panic("curve25519: internal error: scalarBaseMult was not 32 bytes") - } - copy(dst[:], priv.PublicKey().Bytes()) -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/BUILD.bazel b/vendor/golang.org/x/crypto/curve25519/internal/field/BUILD.bazel deleted file mode 100644 index afeef4f5c..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "field", - srcs = [ - "fe.go", - "fe_amd64.go", - "fe_amd64.s", - "fe_amd64_noasm.go", - "fe_arm64.go", - "fe_arm64.s", - "fe_arm64_noasm.go", - "fe_generic.go", - ], - importmap = "sigs.k8s.io/etcd-manager/vendor/golang.org/x/crypto/curve25519/internal/field", - importpath = "golang.org/x/crypto/curve25519/internal/field", - visibility = ["//vendor/golang.org/x/crypto/curve25519:__subpackages__"], -) diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/README b/vendor/golang.org/x/crypto/curve25519/internal/field/README deleted file mode 100644 index e25bca7dc..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/README +++ /dev/null @@ -1,7 +0,0 @@ -This package is kept in sync with crypto/ed25519/internal/edwards25519/field in -the standard library. - -If there are any changes in the standard library that need to be synced to this -package, run sync.sh. It will not overwrite any local changes made since the -previous sync, so it's ok to land changes in this package first, and then sync -to the standard library later. diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go deleted file mode 100644 index ca841ad99..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright (c) 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package field implements fast arithmetic modulo 2^255-19. -package field - -import ( - "crypto/subtle" - "encoding/binary" - "math/bits" -) - -// Element represents an element of the field GF(2^255-19). Note that this -// is not a cryptographically secure group, and should only be used to interact -// with edwards25519.Point coordinates. -// -// This type works similarly to math/big.Int, and all arguments and receivers -// are allowed to alias. -// -// The zero value is a valid zero element. -type Element struct { - // An element t represents the integer - // t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204 - // - // Between operations, all limbs are expected to be lower than 2^52. - l0 uint64 - l1 uint64 - l2 uint64 - l3 uint64 - l4 uint64 -} - -const maskLow51Bits uint64 = (1 << 51) - 1 - -var feZero = &Element{0, 0, 0, 0, 0} - -// Zero sets v = 0, and returns v. -func (v *Element) Zero() *Element { - *v = *feZero - return v -} - -var feOne = &Element{1, 0, 0, 0, 0} - -// One sets v = 1, and returns v. -func (v *Element) One() *Element { - *v = *feOne - return v -} - -// reduce reduces v modulo 2^255 - 19 and returns it. -func (v *Element) reduce() *Element { - v.carryPropagate() - - // After the light reduction we now have a field element representation - // v < 2^255 + 2^13 * 19, but need v < 2^255 - 19. - - // If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1, - // generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise. - c := (v.l0 + 19) >> 51 - c = (v.l1 + c) >> 51 - c = (v.l2 + c) >> 51 - c = (v.l3 + c) >> 51 - c = (v.l4 + c) >> 51 - - // If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's - // effectively applying the reduction identity to the carry. - v.l0 += 19 * c - - v.l1 += v.l0 >> 51 - v.l0 = v.l0 & maskLow51Bits - v.l2 += v.l1 >> 51 - v.l1 = v.l1 & maskLow51Bits - v.l3 += v.l2 >> 51 - v.l2 = v.l2 & maskLow51Bits - v.l4 += v.l3 >> 51 - v.l3 = v.l3 & maskLow51Bits - // no additional carry - v.l4 = v.l4 & maskLow51Bits - - return v -} - -// Add sets v = a + b, and returns v. -func (v *Element) Add(a, b *Element) *Element { - v.l0 = a.l0 + b.l0 - v.l1 = a.l1 + b.l1 - v.l2 = a.l2 + b.l2 - v.l3 = a.l3 + b.l3 - v.l4 = a.l4 + b.l4 - // Using the generic implementation here is actually faster than the - // assembly. Probably because the body of this function is so simple that - // the compiler can figure out better optimizations by inlining the carry - // propagation. TODO - return v.carryPropagateGeneric() -} - -// Subtract sets v = a - b, and returns v. -func (v *Element) Subtract(a, b *Element) *Element { - // We first add 2 * p, to guarantee the subtraction won't underflow, and - // then subtract b (which can be up to 2^255 + 2^13 * 19). - v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0 - v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1 - v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2 - v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3 - v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4 - return v.carryPropagate() -} - -// Negate sets v = -a, and returns v. -func (v *Element) Negate(a *Element) *Element { - return v.Subtract(feZero, a) -} - -// Invert sets v = 1/z mod p, and returns v. -// -// If z == 0, Invert returns v = 0. -func (v *Element) Invert(z *Element) *Element { - // Inversion is implemented as exponentiation with exponent p − 2. It uses the - // same sequence of 255 squarings and 11 multiplications as [Curve25519]. - var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element - - z2.Square(z) // 2 - t.Square(&z2) // 4 - t.Square(&t) // 8 - z9.Multiply(&t, z) // 9 - z11.Multiply(&z9, &z2) // 11 - t.Square(&z11) // 22 - z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 - - t.Square(&z2_5_0) // 2^6 - 2^1 - for i := 0; i < 4; i++ { - t.Square(&t) // 2^10 - 2^5 - } - z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 - - t.Square(&z2_10_0) // 2^11 - 2^1 - for i := 0; i < 9; i++ { - t.Square(&t) // 2^20 - 2^10 - } - z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 - - t.Square(&z2_20_0) // 2^21 - 2^1 - for i := 0; i < 19; i++ { - t.Square(&t) // 2^40 - 2^20 - } - t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 - - t.Square(&t) // 2^41 - 2^1 - for i := 0; i < 9; i++ { - t.Square(&t) // 2^50 - 2^10 - } - z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 - - t.Square(&z2_50_0) // 2^51 - 2^1 - for i := 0; i < 49; i++ { - t.Square(&t) // 2^100 - 2^50 - } - z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 - - t.Square(&z2_100_0) // 2^101 - 2^1 - for i := 0; i < 99; i++ { - t.Square(&t) // 2^200 - 2^100 - } - t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 - - t.Square(&t) // 2^201 - 2^1 - for i := 0; i < 49; i++ { - t.Square(&t) // 2^250 - 2^50 - } - t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 - - t.Square(&t) // 2^251 - 2^1 - t.Square(&t) // 2^252 - 2^2 - t.Square(&t) // 2^253 - 2^3 - t.Square(&t) // 2^254 - 2^4 - t.Square(&t) // 2^255 - 2^5 - - return v.Multiply(&t, &z11) // 2^255 - 21 -} - -// Set sets v = a, and returns v. -func (v *Element) Set(a *Element) *Element { - *v = *a - return v -} - -// SetBytes sets v to x, which must be a 32-byte little-endian encoding. -// -// Consistent with RFC 7748, the most significant bit (the high bit of the -// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1) -// are accepted. Note that this is laxer than specified by RFC 8032. -func (v *Element) SetBytes(x []byte) *Element { - if len(x) != 32 { - panic("edwards25519: invalid field element input size") - } - - // Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51). - v.l0 = binary.LittleEndian.Uint64(x[0:8]) - v.l0 &= maskLow51Bits - // Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51). - v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3 - v.l1 &= maskLow51Bits - // Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51). - v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6 - v.l2 &= maskLow51Bits - // Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51). - v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1 - v.l3 &= maskLow51Bits - // Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51). - // Note: not bytes 25:33, shift 4, to avoid overread. - v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12 - v.l4 &= maskLow51Bits - - return v -} - -// Bytes returns the canonical 32-byte little-endian encoding of v. -func (v *Element) Bytes() []byte { - // This function is outlined to make the allocations inline in the caller - // rather than happen on the heap. - var out [32]byte - return v.bytes(&out) -} - -func (v *Element) bytes(out *[32]byte) []byte { - t := *v - t.reduce() - - var buf [8]byte - for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} { - bitsOffset := i * 51 - binary.LittleEndian.PutUint64(buf[:], l<= len(out) { - break - } - out[off] |= bb - } - } - - return out[:] -} - -// Equal returns 1 if v and u are equal, and 0 otherwise. -func (v *Element) Equal(u *Element) int { - sa, sv := u.Bytes(), v.Bytes() - return subtle.ConstantTimeCompare(sa, sv) -} - -// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise. -func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) } - -// Select sets v to a if cond == 1, and to b if cond == 0. -func (v *Element) Select(a, b *Element, cond int) *Element { - m := mask64Bits(cond) - v.l0 = (m & a.l0) | (^m & b.l0) - v.l1 = (m & a.l1) | (^m & b.l1) - v.l2 = (m & a.l2) | (^m & b.l2) - v.l3 = (m & a.l3) | (^m & b.l3) - v.l4 = (m & a.l4) | (^m & b.l4) - return v -} - -// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v. -func (v *Element) Swap(u *Element, cond int) { - m := mask64Bits(cond) - t := m & (v.l0 ^ u.l0) - v.l0 ^= t - u.l0 ^= t - t = m & (v.l1 ^ u.l1) - v.l1 ^= t - u.l1 ^= t - t = m & (v.l2 ^ u.l2) - v.l2 ^= t - u.l2 ^= t - t = m & (v.l3 ^ u.l3) - v.l3 ^= t - u.l3 ^= t - t = m & (v.l4 ^ u.l4) - v.l4 ^= t - u.l4 ^= t -} - -// IsNegative returns 1 if v is negative, and 0 otherwise. -func (v *Element) IsNegative() int { - return int(v.Bytes()[0] & 1) -} - -// Absolute sets v to |u|, and returns v. -func (v *Element) Absolute(u *Element) *Element { - return v.Select(new(Element).Negate(u), u, u.IsNegative()) -} - -// Multiply sets v = x * y, and returns v. -func (v *Element) Multiply(x, y *Element) *Element { - feMul(v, x, y) - return v -} - -// Square sets v = x * x, and returns v. -func (v *Element) Square(x *Element) *Element { - feSquare(v, x) - return v -} - -// Mult32 sets v = x * y, and returns v. -func (v *Element) Mult32(x *Element, y uint32) *Element { - x0lo, x0hi := mul51(x.l0, y) - x1lo, x1hi := mul51(x.l1, y) - x2lo, x2hi := mul51(x.l2, y) - x3lo, x3hi := mul51(x.l3, y) - x4lo, x4hi := mul51(x.l4, y) - v.l0 = x0lo + 19*x4hi // carried over per the reduction identity - v.l1 = x1lo + x0hi - v.l2 = x2lo + x1hi - v.l3 = x3lo + x2hi - v.l4 = x4lo + x3hi - // The hi portions are going to be only 32 bits, plus any previous excess, - // so we can skip the carry propagation. - return v -} - -// mul51 returns lo + hi * 2⁵¹ = a * b. -func mul51(a uint64, b uint32) (lo uint64, hi uint64) { - mh, ml := bits.Mul64(a, uint64(b)) - lo = ml & maskLow51Bits - hi = (mh << 13) | (ml >> 51) - return -} - -// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3. -func (v *Element) Pow22523(x *Element) *Element { - var t0, t1, t2 Element - - t0.Square(x) // x^2 - t1.Square(&t0) // x^4 - t1.Square(&t1) // x^8 - t1.Multiply(x, &t1) // x^9 - t0.Multiply(&t0, &t1) // x^11 - t0.Square(&t0) // x^22 - t0.Multiply(&t1, &t0) // x^31 - t1.Square(&t0) // x^62 - for i := 1; i < 5; i++ { // x^992 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1 - t1.Square(&t0) // 2^11 - 2 - for i := 1; i < 10; i++ { // 2^20 - 2^10 - t1.Square(&t1) - } - t1.Multiply(&t1, &t0) // 2^20 - 1 - t2.Square(&t1) // 2^21 - 2 - for i := 1; i < 20; i++ { // 2^40 - 2^20 - t2.Square(&t2) - } - t1.Multiply(&t2, &t1) // 2^40 - 1 - t1.Square(&t1) // 2^41 - 2 - for i := 1; i < 10; i++ { // 2^50 - 2^10 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // 2^50 - 1 - t1.Square(&t0) // 2^51 - 2 - for i := 1; i < 50; i++ { // 2^100 - 2^50 - t1.Square(&t1) - } - t1.Multiply(&t1, &t0) // 2^100 - 1 - t2.Square(&t1) // 2^101 - 2 - for i := 1; i < 100; i++ { // 2^200 - 2^100 - t2.Square(&t2) - } - t1.Multiply(&t2, &t1) // 2^200 - 1 - t1.Square(&t1) // 2^201 - 2 - for i := 1; i < 50; i++ { // 2^250 - 2^50 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // 2^250 - 1 - t0.Square(&t0) // 2^251 - 2 - t0.Square(&t0) // 2^252 - 4 - return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3) -} - -// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion. -var sqrtM1 = &Element{1718705420411056, 234908883556509, - 2233514472574048, 2117202627021982, 765476049583133} - -// SqrtRatio sets r to the non-negative square root of the ratio of u and v. -// -// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio -// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00, -// and returns r and 0. -func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) { - var a, b Element - - // r = (u * v3) * (u * v7)^((p-5)/8) - v2 := a.Square(v) - uv3 := b.Multiply(u, b.Multiply(v2, v)) - uv7 := a.Multiply(uv3, a.Square(v2)) - r.Multiply(uv3, r.Pow22523(uv7)) - - check := a.Multiply(v, a.Square(r)) // check = v * r^2 - - uNeg := b.Negate(u) - correctSignSqrt := check.Equal(u) - flippedSignSqrt := check.Equal(uNeg) - flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1)) - - rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r - // r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r) - r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI) - - r.Absolute(r) // Choose the nonnegative square root. - return r, correctSignSqrt | flippedSignSqrt -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go deleted file mode 100644 index 70c541692..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go +++ /dev/null @@ -1,15 +0,0 @@ -// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. - -//go:build amd64 && gc && !purego - -package field - -// feMul sets out = a * b. It works like feMulGeneric. -// -//go:noescape -func feMul(out *Element, a *Element, b *Element) - -// feSquare sets out = a * a. It works like feSquareGeneric. -// -//go:noescape -func feSquare(out *Element, a *Element) diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s deleted file mode 100644 index 60817acc4..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s +++ /dev/null @@ -1,378 +0,0 @@ -// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. - -//go:build amd64 && gc && !purego - -#include "textflag.h" - -// func feMul(out *Element, a *Element, b *Element) -TEXT ·feMul(SB), NOSPLIT, $0-24 - MOVQ a+8(FP), CX - MOVQ b+16(FP), BX - - // r0 = a0×b0 - MOVQ (CX), AX - MULQ (BX) - MOVQ AX, DI - MOVQ DX, SI - - // r0 += 19×a1×b4 - MOVQ 8(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r0 += 19×a2×b3 - MOVQ 16(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r0 += 19×a3×b2 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 16(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r0 += 19×a4×b1 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 8(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r1 = a0×b1 - MOVQ (CX), AX - MULQ 8(BX) - MOVQ AX, R9 - MOVQ DX, R8 - - // r1 += a1×b0 - MOVQ 8(CX), AX - MULQ (BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r1 += 19×a2×b4 - MOVQ 16(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r1 += 19×a3×b3 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r1 += 19×a4×b2 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 16(BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r2 = a0×b2 - MOVQ (CX), AX - MULQ 16(BX) - MOVQ AX, R11 - MOVQ DX, R10 - - // r2 += a1×b1 - MOVQ 8(CX), AX - MULQ 8(BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r2 += a2×b0 - MOVQ 16(CX), AX - MULQ (BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r2 += 19×a3×b4 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r2 += 19×a4×b3 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r3 = a0×b3 - MOVQ (CX), AX - MULQ 24(BX) - MOVQ AX, R13 - MOVQ DX, R12 - - // r3 += a1×b2 - MOVQ 8(CX), AX - MULQ 16(BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r3 += a2×b1 - MOVQ 16(CX), AX - MULQ 8(BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r3 += a3×b0 - MOVQ 24(CX), AX - MULQ (BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r3 += 19×a4×b4 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r4 = a0×b4 - MOVQ (CX), AX - MULQ 32(BX) - MOVQ AX, R15 - MOVQ DX, R14 - - // r4 += a1×b3 - MOVQ 8(CX), AX - MULQ 24(BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // r4 += a2×b2 - MOVQ 16(CX), AX - MULQ 16(BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // r4 += a3×b1 - MOVQ 24(CX), AX - MULQ 8(BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // r4 += a4×b0 - MOVQ 32(CX), AX - MULQ (BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // First reduction chain - MOVQ $0x0007ffffffffffff, AX - SHLQ $0x0d, DI, SI - SHLQ $0x0d, R9, R8 - SHLQ $0x0d, R11, R10 - SHLQ $0x0d, R13, R12 - SHLQ $0x0d, R15, R14 - ANDQ AX, DI - IMUL3Q $0x13, R14, R14 - ADDQ R14, DI - ANDQ AX, R9 - ADDQ SI, R9 - ANDQ AX, R11 - ADDQ R8, R11 - ANDQ AX, R13 - ADDQ R10, R13 - ANDQ AX, R15 - ADDQ R12, R15 - - // Second reduction chain (carryPropagate) - MOVQ DI, SI - SHRQ $0x33, SI - MOVQ R9, R8 - SHRQ $0x33, R8 - MOVQ R11, R10 - SHRQ $0x33, R10 - MOVQ R13, R12 - SHRQ $0x33, R12 - MOVQ R15, R14 - SHRQ $0x33, R14 - ANDQ AX, DI - IMUL3Q $0x13, R14, R14 - ADDQ R14, DI - ANDQ AX, R9 - ADDQ SI, R9 - ANDQ AX, R11 - ADDQ R8, R11 - ANDQ AX, R13 - ADDQ R10, R13 - ANDQ AX, R15 - ADDQ R12, R15 - - // Store output - MOVQ out+0(FP), AX - MOVQ DI, (AX) - MOVQ R9, 8(AX) - MOVQ R11, 16(AX) - MOVQ R13, 24(AX) - MOVQ R15, 32(AX) - RET - -// func feSquare(out *Element, a *Element) -TEXT ·feSquare(SB), NOSPLIT, $0-16 - MOVQ a+8(FP), CX - - // r0 = l0×l0 - MOVQ (CX), AX - MULQ (CX) - MOVQ AX, SI - MOVQ DX, BX - - // r0 += 38×l1×l4 - MOVQ 8(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 32(CX) - ADDQ AX, SI - ADCQ DX, BX - - // r0 += 38×l2×l3 - MOVQ 16(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 24(CX) - ADDQ AX, SI - ADCQ DX, BX - - // r1 = 2×l0×l1 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 8(CX) - MOVQ AX, R8 - MOVQ DX, DI - - // r1 += 38×l2×l4 - MOVQ 16(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 32(CX) - ADDQ AX, R8 - ADCQ DX, DI - - // r1 += 19×l3×l3 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(CX) - ADDQ AX, R8 - ADCQ DX, DI - - // r2 = 2×l0×l2 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 16(CX) - MOVQ AX, R10 - MOVQ DX, R9 - - // r2 += l1×l1 - MOVQ 8(CX), AX - MULQ 8(CX) - ADDQ AX, R10 - ADCQ DX, R9 - - // r2 += 38×l3×l4 - MOVQ 24(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 32(CX) - ADDQ AX, R10 - ADCQ DX, R9 - - // r3 = 2×l0×l3 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 24(CX) - MOVQ AX, R12 - MOVQ DX, R11 - - // r3 += 2×l1×l2 - MOVQ 8(CX), AX - IMUL3Q $0x02, AX, AX - MULQ 16(CX) - ADDQ AX, R12 - ADCQ DX, R11 - - // r3 += 19×l4×l4 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(CX) - ADDQ AX, R12 - ADCQ DX, R11 - - // r4 = 2×l0×l4 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 32(CX) - MOVQ AX, R14 - MOVQ DX, R13 - - // r4 += 2×l1×l3 - MOVQ 8(CX), AX - IMUL3Q $0x02, AX, AX - MULQ 24(CX) - ADDQ AX, R14 - ADCQ DX, R13 - - // r4 += l2×l2 - MOVQ 16(CX), AX - MULQ 16(CX) - ADDQ AX, R14 - ADCQ DX, R13 - - // First reduction chain - MOVQ $0x0007ffffffffffff, AX - SHLQ $0x0d, SI, BX - SHLQ $0x0d, R8, DI - SHLQ $0x0d, R10, R9 - SHLQ $0x0d, R12, R11 - SHLQ $0x0d, R14, R13 - ANDQ AX, SI - IMUL3Q $0x13, R13, R13 - ADDQ R13, SI - ANDQ AX, R8 - ADDQ BX, R8 - ANDQ AX, R10 - ADDQ DI, R10 - ANDQ AX, R12 - ADDQ R9, R12 - ANDQ AX, R14 - ADDQ R11, R14 - - // Second reduction chain (carryPropagate) - MOVQ SI, BX - SHRQ $0x33, BX - MOVQ R8, DI - SHRQ $0x33, DI - MOVQ R10, R9 - SHRQ $0x33, R9 - MOVQ R12, R11 - SHRQ $0x33, R11 - MOVQ R14, R13 - SHRQ $0x33, R13 - ANDQ AX, SI - IMUL3Q $0x13, R13, R13 - ADDQ R13, SI - ANDQ AX, R8 - ADDQ BX, R8 - ANDQ AX, R10 - ADDQ DI, R10 - ANDQ AX, R12 - ADDQ R9, R12 - ANDQ AX, R14 - ADDQ R11, R14 - - // Store output - MOVQ out+0(FP), AX - MOVQ SI, (AX) - MOVQ R8, 8(AX) - MOVQ R10, 16(AX) - MOVQ R12, 24(AX) - MOVQ R14, 32(AX) - RET diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go deleted file mode 100644 index 9da280d1d..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !amd64 || !gc || purego - -package field - -func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } - -func feSquare(v, x *Element) { feSquareGeneric(v, x) } diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go deleted file mode 100644 index 075fe9b92..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build arm64 && gc && !purego - -package field - -//go:noescape -func carryPropagate(v *Element) - -func (v *Element) carryPropagate() *Element { - carryPropagate(v) - return v -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s deleted file mode 100644 index 3126a4341..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build arm64 && gc && !purego - -#include "textflag.h" - -// carryPropagate works exactly like carryPropagateGeneric and uses the -// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but -// avoids loading R0-R4 twice and uses LDP and STP. -// -// See https://golang.org/issues/43145 for the main compiler issue. -// -// func carryPropagate(v *Element) -TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8 - MOVD v+0(FP), R20 - - LDP 0(R20), (R0, R1) - LDP 16(R20), (R2, R3) - MOVD 32(R20), R4 - - AND $0x7ffffffffffff, R0, R10 - AND $0x7ffffffffffff, R1, R11 - AND $0x7ffffffffffff, R2, R12 - AND $0x7ffffffffffff, R3, R13 - AND $0x7ffffffffffff, R4, R14 - - ADD R0>>51, R11, R11 - ADD R1>>51, R12, R12 - ADD R2>>51, R13, R13 - ADD R3>>51, R14, R14 - // R4>>51 * 19 + R10 -> R10 - LSR $51, R4, R21 - MOVD $19, R22 - MADD R22, R10, R21, R10 - - STP (R10, R11), 0(R20) - STP (R12, R13), 16(R20) - MOVD R14, 32(R20) - - RET diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go deleted file mode 100644 index fc029ac12..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !arm64 || !gc || purego - -package field - -func (v *Element) carryPropagate() *Element { - return v.carryPropagateGeneric() -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go deleted file mode 100644 index 2671217da..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package field - -import "math/bits" - -// uint128 holds a 128-bit number as two 64-bit limbs, for use with the -// bits.Mul64 and bits.Add64 intrinsics. -type uint128 struct { - lo, hi uint64 -} - -// mul64 returns a * b. -func mul64(a, b uint64) uint128 { - hi, lo := bits.Mul64(a, b) - return uint128{lo, hi} -} - -// addMul64 returns v + a * b. -func addMul64(v uint128, a, b uint64) uint128 { - hi, lo := bits.Mul64(a, b) - lo, c := bits.Add64(lo, v.lo, 0) - hi, _ = bits.Add64(hi, v.hi, c) - return uint128{lo, hi} -} - -// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits. -func shiftRightBy51(a uint128) uint64 { - return (a.hi << (64 - 51)) | (a.lo >> 51) -} - -func feMulGeneric(v, a, b *Element) { - a0 := a.l0 - a1 := a.l1 - a2 := a.l2 - a3 := a.l3 - a4 := a.l4 - - b0 := b.l0 - b1 := b.l1 - b2 := b.l2 - b3 := b.l3 - b4 := b.l4 - - // Limb multiplication works like pen-and-paper columnar multiplication, but - // with 51-bit limbs instead of digits. - // - // a4 a3 a2 a1 a0 x - // b4 b3 b2 b1 b0 = - // ------------------------ - // a4b0 a3b0 a2b0 a1b0 a0b0 + - // a4b1 a3b1 a2b1 a1b1 a0b1 + - // a4b2 a3b2 a2b2 a1b2 a0b2 + - // a4b3 a3b3 a2b3 a1b3 a0b3 + - // a4b4 a3b4 a2b4 a1b4 a0b4 = - // ---------------------------------------------- - // r8 r7 r6 r5 r4 r3 r2 r1 r0 - // - // We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to - // reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5, - // r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc. - // - // Reduction can be carried out simultaneously to multiplication. For - // example, we do not compute r5: whenever the result of a multiplication - // belongs to r5, like a1b4, we multiply it by 19 and add the result to r0. - // - // a4b0 a3b0 a2b0 a1b0 a0b0 + - // a3b1 a2b1 a1b1 a0b1 19×a4b1 + - // a2b2 a1b2 a0b2 19×a4b2 19×a3b2 + - // a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 + - // a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 = - // -------------------------------------- - // r4 r3 r2 r1 r0 - // - // Finally we add up the columns into wide, overlapping limbs. - - a1_19 := a1 * 19 - a2_19 := a2 * 19 - a3_19 := a3 * 19 - a4_19 := a4 * 19 - - // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) - r0 := mul64(a0, b0) - r0 = addMul64(r0, a1_19, b4) - r0 = addMul64(r0, a2_19, b3) - r0 = addMul64(r0, a3_19, b2) - r0 = addMul64(r0, a4_19, b1) - - // r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2) - r1 := mul64(a0, b1) - r1 = addMul64(r1, a1, b0) - r1 = addMul64(r1, a2_19, b4) - r1 = addMul64(r1, a3_19, b3) - r1 = addMul64(r1, a4_19, b2) - - // r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3) - r2 := mul64(a0, b2) - r2 = addMul64(r2, a1, b1) - r2 = addMul64(r2, a2, b0) - r2 = addMul64(r2, a3_19, b4) - r2 = addMul64(r2, a4_19, b3) - - // r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4 - r3 := mul64(a0, b3) - r3 = addMul64(r3, a1, b2) - r3 = addMul64(r3, a2, b1) - r3 = addMul64(r3, a3, b0) - r3 = addMul64(r3, a4_19, b4) - - // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 - r4 := mul64(a0, b4) - r4 = addMul64(r4, a1, b3) - r4 = addMul64(r4, a2, b2) - r4 = addMul64(r4, a3, b1) - r4 = addMul64(r4, a4, b0) - - // After the multiplication, we need to reduce (carry) the five coefficients - // to obtain a result with limbs that are at most slightly larger than 2⁵¹, - // to respect the Element invariant. - // - // Overall, the reduction works the same as carryPropagate, except with - // wider inputs: we take the carry for each coefficient by shifting it right - // by 51, and add it to the limb above it. The top carry is multiplied by 19 - // according to the reduction identity and added to the lowest limb. - // - // The largest coefficient (r0) will be at most 111 bits, which guarantees - // that all carries are at most 111 - 51 = 60 bits, which fits in a uint64. - // - // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) - // r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²) - // r0 < (1 + 19 × 4) × 2⁵² × 2⁵² - // r0 < 2⁷ × 2⁵² × 2⁵² - // r0 < 2¹¹¹ - // - // Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most - // 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and - // allows us to easily apply the reduction identity. - // - // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 - // r4 < 5 × 2⁵² × 2⁵² - // r4 < 2¹⁰⁷ - // - - c0 := shiftRightBy51(r0) - c1 := shiftRightBy51(r1) - c2 := shiftRightBy51(r2) - c3 := shiftRightBy51(r3) - c4 := shiftRightBy51(r4) - - rr0 := r0.lo&maskLow51Bits + c4*19 - rr1 := r1.lo&maskLow51Bits + c0 - rr2 := r2.lo&maskLow51Bits + c1 - rr3 := r3.lo&maskLow51Bits + c2 - rr4 := r4.lo&maskLow51Bits + c3 - - // Now all coefficients fit into 64-bit registers but are still too large to - // be passed around as a Element. We therefore do one last carry chain, - // where the carries will be small enough to fit in the wiggle room above 2⁵¹. - *v = Element{rr0, rr1, rr2, rr3, rr4} - v.carryPropagate() -} - -func feSquareGeneric(v, a *Element) { - l0 := a.l0 - l1 := a.l1 - l2 := a.l2 - l3 := a.l3 - l4 := a.l4 - - // Squaring works precisely like multiplication above, but thanks to its - // symmetry we get to group a few terms together. - // - // l4 l3 l2 l1 l0 x - // l4 l3 l2 l1 l0 = - // ------------------------ - // l4l0 l3l0 l2l0 l1l0 l0l0 + - // l4l1 l3l1 l2l1 l1l1 l0l1 + - // l4l2 l3l2 l2l2 l1l2 l0l2 + - // l4l3 l3l3 l2l3 l1l3 l0l3 + - // l4l4 l3l4 l2l4 l1l4 l0l4 = - // ---------------------------------------------- - // r8 r7 r6 r5 r4 r3 r2 r1 r0 - // - // l4l0 l3l0 l2l0 l1l0 l0l0 + - // l3l1 l2l1 l1l1 l0l1 19×l4l1 + - // l2l2 l1l2 l0l2 19×l4l2 19×l3l2 + - // l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 + - // l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 = - // -------------------------------------- - // r4 r3 r2 r1 r0 - // - // With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with - // only three Mul64 and four Add64, instead of five and eight. - - l0_2 := l0 * 2 - l1_2 := l1 * 2 - - l1_38 := l1 * 38 - l2_38 := l2 * 38 - l3_38 := l3 * 38 - - l3_19 := l3 * 19 - l4_19 := l4 * 19 - - // r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3) - r0 := mul64(l0, l0) - r0 = addMul64(r0, l1_38, l4) - r0 = addMul64(r0, l2_38, l3) - - // r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3 - r1 := mul64(l0_2, l1) - r1 = addMul64(r1, l2_38, l4) - r1 = addMul64(r1, l3_19, l3) - - // r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4 - r2 := mul64(l0_2, l2) - r2 = addMul64(r2, l1, l1) - r2 = addMul64(r2, l3_38, l4) - - // r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4 - r3 := mul64(l0_2, l3) - r3 = addMul64(r3, l1_2, l2) - r3 = addMul64(r3, l4_19, l4) - - // r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2 - r4 := mul64(l0_2, l4) - r4 = addMul64(r4, l1_2, l3) - r4 = addMul64(r4, l2, l2) - - c0 := shiftRightBy51(r0) - c1 := shiftRightBy51(r1) - c2 := shiftRightBy51(r2) - c3 := shiftRightBy51(r3) - c4 := shiftRightBy51(r4) - - rr0 := r0.lo&maskLow51Bits + c4*19 - rr1 := r1.lo&maskLow51Bits + c0 - rr2 := r2.lo&maskLow51Bits + c1 - rr3 := r3.lo&maskLow51Bits + c2 - rr4 := r4.lo&maskLow51Bits + c3 - - *v = Element{rr0, rr1, rr2, rr3, rr4} - v.carryPropagate() -} - -// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction -// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. TODO inline -func (v *Element) carryPropagateGeneric() *Element { - c0 := v.l0 >> 51 - c1 := v.l1 >> 51 - c2 := v.l2 >> 51 - c3 := v.l3 >> 51 - c4 := v.l4 >> 51 - - v.l0 = v.l0&maskLow51Bits + c4*19 - v.l1 = v.l1&maskLow51Bits + c0 - v.l2 = v.l2&maskLow51Bits + c1 - v.l3 = v.l3&maskLow51Bits + c2 - v.l4 = v.l4&maskLow51Bits + c3 - - return v -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint deleted file mode 100644 index e3685f95c..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint +++ /dev/null @@ -1 +0,0 @@ -b0c49ae9f59d233526f8934262c5bbbe14d4358d diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh deleted file mode 100644 index 1ba22a8b4..000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/bash -set -euo pipefail - -cd "$(git rev-parse --show-toplevel)" - -STD_PATH=src/crypto/ed25519/internal/edwards25519/field -LOCAL_PATH=curve25519/internal/field -LAST_SYNC_REF=$(cat $LOCAL_PATH/sync.checkpoint) - -git fetch https://go.googlesource.com/go master - -if git diff --quiet $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH; then - echo "No changes." -else - NEW_REF=$(git rev-parse FETCH_HEAD | tee $LOCAL_PATH/sync.checkpoint) - echo "Applying changes from $LAST_SYNC_REF to $NEW_REF..." - git diff $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH | \ - git apply -3 --directory=$LOCAL_PATH -fi diff --git a/vendor/golang.org/x/crypto/hkdf/hkdf.go b/vendor/golang.org/x/crypto/hkdf/hkdf.go index f4ded5fee..3bee66294 100644 --- a/vendor/golang.org/x/crypto/hkdf/hkdf.go +++ b/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -8,7 +8,7 @@ // HKDF is a cryptographic key derivation function (KDF) with the goal of // expanding limited input keying material into one or more cryptographically // strong secret keys. -package hkdf // import "golang.org/x/crypto/hkdf" +package hkdf import ( "crypto/hmac" diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index 9486c5986..b93961010 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -71,6 +71,10 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { for auth := AuthMethod(new(noneAuth)); auth != nil; { ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions) if err != nil { + // On disconnect, return error immediately + if _, ok := err.(*disconnectMsg); ok { + return err + } // We return the error later if there is no other method left to // try. ok = authFailure diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go index edbe63340..f5d352fe3 100644 --- a/vendor/golang.org/x/crypto/ssh/doc.go +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -20,4 +20,4 @@ References: This package does not fall under the stability promise of the Go language itself, so its API may be changed when pressing needs arise. */ -package ssh // import "golang.org/x/crypto/ssh" +package ssh diff --git a/vendor/golang.org/x/sys/unix/mremap.go b/vendor/golang.org/x/sys/unix/mremap.go index fd45fe529..3a5e776f8 100644 --- a/vendor/golang.org/x/sys/unix/mremap.go +++ b/vendor/golang.org/x/sys/unix/mremap.go @@ -50,3 +50,8 @@ func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data [ func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) { return mapper.Mremap(oldData, newLength, flags) } + +func MremapPtr(oldAddr unsafe.Pointer, oldSize uintptr, newAddr unsafe.Pointer, newSize uintptr, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mremap(uintptr(oldAddr), oldSize, newSize, flags, uintptr(newAddr)) + return unsafe.Pointer(xaddr), err +} diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index 59542a897..4cc7b0059 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -542,6 +542,18 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { } } +//sys pthread_chdir_np(path string) (err error) + +func PthreadChdir(path string) (err error) { + return pthread_chdir_np(path) +} + +//sys pthread_fchdir_np(fd int) (err error) + +func PthreadFchdir(fd int) (err error) { + return pthread_fchdir_np(fd) +} + //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_unix.go b/vendor/golang.org/x/sys/unix/syscall_unix.go index 77081de8c..4e92e5aa4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -154,6 +154,15 @@ func Munmap(b []byte) (err error) { return mapper.Munmap(b) } +func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mmap(uintptr(addr), length, prot, flags, fd, offset) + return unsafe.Pointer(xaddr), err +} + +func MunmapPtr(addr unsafe.Pointer, length uintptr) (err error) { + return mapper.munmap(uintptr(addr), length) +} + func Read(fd int, p []byte) (n int, err error) { n, err = read(fd, p) if raceenabled { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index ccb02f240..07642c308 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -760,6 +760,39 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pthread_chdir_np(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_chdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pthread_fchdir_np(fd int) (err error) { + _, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_fchdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index 8b8bb2840..923e08cb7 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -228,6 +228,16 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_chdir_np(SB) +GLOBL ·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB) + +TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_fchdir_np(SB) +GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 1b40b997b..7d73dda64 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -760,6 +760,39 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pthread_chdir_np(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_chdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pthread_fchdir_np(fd int) (err error) { + _, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_fchdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index 08362c1ab..057700111 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -228,6 +228,16 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_chdir_np(SB) +GLOBL ·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB) + +TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_fchdir_np(SB) +GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/windows/security_windows.go b/vendor/golang.org/x/sys/windows/security_windows.go index 6f7d2ac70..97651b5bd 100644 --- a/vendor/golang.org/x/sys/windows/security_windows.go +++ b/vendor/golang.org/x/sys/windows/security_windows.go @@ -894,7 +894,7 @@ type ACL struct { aclRevision byte sbz1 byte aclSize uint16 - aceCount uint16 + AceCount uint16 sbz2 uint16 } @@ -1087,6 +1087,27 @@ type EXPLICIT_ACCESS struct { Trustee TRUSTEE } +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header +type ACE_HEADER struct { + AceType uint8 + AceFlags uint8 + AceSize uint16 +} + +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace +type ACCESS_ALLOWED_ACE struct { + Header ACE_HEADER + Mask ACCESS_MASK + SidStart uint32 +} + +const ( + // Constants for AceType + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header + ACCESS_ALLOWED_ACE_TYPE = 0 + ACCESS_DENIED_ACE_TYPE = 1 +) + // This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions. type TrusteeValue uintptr @@ -1158,6 +1179,7 @@ type OBJECTS_AND_NAME struct { //sys makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD //sys setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW +//sys GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) = advapi32.GetAce // Control returns the security descriptor control bits. func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) { diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 9f73df75b..eba761018 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -91,6 +91,7 @@ var ( procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") procEqualSid = modadvapi32.NewProc("EqualSid") procFreeSid = modadvapi32.NewProc("FreeSid") + procGetAce = modadvapi32.NewProc("GetAce") procGetLengthSid = modadvapi32.NewProc("GetLengthSid") procGetNamedSecurityInfoW = modadvapi32.NewProc("GetNamedSecurityInfoW") procGetSecurityDescriptorControl = modadvapi32.NewProc("GetSecurityDescriptorControl") @@ -1224,6 +1225,14 @@ func setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCE return } +func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) { + r0, _, _ := syscall.Syscall(procGetAce.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce))) + if r0 == 0 { + ret = GetLastError() + } + return +} + func SetKernelObjectSecurity(handle Handle, securityInformation SECURITY_INFORMATION, securityDescriptor *SECURITY_DESCRIPTOR) (err error) { r1, _, e1 := syscall.Syscall(procSetKernelObjectSecurity.Addr(), 3, uintptr(handle), uintptr(securityInformation), uintptr(unsafe.Pointer(securityDescriptor))) if r1 == 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index a446a16fe..eabaa5d8b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -356,10 +356,6 @@ github.com/googleapis/gax-go/v2/internal ## explicit; go 1.14 github.com/gophercloud/gophercloud github.com/gophercloud/gophercloud/openstack -github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes -github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones -github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach -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 @@ -370,6 +366,21 @@ github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects github.com/gophercloud/gophercloud/openstack/utils github.com/gophercloud/gophercloud/pagination +# github.com/gophercloud/gophercloud/v2 v2.2.0 +## explicit; go 1.22 +github.com/gophercloud/gophercloud/v2 +github.com/gophercloud/gophercloud/v2/openstack +github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes +github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones +github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers +github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach +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/utils +github.com/gophercloud/gophercloud/v2/pagination # github.com/hashicorp/go-cleanhttp v0.5.2 ## explicit; go 1.13 github.com/hashicorp/go-cleanhttp @@ -516,15 +527,14 @@ go.uber.org/zap/internal/color go.uber.org/zap/internal/exit go.uber.org/zap/zapcore go.uber.org/zap/zapgrpc -# golang.org/x/crypto v0.24.0 -## explicit; go 1.18 +# golang.org/x/crypto v0.25.0 +## explicit; go 1.20 golang.org/x/crypto/blowfish golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20poly1305 golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/curve25519 -golang.org/x/crypto/curve25519/internal/field golang.org/x/crypto/hkdf golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 @@ -556,7 +566,7 @@ golang.org/x/oauth2/google/internal/stsexchange golang.org/x/oauth2/internal golang.org/x/oauth2/jws golang.org/x/oauth2/jwt -# golang.org/x/sys v0.21.0 +# golang.org/x/sys v0.22.0 ## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/unix